Ads Top

Criando uma aplicação com Rabbitmq

 A arquitetura do aplicativo 


Precisamos de um aplicativo que seja usado pelos motoristas de táxi e outro que seja usado pelo cliente. O cliente deve ser capaz de solicitar um táxi por meio do aplicativo e o taxista deve ser capaz de aceitar uma solicitação (a viagem):



O cliente deve ser capaz de inserir informações sobre o ponto de partida e o ponto final da viagem. 


Os motoristas ativos recebem as solicitações e podem aceitá-las. O cliente deve, no final, ser capaz de seguir a localização do táxi durante a viagem. O diagrama a seguir mostra a arquitetura de mensagens que desejamos alcançar:



Esse fluxo pode ser explicado em 10 etapas, conforme destacado no diagrama anterior: 


  1. Um cliente usa o aplicativo móvel para reservar um táxi. Uma solicitação agora é enviada do aplicativo móvel para o serviço de aplicativo. Esta solicitação inclui informações sobre a viagem que o cliente deseja reservar. 
  2. O Serviço de Aplicativo armazena a solicitação em um banco de dados. 
  3. O Serviço de Aplicativo adiciona uma mensagem com informações sobre a viagem a uma fila no RabbitMQ. 
  4. Carros de táxi conectados assinam a mensagem (o solicitação de reserva). 
  5. Um táxi responde ao cliente enviando uma mensagem de volta ao RabbitMQ. 
  6. O serviço de aplicativo assina as mensagens. 
  7. Mais uma vez, o serviço de aplicativo armazena as informações em um banco de dados. 
  8. O serviço de aplicativo encaminha as informações ao cliente.
  9. O app passa a enviar automaticamente a localização geográfica do táxi em determinado intervalo para o RabbitMQ. 
  10. A localização do táxi é então passada direto para o aplicativo móvel do cliente, via WebSockets, para que ele saiba quando o táxi chega.

Vamos começar dando uma olhada nas etapas 1, 2, 3 e 4, conforme mostrado no diagrama anterior, onde um cliente solicita um táxi (uma mensagem é publicada no RabbitMQ) e um motorista de táxi recebe a solicitação (uma mensagem é consumido de RabbitMQ).


Estabelecendo uma conexão sólida com RabbitMQ

As mencionado no artigo Rabbitmq ganha vida, uma conexão de rede física deve ser estabelecida entre os servidores de aplicativos e RabbitMQ. Uma conexão AMQP (Advanced Message Queuing Protocol) é um link entre o cliente e o broker que executa tarefas de rede subjacentes, incluindo autenticação inicial, resolução de IP e rede:




Cada conexão AMQP mantém um conjunto de canais subjacentes. 


Um canal reutiliza uma conexão, dispensando a necessidade de reautorizar e abrir um novo fluxo TCP, tornando-o mais eficiente em termos de recursos. O diagrama a seguir ilustra um canal dentro de uma conexão entre um aplicativo e o RabbitMQ:


Ao contrário da criação de canais, a criação de conexões é uma operação cara, muito parecida com as conexões de banco de dados. 


Normalmente, as conexões de banco de dados são agrupadas, onde cada instância do conjunto é usada por um único encadeamento de execução. 


AMQP é diferente no sentido de que uma única conexão pode ser usada por muitos encadeamentos através de muitos canais multiplexados. 


O processo de handshake para uma conexão AMQP requer pelo menos sete pacotes TCP, e ainda mais quando se usa TLS. Os canais podem ser abertos e fechados com mais frequência, se necessário:


  • Conexões AMQP: 7 pacotes TCP 
  • Canal AMQP: 2 pacotes TCP 
  • Publicação AMQP: 1 pacote TCP (mais para mensagens maiores) 
  • Canal fechado AMQP: 2 pacotes TCP 
  • Conexão fechada AMQP: 2 pacotes TCP
  • Total 14-19 pacotes (mais Acks)

O diagrama a seguir ilustra uma visão geral das informações que são enviadas para conexões e canais:



Estabelecer uma única conexão de longa duração entre o Serviço de Aplicativo e o RabbitMQ é um bom começo. 


