Search code examples
c#asp.net-web-apiasp.net-web-api2owinowin-middleware

Web Api 2 OWIN Selfhost handle multiple concurrent requests


I have a selfhosted web api using OWIN, and I seems to struggle on concurrent connections. When I run a stress test with 50 concurrent get events, the response time is increasing A LOT, even tho the operation is quite simple, return 200 OK with the id typed in the uri.

How do I my web api OWIN and api controllers to run simultanious per requests??

Here's a sample of my project:

Startup class

public class Startup
{
    public void Configuration(IAppBuilder appBuilder)
    {
        try
        {
            appBuilder.UseWelcomePage("/welcome.html");
            appBuilder.UseErrorPage(new ErrorPageOptions
            {
                ShowExceptionDetails =
    #if DEBUG
                true
    #else
                false
    #endif
            });

            Debug.WriteLine($"Setting Web Api Configuration up...");
            var apiConfig = ConfigureWebApi();
            appBuilder.UseWebApi(new HttpServer(apiConfig));
            appBuilder.UseCors(CorsOptions.AllowAll);

            Debug.WriteLine($"Completed Web Api configuration.");
        }
        catch (Exception ex)
        {
            Debug.WriteLine($"Failed to complete Web Api setup.", ex);
            throw;
        }
    }
}

Controller

public class TestController : ApiController
{
    public async Task<IHttpActionResult> Get(int id)
    {
        return Task<IHttpActionResult>.Factory.StartNew(() => {
            try
            {
                return Ok(id);
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.Message, nameof(Get));
                return InternalServerError();
            }
        });
    }
}

My console startup of web api:

public static void Open()
{
    _webApp = WebApp.Start<Startup>(HostAddress);
    Console.WriteLine($"Host open -> {HostAddress}");
}

Solution

  • All of your api controller methods will run simultaneously, obviously limited by the capabilities of the server hosting the api. That is a function of the web server, whether it is IIS or self hosted. There are, however, things that you can do to help that along.

    If you keep everything asynchronous all the way through, you will see the best performance. For example, if you controller method looks like this:

    public async Task<IHttpActionResult> Get(int id)
    {
        var testObject = await _repository.GetAsync(id);
    
        return Ok(testObject);
    }
    

    and you have a repository method that looks like this:

    public async Task<TestObject> GetAsync(int id)
    {
        using(var connection = new SqlConnection(_connectionString)
        {
            // using Dapper
            return await connection.QuerySingleAsync<TestObject>("sp_GetTestObject", new {Id = id}, commandType = CommandType.StoredProcedure);
        }
    }
    

    Doing this allows the computer to use worker threads for the database I/O, freeing processing power for handling other tasks, such as accepting other requests to the API endpoint. Someone like @StephenCleary can probably expound on what I say and/or correct it. @StephenCleary's blog and book are definitely worth a read to understand asynchronous programming.

    Hope that helps.