Trabalhando com Serviços Assíncronos

Geralmente desenvolvemos serviços síncronos, mesmo quando uma grande quantidade de etapas precisa ser executada, em detrimento de serviços assíncronos.

Imagine um sistema que, em dado momento, precisa enviar emails. Podem ser aqueles emails para convidar outras pessoas para usar a plataforma, algo que vc faz uns 50 ou 100 de uma vez. O desenvolvimento natural é ler essa lista de emails e, iterativamente, enviar um convite para cada pessoa (com algum codigo que identifique esse convite para evitar fraudes, por exemplo). Coisa que, dependendo do numero de emails e como cada passo é processado pode demorar um pouco.

Precisa ser síncrono? Vc precisa enviar esses convites naquele momento? Faz diferença se demorar uns minutinhos? Como esta solução sincrona afeta a performance do resto da minha aplicação (coisa que posso avaliar num teste de carga)? São perguntas que podemos nos fazer e dão algumas pistas, abrindo alas para a verdadeira pergunta: como a minha aplicação escala?

Se esta etapa pode demorar um tempinho e ela afeta (ou pode afetar) negativamente a experiência do usuario ao demorar para enviar 50 ou 100 convites, pode ser o momento de separar a minha aplicação do serviço de ‘convites por e-mail’, que ira trabalhar assincronamente sem concorrer tanto com CPU (ex: usando nice ou rodando em outro servidor). A minha aplicação apenas iria dizer “essas são as informações: nome do usuario e lista de emails para convites” que o sistema sabe se virar.

E como essa integração é feita? Existem varias formas. Eu posso fazer através do banco de dados, inserindo numa tabela que o meu sistema lê, porém integração pelo banco nunca é algo simples e frequentemente causa problemas (principalmente se a documentação se perde e altera alguma coisa no banco, prejudicando outras aplicações que ninguem sonha que trabalhem assim). Uma boa forma seria passar uma mensagem com todos os dados para o servidor de alguma forma inequivoca, como um POST HTTP (que retorna “ok, recebido, logo vou processar isso”) ou, simplesmente, escrevendo num arquivo em um determinado diretorio. O serviço trata de ler essa requisição e colocar em uma fila ou outra estrutura de dados para atender no prazo que for possivel.

No caso de uma lista de emails escrever em um arquivo parece grosseria mas se eu estivesse trabalhando com fotos, em um serviço de geração de thumbnails, por exemplo, parece ser uma saida bem simples. Nesse caso em particular eu posso criar um “vigia de diretorios”, alguem que vigia uma pasta de tempos em tempos e, quando surge algo novo, executa alguma ação propriamente dita. A forma como eu trabalho pode ser bem simples, como: acordar, listar fotos, executar ação, apagar fotos. Se eu quiser trabalhar com uma maquina de estados eu posso criar pastas como: chegada, processando, fim. O serviço trata de olhar a pasta de chegada e, quando precisa, move as fotos ou outras entidades para o diretorio processando e, então, executa alguma ação.

Nesse ponto algumas coisas precisam ser acertadas: se eu vou trabalhar com estados das minhas fotos, convites, ou qualquer outra entidade, posso modelar este serviço de forma que, se a minha entidade esta na pasta X, então ela tem um determinado estado. Isso é interessante pois caso o meu serviço seja interrompido basta mover tudo o que estava sendo processado para a pasta de chegada! Nesse ponto temos que ter bem claro o que acontece em cada transição de estado, os fluxos possiveis e estabelecer os corretos tratamento de erros.

Se tudo der certo, eu posso rumar para uma aplicação que, para escalar, bastaria subir um segundo serviço em outra maquina (ou aproveitando outra CPU). É claro que existem muitas outras formas de chegar a este resultado e muitas tecnologias disponiveis, porém soluções simples e robustas são sempre bem vindas: percebam que existem muitos pontos de falha que temos que tratar então com a suites de testes apropriada e uma documentação consisa podemos ter um bom serviço assincrono que só vem a agregar valor.

Rating 4.00 out of 5
[?]

