Параллелизм и масштабируемость Web API

перед нами стоит задача преобразовать службу 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 как одноэлементный и вводится в контроллер. Ленивый с LazyThreadSafetyMode.ExecutionAndPublication гарантирует, что только первый поток/запрос выполняет код инициализации, следующие запросы блокируются до инициализации завершает.

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

1 ответов


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

public IEnumerable<Contact> Get()

to

public Task<IEnumerable<Contact>> Get()

не забудьте вернуть started задача в противном случае поток будет просто сидеть и ничего не делать.

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

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