Esta é o último post de uma série em que vimos até então um apanhado geral sobre o e-book gratuito .NET Technology Guide for Business Applications, escrito por Cesar de La Torre e David Carmona. No post anterior terminamos de abordar toda a camada Application services. Neste, passaremos pelas camadas Infrastructure services e Deployment environment, ilustradas no diagrama a seguir. Vamos lá!
No post anterior fomos até o quadro Collaboration and Portals da camada Application Services, representada no diagrama ao lado. Neste daremos continuidade a partir de Custom application platform, indo até Business products and platforms, finalizando assim completamente a camada Application Services.
Neste post e nos seguintes veremos um apanhado geral sobre o e-book gratuito .NET Technology Guide for Business Applications , escrito por Cesar de La Torre e David Carmona. Como seu próprio nome já anuncia, nele é apresentado um guia de tecnologias .Net para a criação de aplicações de negócios. O seu conteúdo discorre basicamente sobre o diagrama abaixo, onde a pilha mais atual de tecnologias para desenvolvimento e infraestrutura Microsoft é apresentada.
No post anterior vimos alguns conceitos de Event-Driven Architecture e como seria sua arquitetura de referência. Neste veremos as possíveis realizações de tal arquitetura de referência e também iremos conferir alguns princípios que devem ser levados em consideração ao se fazer o design deste tipo de arquitetura.
Vamos começar então pelas possíveis utilizações de uma Event-Driven Architecture. No livro Event Processing: Designing IT Systems for Agile Companies são relacionadas três principais cenários: Disseminação de Informação, Conhecimento de Situação e Integração de Aplicações. Cada possível utilização demanda uma forma distinta de implementação da arquitetura de referência vista. Vejamos cada uma delas a seguir.
Neste post veremos um conteúdo bastante interessante. Se você busca um estilo arquitetural que proporcione benefícios para sua organização como execução de processos de negócio em menor tempo, agilidade em resposta às mudanças e disponibilidade de informação para tomada de decisão, então você deveria dar uma olhada em “Event Driven Architecture”.
Neste artigo entenderemos o conceito de eventos e eventos de negócios, veremos porque uma arquitetura orientada a eventos é importante e como tal arquitetura se realiza de forma lógica. Já na parte 2 desta série, veremos como a arquitetura lógica vista aqui se realiza fisicamente (quais são os produtos envolvidos) e alguns princípios que devem ser considerados ao se estabelecer tal arquitetura.
Muito dificilmente você ainda não ouviu falar do livro Enterprise Integration Patterns, de Gregor Hohpe e Bobby Woolf, com prefácio e contribuição de Martin Fowler. Trata-se de excelente livro sobre integrações, mais especificamente sobre padrões de integrações corporativas, como o próprio título enuncia. O livro aborda padrões de integração amplamente vistos no mercado e explica porque a Mensageria (Messaging) é, dentre eles, o que melhor endereça diversos aspectos que devem ser levados em consideração ao estabelecer integrações confiáveis. Os autores ainda consideram que apesar desta ser uma abordagem amplamente indicada, ela é igualmente pouco conhecido ou aprofundado pelo mercado, e é justamente essa a motivação para que a maior parte do livro discorra sobre os diversos padrões relacionados à mensageria, como por exemplo: Guaranteed Delivery, Message Broker, Message Bus, Publish Subscribe Channel, Request-Reply e diversos outros.
Frequentemente sou questionado com relação a quando a utilização de um ESB (Enterprise Service Bus ou Barramento de Serviços Corporativo) é interessante em um dado cenário de integração ou não. Espero que este post ajude a esclarecer a esta dúvida, bem como a elucidar as principais funcionalidades que se pode tirar proveito quando se emprega uma ferramenta robusta como esta. Vamos lá!
Em geral, um Enterprise Service Bus está relacionado a estratégias de arquiteturas orientadas a serviço (SOA), com o objetivo de proporcionar a disponibilização de serviços em um local único e centralizado, alavancando reuso, diminuindo acoplamento entre consumidores e provedores de serviços, o que por sua vez reduz significativamente a dependência entre ambos, de forma que manutenções – que, diga-se de passagem, são inevitáveis – possam ser feitas de forma mais tranquila. Todos estes fatores apoiam na redução de custos, seja pela manutenção facilitada (ou com baixo impacto), seja pelo reuso proporcionado.
No post anterior vimos as possíveis situações em que uma chamada a um web service pode falhar. Vimos também que uma possível solução seria utilizar estruturas de enfileiramento de mensagens (MSMQ, WebSphere MQ, por exemplo), e que porém, tais soluções não são indicadas para cenários em que a necessidade de entrega ou recebimento de mensagens ultrapasse o escopo da rede interna da organização.
Outro ponto a se considerar, é que tais abordagens caracterizam o que é conhecido com integração near real-time, onde o dado consumido ou provido chega ao seu destino não em tempo real, mais próximo disso. Existem situações em que requisitos estabelecerão que o dado deverá ser obtido ou entregue ao seu destino em tempo real, o que gera uma demanda por abordagens denominadas real-time.Web service é uma abordagem que permite a implementação de integrações real-time, porém, como visto anteriormente, como endereçar as possíveis falhas que podem ocorrer quando da sua utilização?
Nos dois posts anteriores vimos alguns padrões para requisição e reposta de serviços, como por exemplo, os padrões síncronos Request/Response e Asynchronous Response Handler e os padrões assíncronos Request/Acknowledge/Pool e Request/Acknowledge/Callback, que foram discutidos em detalhes. Em todos os padrões para requisição e resposta de serviços que vimos, podemos nos deparar com problemas de indisponibilidade, o que levará a uma falha que será percebida pela aplicação consumidora. Se tal falha não for tratada adequadamente, poderemos ter o nível de confiabilidade (reliability) da solução diminuído, além da possibilidade de termos impactos para o negócio.
Neste post veremos como endereçar esta questão através de um padrão para tratamento de falhas de disponibilidade de serviços, também descrito no livro Service Design Patterns, de Ian Robinson.
Infraestruturas em geral muito dificilmente estão disponíveis em 100% do tempo. Comumente encontramos níveis de disponibilidade estabelecidas por SLA’s de 99.9%, 99.8% e 98%, que implicam em 8,76, 17,52 e 175 horas de indisponibilidade por ano, respectivamente. Menos comuns, porém, são infraestruturas estruturadas para 99.99% ou 99.999%, que representam 52,56 e 5,25 minutos de indisponibilidade por ano, respectivamente. Diversos são os recursos envolvidos para se estabelecer uma infraestrutura de alta disponibilidade e o custo associado é proporcionalmente alto. Mesmo em infraestruturas de alta disponibilidade, como as que garantem 99.999%, ainda temos períodos de indisponibilidade acordados em contrato, que podem calhar no momento exato da chamada de um serviço hospedado em tal infraestrutura.
É preciso ainda considerar que mesmo serviços hospedados na nuvem estão sujeitos à impossibilidade de acesso, seja por uma indisponibilidade prevista em um SLA definido pelo próprio provedor da infraestrutura, ou por indisponibilidade de banda da organização consumidora, ou ainda do seu provedor de internet (ISP), por exemplo.
Neste contexto, como garantir que chamadas à web services (sejam síncronas ou assíncronas) sejam bem sucedidas, independentemente de indisponibilidades de infraestrutura (falhas de rede ou de servidores)? Veremos a seguir mais detalhes sobre as possíveis causas de falhas em requisições de serviços e posteriormente possíveis soluções.
Tipos de Falha
Toleráveis
Em determinadas situações, falhas em chamadas de serviços são toleráveis. Considere, por exemplo, um serviço de busca de restaurantes, acionado pela implementação de uma funcionalidade acessada diretamente pelo usuário. Caso a requisição a este serviço não seja bem-sucedida, o usuário poderá ser notificado, podendo, por sua vez, acionar a mesma funcionalidade novamente, até que uma eventual falha temporária seja corrigida e o resultado esperado seja retornado.
Falha no estabelecimento de conexão
Um consumidor de serviço pode não ser capaz de se conectar ao serviço seja por instabilidade de rede – redes possuem intrinsicamente características de baixa confiabilidade – ou indisponibilidade de servidor (aquele que hospeda o serviço, por exemplo). Tais problemas podem ser temporários – uma instabilidade retomada em poucos segundos – ou crônica – uma indisponibilidade de servidor mais séria, por exemplo, como danos à CPU ou ao disco, em uma infraestrutura não projetada para alta disponibilidade.
Perda de conexão
Um consumidor de serviço pode enviar uma requisição ao serviço com sucesso, porém, pode ter sua conexão perdida enquanto o serviço ainda está processando sua requisição. Se o consumidor do serviço considerar que houve uma falha e enviar a mesma requisição novamente, é possível que dados sejam duplicados no provedor do serviço, o que causaria inconsistências e diminuiria a confiabilidade da solução.
Sobrecarga de servidores
Servidores podem estar sobrecarregados de tempos em tempos, de forma que se tais eventos coincidirem com momentos em que os serviços hospedados por eles são requisitados, haverá a possibilidade de o servidor recusar o estabelecimento da conexão ou não ser capaz de processá-la.
Timeout
Seja por sobrecarga de servidores, ou por qualquer falha que faça com que o servidor destino demore mais para responder do que o normal, um timeout poderá ser lançado no consumidor do serviço. A configuração e um tempo condizente de timeout é de extrema importância, para que o consumidor do serviço não fique bloqueado indefinidamente em tais situações. É importante notar que quando um timeout é lançado no consumidor do serviço, não é possível estabelecer se a requisição foi processada com sucesso ou não pelo serviço, uma vez que o processamento pode ter sido bem-sucedido momentos após a perda da conexão.
Indisponibilidade de URI
Uma vez estabelecido o serviço em produção, tal problema é menos comum. Porém, existem cenários de implementação que podem potencializar situações como esta. Serviços que implementam o padrão REST, por exemplo, frequentemente retornam como resposta URI’s para outras funcionalidades / dados. Tais URI’s podem ou ser inválidas por um bug de implementação ou podem ainda não estar disponíveis no momento da sua requisição. Este último cenário é conhecido como “race condition”, onde o consumidor do serviço compete com o serviço em si. Se o cliente for mais rápido, então links providos na comunicação anterior podem ser inválidos. Porém, se o cliente aguardar ou tentar novamente, as mesmas URI’s serão válidas posteriormente.
Possíveis soluções
A utilização de um sistema de enfileiramento de mensagens, como Microsoft Message Queue ou WebSphere MQ, resolveria o problema, uma vez que tais sistemas implementam o que se chama de garantia de entrega. Com a garantia de entrega (ou guaranteed delivery), quando uma fila remota não está disponível ou acessível, as mensagens são guardadas em uma fila local até que a infraestrutura remota seja restabelecida. Uma vez que a infraestrutura de enfileiramento de mensagens remota esteja estabelecida novamente, as mensagens pendentes serão entregues, podendo ser consumidas pelo sistema destino.
O problema com esta abordagem é que ele requer um protocolo de comunicação específico e proprietário, o pode não ser viável ou acessível para qualquer sistema. Ou seja, a interoperabilidade provida por protocolos de comunicação estabelecidos sobre HTTP, como SOAP e REST, não é mantida em tal abordagem. Além disso, a exposição de sistemas de enfileiramento de mensagens diretamente através de um firewall corporativo para consumidores externos à organização pode implicar na necessidade de abertura de portas, o que junto com outras questões, podem implicar em riscos à segurança.
Como endereçar então falhas em requisições de web servies de forma adequada?
Este é tópico do próximo post, onde será detalhado o padrão Idempotent Retry Pattern.
No post anterior vimos alguns padrões para modelos de processamento síncronos de web services. Vimos também os problemas decorrentes destes padrões, como por exemplo, o acoplamento temporal, que está relacionado à dependência que consumidores de serviços síncronos possuem com relação ao tempo de processamento do serviço, para que a operação seja bem sucedida. Problemas com estouro do tempo de resposta previsto (timeout) podem fazer com que mensagens sejam perdidas, diminuindo a confiabilidade da abordagem. Outros problemas relacionados também são, por exemplo, a dependência com relação à disponibilidade de todos os demais sistemas subjacentes ao serviço (sistemas gerenciadores de banco de dados, rede, servidores de aplicação, sistema de arquivos, etc.). Caso haja qualquer indisponibilidade em tais componentes, consumidores de serviços síncronos são diretamente afetados. Além disso, cargas acima do planejado em serviços que implementam o padrão Request/Response comprometem seu funcionamento e o da infraestrutura que o suporta, uma vez que todas as requisições são processadas assim que recebidas, dado o fato de que o número de requisições a serem processadas em paralelo é, em geral, comandado pelo cliente.
Para endereçar tais questões, no livro Service Design Patterns é sugerido à utilização de padrões assíncronos, e a utilização de message queues seria uma alternativa. Tal mecanismo permite que um cliente envie uma mensagem para uma fila remota em qualquer horário, independentemente do estado operacional do sistema destino. Usualmente, mensagens são salvas em filas locais até que as filas destino estejam disponíveis. Consumidores de mensagens podem proteger recursos dos sistemas remotos pela implementação de mecanismos de “throttling” (afunilamento – controle da taxa de processamento de mensagens), desta forma endereçando requisitos de escalabilidade e disponibilidade de forma mais eficiente do que o padrão síncrono Request/Response visto anteriormente.
Se por um lado a utilização de message queues resolve boa parte dos problemas, por outro, novos problemas podem ser introduzidos. O uso de mensageria, em geral, é aconselhado como uma abordagem interna à corporação, ou seja, não deveria ser exposto diretamente além do firewall corporativo, seja por questões de segurança, seja por acoplar consumidores externos às tecnologias específicas dos sistemas de mensageria.
Para endereçar tais questões, é sugerida então a utilização do padrão de web services Request/Acknowledge, no qual um web service recebe uma requisição, a encaminha para um processo em background (uma thread secundária, por exemplo) e então retorna uma mensagem ao consumidor (acknowledgment) contendo um identificador único para referência futura. Veja como isso funciona no diagrama de sequencia ao lado (direito).
Em diversas situações apenas o recebimento de um “acknowledgment” é suficiente para o consumidor saber que sua requisição foi armazenada para processamento posterior. Porém, em outras situações, um cliente pode precisar de uma resposta, seja, por exemplo, de um processo de longa duração iniciado pela requisição inicial. Imagine um cenário em que uma requisição é enviada contendo uma solicitação de compra. Um retorno (acknowledgment) é enviado para consumidor informando que a solicitação foi recebida e que será processada. Um acknowledgment contendo um ID único serve como um protocolo de recebimento, um recibo. Quando todos os processos de aprovação da compra (sejam automáticos ou manuais) forem finalizados, o que pode demorar horas ou dias, dependendo do processo da organização, o cliente deverá ser informado, para que possa tomar outras ações.
Existem três formas no qual o padrão Request/Acknowledge permite prover respostas assíncronas aos consumidores de serviços. Vejamos a seguir.
Request/Acknowledge/Poll
Neste padrão, uma vez que o cliente possui um acknowledge contendo um identificador único da sua requisição, ele fica constantemente consultando um outro serviço pelo resultado do seu processamento. Veja no diagrama de sequencia ao lado (esquerdo) a ilustração do fluxo deste padrão.
Os problemas decorrentes da utilização deste padrão estão relacionados ao intervalo de tempo em que o cliente efetua consultas pelo resultado final da operação. Se o intervalo de tempo for alto, então poderá haver um atraso entre o tempo em que o resultado final está pronto e o tempo em que ele é obtido. Se for baixo, então uma carga excessiva poderá ser colocada nos servidores envolvidos, além de aumentar trafego de rede.
Request/Acknowledge/Callback e Request/Acknowledge/Relay
No padrão Request/Acknowledge/Callback ao invés do cliente ficar constantemente buscando pelo resultado final da operação, ele é avisado através de um serviço que ele deve prover ou através do encaminhamento para outras partes interessadas. Esta última variação descreve o padrão Request/Acknowledge/Relay. A vantagem de tais abordagens com relação ao Request/Acknowledge/Poll está no tempo em que o cliente receberá a resposta e nos recursos consumidos para tal. Veja no diagrama de sequencia a mais abaixo (à direita) a ilustração do fluxo descrito.
Note que a forma pela qual o processador de requests pode saber para qual serviço ele deve retornar o resultado pode ser tanto através de um endereço provido no request inicial, pelo próprio cliente (veja, por exemplo, WS-Addressing), quanto através de configurações que relacionem o tipo de mensagem / processamento aos destinatários interessados (relay). Quando os destinatários de mensagens de callback são especificados pelo consumidor, cuidados especiais devem ser tomados para que eles não sejam alterados no meio do caminho. Segurança no nível de transporte ou mesmo WS-Security podem ser usados para mitigar este risco.
Algumas considerações devem ser levadas em conta antes de optar por um dos padrões. Em geral, o padrão Request/Acknowledge/Poll é mais simples de se implementar e de se depurar do que o padrão Request/Acknowledge/Callback. O planejamento de capacity também é diferenciado dentre as abordagens. Se o callback for efetuado sempre para um destinatário apenas, então o número de mensagens de resposta deve ser semelhante ao que se teria no padrão Request/Response. Porém, se o número de destinatários for significante e se houver necessidade de transformar mensagens em formatos específicos para cada destinatário, então, os recursos de infraestrutura necessários devem ser maiores, o que pode implicar em investimentos mais significativos.
O padrão Request/Acknowledge/Callback pode não ser viável em cenários em que o cliente não pode ou não está disposto a prover um serviço para receber retornos, seja pela necessidade de abertura de porta para recebimento de trafego de dados, seja pela falta de recursos para prover infraestrutura necessária. Neste caso, a única alternativa possível para o recebimento de retornos seria o Request/Acknowledge/Poll.
Conclusão
Diante de tantos fatores a serem levados em consideração quando da escolha por um dos padrões mencionados, uma “Trade-off Analysis” pode lhe ser útil para ajudar em sua decisão, dado seu contexto de aplicação.