Uma decisão deve ser tomada em relação à linguagem de programação e à biblioteca cliente a serem usadas. Os primeiros exemplos são escritos em Ruby, e a biblioteca cliente Bunny (https://github.com/ruby-amqp/bunny) é usada para publicar e consumir mensagens. 


Ruby é uma linguagem fácil de ler e entender, mesmo que não seja familiar para você. O aplicativo deve ser configurado para usar um determinado terminal de conexão, geralmente referido como uma string de conexão; por exemplo, um host e uma porta. 


A string de conexão contém as informações necessárias para estabelecer uma conexão. O número de porta atribuído do AMQP é 5672. O AMQP criptografado por TLS / SSL pode ser usado via AMQPS; é uma versão segura do protocolo AMQP com a porta 5671 atribuída. 


A biblioteca é o elemento que abre a conexão TCP para o endereço IP e a porta de destino. Os parâmetros de conexão foram incluídos como uma sequência de URI em uma variável de ambiente para o código chamado RABBITMQ_URI. Não existe um padrão de URI para URIs AMQP, mas este formato é amplamente utilizado:


 RABBITMQ_URI="amqp://user:password@host/vhost"


De acordo com a documentação do Ruby (Bunny), conectar-se ao RabbitMQ é simples. O código para isso é dividido em blocos de código e pode ser encontrado posteriormente no artigo de instalação.


1 - Adicione o nome de usuário, a senha e o vhost que foram configurados no artigo de instalação, em seguida, adicione a string a um variável de ambiente na máquina:


RABBITMQ_URI="amqp://cc-dev:taxi123@localhost/cc-dev-vhost"

2 - Requer a biblioteca cliente do Bunny:


# Require client library
require "bunny"

3 - Leia o URI de conexão da variável de ambiente e inicie uma conexão:


connection = Bunny.new ENV['RABBITMQ_URI']
# Start a session with RabbitMQ 
connection.start


Isso parece simples até agora, mas queremos um código de nível de produção que possa lidar com as falhas normalmente. E se RabbitMQ não estiver funcionando? Claramente, é ruim se todo o aplicativo estiver inativo. E se o RabbitMQ precisar ser reiniciado? Desejamos que nosso aplicativo se recupere normalmente se ocorrer algum problema. Na verdade, queremos que o aplicativo continue funcionando, independentemente se todo o subsistema de mensagens estar funcionando ou não. A experiência do usuário deve ser suave e fácil de entender, bem como confiável. 


Em resumo, o comportamento que o CC deseja alcançar é o seguinte:


  • Se a conexão com o RabbitMQ for perdida, ele deve se reconectar sozinho. 
  • Se a conexão for interrompida, o envio ou a obtenção de mensagens deve falhar normalmente. 


Quando o aplicativo se conecta ao broker, ele precisa lidar com as falhas de conexão. Nenhuma rede é confiável o tempo todo e configurações incorretas e erros acontecem; o corretor pode estar inativo e assim por diante. Embora não seja automática, neste caso, a detecção de erros deve ocorrer no início do processo. 


Para lidar com falhas de conexão TCP no Bunny, é necessário capturar a exceção:


begin  
connection = Bunny.new ENV['RABBITMQ_URI']  
connection.start
rescue Bunny::TCPConnectionFailed => e  
puts "Connection to server failed"
end

Detectar falhas de conexão de rede é quase inútil se um aplicativo não puder se recuperar delas. A recuperação é uma parte importante do tratamento de erros. Algumas bibliotecas cliente oferecem recursos de recuperação automática de conexão que incluem a recuperação do consumidor. 


Qualquer operação que seja tentada em um canal fechado falhará com uma exceção. Se Bunny detectar uma falha na conexão TCP, ele tentará se reconectar a cada 5 segundos sem limite quanto ao número de tentativas de reconexão. 

É possível desabilitar a recuperação automática da conexão adicionando automatic_recovery => false ao Bunny.new. Essa configuração só deve ser usada se você estiver se reconectando de alguma outra forma ou ao testar a string de conexão.


Trabalhando com canais


Cada operação relacionada ao protocolo AMQP ocorre em um canal. As instâncias de canal são criadas pela instância de conexão. Conforme descrito, um canal é uma conexão virtual (AMQP) dentro da conexão (TCP). Todas as operações realizadas por um cliente acontecem em um canal, as filas são declaradas nos canais e as mensagens são enviadas pelos canais.


Um canal nunca existe por conta própria; está sempre no contexto de uma conexão:


# Declare a channel
channel = connection.create_channel

Os canais em uma conexão são fechados assim que a conexão é fechada ou quando ocorre um erro de canal. As bibliotecas de cliente nos permitem observar e reagir às exceções do canal. 


Mais exceções são geralmente lançadas no nível do canal do que no nível da conexão. As exceções no nível do canal geralmente indicam erros dos quais o aplicativo pode se recuperar, como quando ele não tem permissões ou quando tenta consumir de uma fila excluída. Qualquer tentativa de operação em um canal fechado também falhará, com uma exceção.


Mesmo que as instâncias de canal sejam tecnicamente seguras para thread, é altamente recomendável evitar ter vários threads que estão usando o mesmo canal simultaneamente. 


O app agora é capaz de se conectar a um broker RabbitMQ, abrir um canal e emitir uma série de comandos, todos de maneira thread-safe e exceção-safe. Agora é hora de construir nesta base!

Nenhum comentário:

Tecnologia do Blogger.