Async Handler


Desde a versão 1.x do ASP.NET podemos facilmente criar um handler para permitir a execução de algum código. O idéia por detrás deste handler, é justamente servir como alvo para alguma requisição. Um exemplo muito típico é a extração de arquivos do banco de dados, arquivos do disco, geração automática de imagens, etc..

Havia alguns detalhes necessários na versão 1.x que, se não realizados, não funcionava adequadamente. A partir da versão 2.0, a Microsoft incluiu um novo tipo de arquivo, com extensão ASHX, chamado de Generic Handler. Esse arquivo traz uma classe que, por padrão, implementa a interface IHttpHandler, cabendo aos desenvolvedores implementar o método ProcessRequest para tratar o pedido.

O problema é que o processamento deste handler estará ocupando uma thread do ThreadPool que, por sua vez, serve as requisições para as páginas da aplicação. Caso o código a ser realizado dentro deste handler for um processo custoso, podemos comprometer as outras requisições que nada tem a ver com ele (este artigo explica mais detalhes sobre este comportamento).

Para permitir que o handler também faça o uso do processamento assíncrono, podemos fazer com que o mesmo implemente a interface IHttpAsyncHandler e, ao invés de implementar o método ProcessRequest, devemos implementar os métodos BeginProcessRequest e EndProcessRequest. O exemplo abaixo utiliza os métodos (BeginRead e EndRead) para leitura assíncrona de um arquivo do disco:

public class ImageAsyncHandler : IHttpAsyncHandler
{
    private struct InnerState : IDisposable
    {
        public HttpContext HttpContext;
        public FileStream Stream;

        public void Dispose()
        {
            if (this.Stream != null)
                this.Stream.Dispose();
        }
    }

    public bool IsReusable
    {
        get
        {
            return false;
        }
    }

    public IAsyncResult BeginProcessRequest(HttpContext context,
        AsyncCallback cb, object extraData)
    {
        string fileName =
            context.Server.MapPath(context.Request.QueryString[“FileName”]);

        FileStream fs = new FileStream(fileName, FileMode.Open);
        int length = (int)fs.Length;

        return fs.BeginRead(new byte[length], 0, length, cb,
            new InnerState()
            {
                HttpContext = context
                , Stream = fs
            });
    }

    public void ProcessRequest(HttpContext context) { }

    public void EndProcessRequest(IAsyncResult result)
    {
        using (InnerState state = (InnerState)result.AsyncState)
        {
            int length = state.Stream.EndRead(result);
            state.Stream.Position = 0;
           
            byte[] temp = new byte[length];
            state.Stream.Read(temp, 0, length);

            state.HttpContext.Response.OutputStream.Write(temp, 0, length);
        }
    }
}

Esse processo não mudará nada o resultado final do handler, mas trabalhará de uma forma muito mais otimizada, garantido que threads criadas para servir a aplicação não fiquem ocupadas executando um processamento mais custoso. Caso a imagem ou os dados venham de um banco de dados, podemos combinar essa técnica com os métodos assíncronos do ADO.NET 2.0.

Anúncios

Deixe uma resposta

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s