Подтвердить что ты не робот

Web API concurrency и масштабируемость

Перед нами стоит задача конвертировать службу REST на основе пользовательского кода в Web API. Услуга имеет значительное количество запросов и работает с данными, которые могут потребоваться некоторое время для загрузки, но после загрузки их можно кэшировать и использовать для обслуживания всех входящих запросов. В предыдущей версии службы был бы один поток, ответственный за загрузку данных и попадание в кеш. Чтобы предотвратить запуск IIS из рабочих потоков, клиенты получат ответ "вернуться позже", пока кеш не будет готов.

Мое понимание веб-API заключается в том, что он имеет асинхронное поведение, основанное на работе над задачами, и в результате количество запросов напрямую не связано с количеством удерживаемых физических потоков.

В новой реализации службы я планирую, чтобы запросы дождались, пока кеш не будет готов, а затем сделайте правильный ответ. Я сделал очень грубый эскиз кода, чтобы проиллюстрировать:

public class ContactsController : ApiController
{
    private readonly IContactRepository _contactRepository;

    public ContactsController(IContactRepository contactRepository)
    {
        if (contactRepository == null) 
            throw new ArgumentNullException("contactRepository");
        _contactRepository = contactRepository;
    }

    public IEnumerable<Contact> Get()
    {
        return _contactRepository.Get();
    }
}

public class ContactRepository : IContactRepository
{
    private readonly Lazy<IEnumerable<Contact>> _contactsLazy;

    public ContactRepository()
    {
        _contactsLazy = new Lazy<IEnumerable<Contact>>(LoadFromDatabase, 
            LazyThreadSafetyMode.ExecutionAndPublication);
    }

    public IEnumerable<Contact> Get()
    {
        return _contactsLazy.Value;
    }

    private IEnumerable<Contact> LoadFromDatabase()
    {
        // This method could be take a long time to execute.
        throw new NotImplementedException();
    }
}

Пожалуйста, не придавайте слишком большого значения дизайну кода - он создан только для иллюстрации проблемы и не так, как мы это сделали в реальном решении. IContactRepository регистрируется в контейнере IoC как однотонный и вводится в контроллер. Lazy с LazyThreadSafetyMode.ExecutionAndPublication гарантирует, что только первый поток/запрос запускает код инициализации, следующие rquests блокируются до завершения инициализации.

Может ли веб-API обрабатывать 1000 запросов, ожидающих завершения инициализации, в то время как другие запросы, не попавшие в этот Lazy, являются службами и без IIS заканчиваются рабочие потоки?

4b9b3361

Ответ 1

Возврат Task<T> из действия позволит запустить код в фоновом потоке (ThreadPool) и освободить поток IIS. Поэтому в этом случае я бы изменил

public IEnumerable<Contact> Get()

к

public Task<IEnumerable<Contact>> Get()

Не забудьте вернуть запущенную задачу, иначе поток будет просто сидеть и ничего не делать.

Ленивая реализация пока может быть полезна, имеет мало общего с поведением веб-API. Поэтому я не буду комментировать это. С ленивым типом возврата на основе задачи можно использовать для длительных операций.

У меня есть два сообщения в блоге, которые, вероятно, вам полезны: здесь и .