Web Api 2 OWIN Selfhost обрабатывает несколько одновременных запросов

У меня есть автономный веб-API, использующий OWIN, и я, кажется, борюсь с одновременными подключениями. Когда я запускаю стресс-тест с 50 одновременными событиями получения, время отклика увеличивается НАМНОГО, даже если операция довольно проста, верните 200 OK с идентификатором, введенным в uri.

Как мне сделать так, чтобы мои контроллеры OWIN и API для веб-API выполнялись одновременно для каждого запроса??

Вот пример моего проекта:

Стартовый класс

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;
        }
    }
}

Контроллер

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();
            }
        });
    }
}

Мой консольный запуск веб-API:

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

person grmihel    schedule 16.05.2017    source источник
comment
Как вы проводите стресс-тест? Можете ли вы опубликовать этот код?   -  person MichaelDotKnox    schedule 16.05.2017
comment
Кроме того, вместо того, чтобы делать Factory.StartNew, вы можете вернуть Task.FromResult(Ok(Id)), который с меньшей вероятностью будет заблокирован. Не уверен, что это вызовет ваши проблемы.   -  person MichaelDotKnox    schedule 16.05.2017
comment
Использование инструмента Paessler Webserver Stress для проведения стресс-теста. Вместо этого подумайте о создании простого клиента и посмотрите, как все пойдет. Спасибо за подсказку с Task.FromResult.   -  person grmihel    schedule 16.05.2017
comment
Я думаю, вам было бы лучше создать свой собственный тестовый клиент. Я не думаю, что инструмент Паесслера правильно протестирует WebApi. Похоже, он больше подходит для традиционных веб-страниц HTML.   -  person MichaelDotKnox    schedule 16.05.2017
comment
Вы используете поток только для возврата 200. Это снижение производительности. Зачем ты это делаешь? Вам не нужно создавать методы на основе задач в вашем контроллере. Таким образом, вы не делаете ничего, что выиграет от многопоточности.   -  person Peter Bons    schedule 16.05.2017
comment
@PeterBons Пример упрощен, чтобы исключить другую логику. Производственный выпускает репозиторий EF, следовательно, операции Task для имитации поведения. Я все еще новичок в Web Api, но после того, что я получил от asp.net/web-api создание асинхронной задачи метода ApiController должно сделать обработку асинхронной, верно?   -  person grmihel    schedule 17.05.2017
comment
@MichaelDotKnox Я создал небольшой тестовый инструмент, который в основном запускает экземпляр HttpClient в задаче. Затем я понял, что трассировка WebApi, которую я запускал в режиме отладки, привела к снижению производительности, вероятно, потому, что трассировщик не работает асинхронно. И теперь я даже могу нарушать свои правила ограничения скорости, так что это здорово. Это все еще оставляет меня с моим первоначальным вопросом: есть ли лучший способ выполнения параллельных операций в веб-API?   -  person grmihel    schedule 19.05.2017


Ответы (1)


Все методы вашего контроллера API будут работать одновременно, что, очевидно, ограничено возможностями сервера, на котором размещен API. Это функция веб-сервера, будь то IIS или собственный сервер. Однако есть вещи, которые вы можете сделать, чтобы помочь в этом.

Если вы все время будете поддерживать асинхронность, вы увидите лучшую производительность. Например, если ваш метод контроллера выглядит так:

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

    return Ok(testObject);
}

и у вас есть метод репозитория, который выглядит так:

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);
    }
}

Это позволяет компьютеру использовать рабочие потоки для ввода-вывода базы данных, высвобождая вычислительную мощность для обработки других задач, таких как прием других запросов к конечной точке API. Кто-то вроде @StephenCleary, вероятно, сможет разъяснить то, что я говорю, и/или исправить. блог и книгу @StephenCleary определенно стоит прочитать, чтобы понять асинхронное программирование.

Надеюсь, это поможет.

person MichaelDotKnox    schedule 19.05.2017
comment
Спасибо, это именно то, что я искал. Кажется, теперь все работает максимально гладко. - person grmihel; 23.05.2017