12

我们面临着将基于自定义代码的 REST 服务转换为 Web API 的任务。该服务有大量请求并对可能需要一些时间才能加载的数据进行操作,但一旦加载,它就可以被缓存并用于为所有传入请求提供服务。该服务的先前版本将有一个线程负责加载数据并将其放入缓存中。为了防止 IIS 用尽工作线程,客户端将获得“稍后再回来”响应,直到缓存准备好。

我对 Web 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 确保只有第一个线程/请求正在运行初始化代码,随后的 rquest 被阻塞,直到初始化完成。

Web API 是否能够处理等待初始化完成的 1000 个请求,而其他未命中此 Lazy 的请求正在被服务并且 IIS 不会耗尽工作线程?

4

1 回答 1

10

从操作返回Task<T>将允许代码在后台线程 (ThreadPool) 上运行并释放 IIS 线程。所以在这种情况下,我会改变

public IEnumerable<Contact> Get()

public Task<IEnumerable<Contact>> Get()

记住要返回一个已启动的任务,否则线程只会坐在那里什么都不做。

延迟实现虽然很有用,但与 Web API 的行为几乎没有关系。所以我不会对此发表评论。无论有没有惰性,基于任务的返回类型都是长时间运行的操作的方式。

我有两篇关于这方面的博文,可能对你有用:这里这里

于 2012-04-16T12:46:42.647 回答