Synchronous x Asynchronous Services – Parte 1
Olá pessoal,

Neste post gostaria de trazer um assunto bastante recorrente em discussões relacionadas à integração, principalmente quando o meio escolhido para a tal é web service: quando que serviços devem ser síncronos ou assíncronos e quais as vantagens e desvantagens de ambas as abordagens. Além disso, discutiremos também, para o caso de serviços assíncronos, quais os padrões que podemos considerar para retornarmos um determinado resultado para o chamador do serviço, uma vez que o processamento tenha sido concluído. Padrões como Request/Response, Asynchronous Response Handler, Request/Acknowledge/Poll e Request/Acknowledge/Callback serão explicados, bem como o impacto da sua adoção na forma como os serviço são estruturados, o que incorre em esforço de desenvolvimento, timing de atualização e recuperação de informações, além de disponibilidade e escalabilidade do serviço provido. Neste primeira parte (parte 1) focarei em em padrões de serviços síncronos (padrão Request/Response e Asynchronous Response Handler) e no seguinte fecharei a comparação com os padrões de implementação de serviços assíncronos (Request/Acknowledge/Poll e Request/Acknowledge/Callback).
Todas estas questões e diversas outras, como padrões de APIs de serviços, padrões de interação client-service (que discutiremos em parte neste artigo), padrões para gerenciamento de requisição e resposta, padrões para implementação de serviços, padrões de infraestrutura de serviços e padrões de evolução de serviços, todos bastante relevantes e úteis em qualquer discussão que envolva web service como meio de integração, podem ser encontradas no livro Service Design Patterns de Robert Daigneau, que conta com prefácio de nada mais nada menos que Martin Fowler, além de Ian Robinson.

Padrão Request/Response
Um serviço que implementa o padrão Request/Response processa requisições assim que elas são recebidas, retornando resultados em uma mesma conexão de um cliente, de forma síncrona. Uma vez que o cliente submeta uma requisição, usualmente ele não continua com seu processamento até que uma resposta seja retornada. Na utilização deste padrão, frequentemente assume-se que o processamento será concluído em apenas alguns segundos. Dentre os padrões que serão abordados, o padrão Request/Response é mais simples de se implementar, depurar e testar, uma vez que funciona como uma chamada remota de uma operação, com parâmetros de entrada e resposta síncrona. Veja a exemplificação do padrão Request/Response no diagrama de sequencia ao lado.
O padrão Request/Response é indicado para casos em que uma resposta imediata seja necessária, como por exemplo, uma página web que proveja uma funcionalidade para consulta de abertura de vagas, cuja consulta das vagas disponíveis seja provida por um serviço.
Acoplamento temporal (temporal coupling)
A adoção do padrão Request/Response incorre em algumas implicações. Acoplamento temporal é uma delas. O acoplamento temporal remete à dependência do tempo de resposta que um serviço que implementa o padrão Request/Response implica para seu consumidor. No padrão Request/Response é assumido que o serviço sempre poderá processar uma requisição quando esta for recebida e que uma resposta será enviada assim que o processamento for encerrado. A implicação neste caso é que todos os sistemas dos quais o serviço depende (banco de dados, servidores de arquivos, etc.) sempre devem estar operacionais e sempre devem estar aptos a processar requisições imediatamente. Se tais sistemas estiverem indisponíveis, seja por uma falha ou por uma parada programada para manutenção, então as requisições dos clientes poderão ser rejeitadas.
Problemas relacionados ao tempo de resposta do serviço podem ainda acontecer mesmo quando todos os recursos dos quais o serviço dependem estão disponíveis. Uma vez que este padrão requer que requisições sejam processadas assim que recebidas, um grande volume de requisições pode comprometer a capacidade do sistema. Ao adotar tal padrão é preciso compreender a carga típica que o serviço deterá ter e projetar a capacidade de servidores, bancos de dados, rede, etc. de forma a endereçar tais cargas. Uma arquitetura tal deve ser projetada para que seja possível escalar horizontal ou verticalmente tais recursos, uma vez que as demandas de requisição aumentem ao longo do tempo. A adoção dos padrões assíncronos Request/Acknowledge/Poll e Request/Acknowledge/Callback, que serão vistos no próximo post, ajuda a mitigar esta necessidade, uma vez que transfere do cliente do serviço para o provedor do serviço o controle das requisições que serão processadas em paralelo.
O acoplamento temporal também traz implicações relacionadas ao tempo em que o consumidor do serviço aguardará por uma resposta antes que um timeout seja lançado. Nestes casos, uma vez que o processamento já tenha sido iniciado pelo serviço, a resposta não seria recebida pelo cliente, implicando em perda de sincronismo ou inconsistência de dados. As razões para que um serviço demore mais para responder do que o tempo planejado são inúmeras. Atividades que requeiram maior I/O, altos níveis de trafego de rede são exemplos comuns. Neste ponto, mais uma vez os padrões de serviços assíncronos são mais vantajosos.
Client-side Blocking / Asynchronous Response Handler
Por padrão, clientes de serviços que implementam o padrão Request/Response aguardam pela resposta do serviço invocado sem realizar outras operações no meio tempo. Esta característica é conhecida como “Client-side Blocking”, uma vez que o cliente fica bloqueado até que uma resposta do serviço chamado seja retornada. Para casos em que se deseje aproveitar o tempo de espera de um serviço processando outras atividades, pode-se utilizar o padrão denominado “Asynchronous Response Handler” em conjunto com o padrão Request/Response, o qual permite que o cliente efetue requisições para um serviço Request/Response, delegando o recebimento da resposta para um thread apartada da thread principal, de forma que outras atividades sejam executadas na thread principal logo após a requisição do serviço. Veja no diagrama de sequencia mais abaixo a ilustração desta implementação.
É importante destacar que este padrão também não garante que respostas não sejam perdidas, como já descrito anteriormente.

Por todos os motivos apresentados, podemos concluir que o padrão Request/Response deve ser usado apenas quando for possível garantir que o tempo médio de resposta do serviço seja relativamente baixo, e que o cliente será projetado para tolerar perda de respostas. No próximo post veremos como os padrões assíncronos ajudam a mitigar os riscos relacionados ao acoplamento temporal, à perda de sincronismo e inconsistência de dados, além de permitir que o cliente não fique bloqueado enquanto aguarda o resultado do processamento.
Neste post ficamos por aqui. Até o próximo!