9 thoughts on “Trabalhando com Serviços Assíncronos

  1. Olá Peczenyj!
    Realmente serviços assincronos são muito pouco usados. Um exemplo legal de serv. assincrono, no caso de JEE, é uso de JMS. Já usei algumas vezes, e realmente é muito simples de usar e a maioria dos appication servers disponibilizam um “monitor” de filas.
    Eu acho que um tema interessante e não muito discutido é: “O que fazer quando o meu processamento assincrono deu erro?”, você sabe se existe algo catalogado sobre? Eu tenho a impressão que a maioria das soluções neste caso é sempre POG. O que vc costuma fazer neste caso?

    valeu e sucesso!

  2. No vaso de Java EE e Java SE, chega a ser impressionante a falta de suporte para serviços assíncronos. A única alternativa é o MDB, que não é muito lightweight.

    Felizmente, a versão do Java EE 6 irá trazer suporte a continuations no Servlet e Session Beans assíncronos (desde que retorne void e que explicitamente seja informado a opção de assíncrono). E no Java SE 7, haverá suporte a operações de arquivo de forma assíncrona também. Parecem coisas que já deveriam estar presentes na plataforma há faz tempo!

    @Roger Leite
    Quanto a sua pergunta, desculpe a obviedade da resposta; mas quando o processamento assíncrono deu erro, dispare uma chamada assíncrona a quem estiver interessado no evento de erro (tipo o padrão Observer). O interessados que se virem com a informação recebida.

  3. A velocidade (se demora ou nao) não é necessariamente o fator que decide se o serviço é síncrono ou não, e sim se há necessidade de ter uma resposta imediata.

    Se voce precisa ter uma resposta imediata se o que você quer fazer deu certo ou nao, voce pode “travar” sua aplicação enviando milhoes de e-mails ou processando um diretorio inteiro e ver se cada uma dessas requisições falhou. Enquanto sua aplicação tá fazendo essa porcaria, quem iniciou o request fica preso esperando a resposta.

    Obviamente a melhor solução nesse caso é trabalhar assincronamente, pois está claro que você (sistema) nao precisa de resposta imediata, muito menos o usuário.

    Abraços,
    Bruno Carvalho

  4. @rogerleite Serviços assincronos em Java eu tenho pouca experiência: em Perl eu utilizo um serviço que toma conta dos outros, um “cão de guarda” que levanda serviços que morreram, por exemplo. Eu poderia adicionar outras coisas para serem monitoradas como tempo de resposta ou algum outro parâmetro e tentar detectar erros sem muita intervenção.

    @brunocarvalho eu usei velocidade mas num sentido mais vulgar. A velocidade com que uma determinada tarefa sera executada num serviço assincrono é dificil de mensurar pois depende de varios fatores como a taxa de serviços de entrada, gargalos, latencias, etc.

  5. @Leonardo Veríssimo a solução que você passou é boa, mas acho que não chega a ser tão óbvio assim, pois depende muito de como a(s) aplicação(ões) envolvidas estão desenvolvidas. O que eu ja vi muito é criarem um campo tipo “result code”, que jogavam no banco com os dados processados.

  6. @Bruno Carvalho

    Mesmo que haja necessidade de resposta imediata pode-se fazer de forma assincrona, retornando ao usuário uma informação de que os e-mails estão sendo enviados. E assim que todos forem informa-se que a tarefa foi concluída.

  7. Fala Pac. Eu já tive um problema semelhante a esse, desenvolvi uma ferramenta que precisava enviar emails para algumas dezenas ou centenas de milhares de destinatários, mais ou menos uma vez por semana.

    A solução que eu criei foi uma interface web pra agendar o envio (upload da lista de destinatários, definição do dia e horário de envio e prioridade). Esse agendamento era registrado em banco de dados. Coloquei um processo na crontab rodando de hora em hora, verificando se havia algum agendamento para agora (ou para os próximos minutos) e se o envio anterior já tinha terminado. Neste caso, era executado o envio de maior prioridade.

Deixe um Comentário

O seu endereço de email não será publicado Campos obrigatórios são marcados *

*

Você pode usar estas tags e atributos de HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="" highlight="">