2012-04-16 10 views
12

Nos enfrentamos a la tarea de convertir un servicio REST basado en código personalizado a API web. El servicio tiene una cantidad sustancial de solicitudes y opera con datos que podrían demorar un tiempo en cargarse, pero una vez cargados, pueden almacenarse en caché y utilizarse para atender todas las solicitudes entrantes. La versión anterior del servicio tendría un hilo responsable de cargar los datos y meterlos en el caché. Para evitar que el IIS se quede sin subprocesos de trabajo, los clientes obtendrían una respuesta de "volver más tarde" hasta que el caché estuviera listo.Concurrencia y escalabilidad de API web

Mi comprensión de la API web es que tiene un comportamiento asíncrono incorporado al operar en tareas y, como resultado, el número de solicitudes no se relaciona directamente con la cantidad de subprocesos físicos que se mantienen.

En la nueva implementación del servicio, estoy planeando dejar que las solicitudes esperen hasta que la memoria caché esté lista y luego hacer una respuesta válida. He hecho un boceto muy aproximada del código para ilustrar:

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

favor, no ponga demasiado valor en el diseño del código - que sólo se construye para ilustrar el problema y no es así como lo hicimos en la solución real. IContactRepository se registra en el contenedor IoC como singleton y se inyecta en el controlador. The Lazy con LazyThreadSafetyMode.ExecutionAndPublication asegura que solo el primer subproceso/solicitud ejecute el código de inicialización, las siguientes rutas se bloquean hasta que finalice la inicialización.

¿Sería capaz la API web de manejar 1000 solicitudes en espera de que se complete la inicialización, mientras que otras solicitudes que no afectan a este Lazy se están ejecutando y sin que IIS se quede sin hilos de trabajo?

Respuesta

10

Al devolver Task<T> de la acción, permitirá que el código se ejecute en el subproceso en segundo plano (ThreadPool) y libere el subproceso IIS. Así que en este caso, me gustaría cambiar

public IEnumerable<Contact> Get() 

a

public Task<IEnumerable<Contact>> Get() 

Recuerde que debe devolver un comenzó tarea de hacerlo el hilo será simplemente sentarse y no hacer nada.

La implementación diferida aunque puede ser útil, tiene poco que ver con el comportamiento de la API web. Entonces no voy a comentar sobre eso. Con o sin vago, el tipo de devolución basado en tareas es el camino a seguir para las operaciones de larga ejecución.

Tengo dos publicaciones en este blog que probablemente le resulten útiles: here y here.