A arquitetura de microsserviço é uma abordagem muito popular no projeto e implementação de aplicativos da web altamente escalonáveis. A comunicação em um aplicativo monolítico entre componentes geralmente é baseada em chamadas de método ou função dentro do mesmo processo. Um aplicativo baseado em microsserviços, por outro lado, é um sistema distribuído executado em várias máquinas.
A comunicação entre esses microsserviços é importante para ter um sistema estável e escalonável. Existem várias maneiras de fazer isso. A comunicação baseada em mensagens é uma forma confiável de fazer isso.
Ao usar o sistema de mensagens, os componentes interagem uns com os outros trocando mensagens de forma assíncrona. As mensagens são trocadas por meio de canais.
Quando o serviço A deseja se comunicar com o serviço B, em vez de enviá-lo diretamente, A o envia para um canal específico. Quando o serviço B deseja ler a mensagem, ele a pega de um canal de mensagem específico.
Neste tutorial de integração do Spring, você aprenderá como implementar mensagens em um aplicativo Spring usando Redis. Você verá um exemplo de aplicativo em que um serviço está enviando eventos para a fila e outro serviço processando esses eventos um por um.
O projeto Spring Integration estende a estrutura Spring para fornecer suporte para mensagens entre ou dentro de aplicativos baseados em Spring. Os componentes são conectados por meio do paradigma de mensagens. Os componentes individuais podem não estar cientes de outros componentes no aplicativo.
O Spring Integration fornece uma ampla seleção de mecanismos para se comunicar com sistemas externos. Adaptadores de canal são um mecanismo usado para integração unilateral (envio ou recebimento). E os gateways são usados para cenários de solicitação / resposta (entrada ou saída).
O Apache Camel é uma alternativa amplamente utilizada. A integração Spring é geralmente preferida em serviços existentes baseados em Spring, pois é parte do ecossistema Spring.
Redis é um armazenamento de dados na memória extremamente rápido. Opcionalmente, ele também pode persistir em um disco. Suporta diferentes estruturas de dados, como pares chave-valor simples, conjuntos, filas, etc.
Usar o Redis como uma fila torna o compartilhamento de dados entre os componentes e o dimensionamento horizontal muito mais fácil. Um produtor ou vários produtores podem enviar dados para a fila e um consumidor ou vários consumidores podem extrair os dados e processar o evento.
Vários consumidores não podem consumir o mesmo evento - isso garante que um evento seja processado uma vez.
Benefícios de usar o Redis como uma fila de mensagens:
Regras:
A seguir, apresentamos a criação de um aplicativo de amostra para explicar como usar o Spring Integration com Redis.
Digamos que você tenha um aplicativo que permite aos usuários publicar postagens. E você deseja construir um recurso para seguir. Outro requisito é que cada vez que alguém publica uma postagem, todos os seguidores devem ser notificados por meio de algum canal de comunicação (por exemplo, e-mail ou notificação push).
Uma maneira de implementar isso é enviar um e-mail para cada seguidor assim que o usuário publicar algo. Mas o que acontece quando o usuário tem 1.000 seguidores? E quando 1.000 usuários publicam algo em 10 segundos, cada um deles com 1.000 seguidores? Além disso, a postagem do editor vai esperar até que todos os e-mails sejam enviados?
Os sistemas distribuídos resolvem esse problema.
Esse problema específico pode ser resolvido usando uma fila. O serviço A (o produtor), que é responsável por publicar as postagens, fará exatamente isso. Ele publicará uma postagem e enviará um evento com a lista de usuários que precisam receber um e-mail e a própria postagem. A lista de usuários pode ser obtida no serviço B, mas para simplificar este exemplo, iremos enviá-la do serviço A.
Esta é uma operação assíncrona. Isso significa que o serviço que está publicando não terá que esperar para enviar emails.
O serviço B (o consumidor) puxará o evento da fila e o processará. Dessa forma, poderíamos facilmente dimensionar nossos serviços e poderíamos ter n
consumidores enviando emails (processamento de eventos).
Então, vamos começar com uma implementação no serviço do produtor. As dependências necessárias são:
redis.clients jedis org.springframework.data spring-data-redis org.springframework.integration spring-integration-redis
Essas três dependências do Maven são necessárias:
Em seguida, precisamos configurar o cliente Jedis:
@Configuration public class RedisConfig { @Value('${redis.host}') private String redisHost; @Value('${redis.port:6379}') private int redisPort; @Bean public JedisPoolConfig poolConfig() { JedisPoolConfig poolConfig = new JedisPoolConfig(); poolConfig.setMaxTotal(128); return poolConfig; } @Bean public RedisConnectionFactory redisConnectionFactory(JedisPoolConfig poolConfig) { final JedisConnectionFactory connectionFactory = new JedisConnectionFactory(); connectionFactory.setHostName(redisHost); connectionFactory.setPort(redisPort); connectionFactory.setPoolConfig(poolConfig); connectionFactory.setUsePool(true); return connectionFactory; } }
A anotação @Value
significa que o Spring injetará o valor definido nas propriedades do aplicativo no campo. Isso significa redis.host
e redis.port
os valores devem ser definidos nas propriedades do aplicativo.
Agora, precisamos definir a mensagem que queremos enviar para a fila. Um exemplo simples de mensagem poderia ser assim:
@Getter @Setter @Builder public class PostPublishedEvent { private String postUrl; private String postTitle; private List emails; }
Nota: Projeto Lombok ( https://projectlombok.org/ ) fornece @Getter
, @Setter
, @Builder
e muitas outras anotações para evitar a confusão de código com getters, setters e outras coisas triviais. Você pode aprender mais sobre isso em este artigo do ApeeScape .
A própria mensagem será salva no formato JSON na fila. Cada vez que um evento é publicado na fila, a mensagem é serializada em JSON. E ao consumir da fila, a mensagem será desserializada.
Com a mensagem definida, precisamos definir a própria fila. Na integração Spring, isso pode ser feito facilmente por meio de um .xml
configuração. A configuração deve ser colocada dentro de resources/WEB-INF
diretório.
MessageChannel
Na configuração, você pode ver a parte “int-redis: queue-outbound-channel-adapter”. Suas propriedades são:
RedisConnectionFactory
do qual este terminal recebe mensagens.#root
feijão.RedisSerializer
variável. Este atributo é mutuamente exclusivo com a fila.JdkSerializationRedisSerializer
referência de feijão. Por padrão, é um String
. No entanto, para StringRedisSerializer
cargas úteis, a true
é usado se uma referência de serializador não for fornecida.true
.false
) ou push à direita (quando false
) para gravar mensagens na lista Redis. Se verdadeiro, a lista Redis atua como uma fila FIFO quando usada com um adaptador de canal de entrada de fila Redis padrão. Defina como true
para usar com software que lê a lista com pop esquerdo ou para obter uma ordem de mensagem em pilha. Seu valor padrão é .xml
.A próxima etapa é definir o gateway, que é mencionado em RedisChannelGateway
configuração. Para um gateway, estamos usando o org.toptal.queue
classe de StringRedisSerializer
pacote.
.xml
é usado para serializar a mensagem antes de salvá-la no Redis. Também no RedisChannelGateway
configuração, definimos o gateway e configuramos RedisChannelGateway
como um serviço de gateway. Isso significa que default-request-channel
feijão pode ser injetado em outros grãos. Definimos a propriedade @Gateway
porque também é possível fornecer referências de canal por método usando o public interface RedisChannelGateway { void enqueue(PostPublishedEvent event); }
anotação. Definição de classe:
SpringIntegrationConfig
Para conectar essa configuração em nosso aplicativo, temos que importá-la. Isso é implementado no @ImportResource('classpath:WEB-INF/event-queue-config.xml') @AutoConfigureAfter(RedisConfig.class) @Configuration public class SpringIntegrationConfig { }
classe.
@ImportResource
.xml
a anotação é usada para importar Spring @Configuration
arquivos de configuração em @AutoConfigureAfter
. E enqueue
a anotação é usada para sugerir que uma configuração automática deve ser aplicada após outras classes de configuração automática especificadas.
Agora criaremos um serviço e implementaremos o método que public interface QueueService { void enqueue(PostPublishedEvent event); }
eventos para a fila do Redis.
@Service public class RedisQueueService implements QueueService { private RedisChannelGateway channelGateway; @Autowired public RedisQueueService(RedisChannelGateway channelGateway) { this.channelGateway = channelGateway; } @Override public void enqueue(PostPublishedEvent event) { channelGateway.enqueue(event); } }
enqueue
E agora, você pode enviar facilmente uma mensagem para a fila usando QueueService
método de LPUSH
.
As filas do Redis são simplesmente listas com um ou mais produtores e consumidores. Para publicar uma mensagem em uma fila, os produtores usam o redis-cli monitor
Comando Redis. E se você monitorar o Redis (dica: digite 'LPUSH' 'my-event-queue' '{'postUrl':'test','postTitle':'test','emails':['test']}'
), poderá ver que a mensagem é adicionada à fila:
PostPublishedEvent
Agora, precisamos criar um aplicativo de consumidor que extrairá esses eventos da fila e os processará. O serviço do consumidor precisa das mesmas dependências do serviço do produtor.
Agora podemos reutilizar o resources/WEB-INF
classe para desserializar mensagens.
Precisamos criar a configuração da fila e, novamente, ela deve ser colocada dentro de .xml
diretório. O conteúdo da configuração da fila é:
int-redis:queue-inbound-channel-adapter
No MessageChannel
configuração, SmartLifecycle
pode ter as seguintes propriedades:
true
para o qual enviamos mensagens deste ponto de extremidade.SmartLifecycle
atributo para especificar se este ponto de extremidade deve iniciar automaticamente após o início do contexto do aplicativo ou não. Seu valor padrão é 0
.RedisConnectionFactory
atributo para especificar a fase em que este terminal será iniciado. Seu valor padrão é MessageChannel
.ErrorMessages
feijão.Exceptions
para o qual enviaremos Endpoint
com MessagePublishingErrorHandler
da tarefa de escuta do errorChannel
. Por padrão, o RedisSerializer
subjacente | usa o padrão byte[]
do contexto do aplicativo.Message
referência de feijão. Pode ser uma string vazia, o que significa nenhum serializador. Nesse caso, o JdkSerializationRedisSerializer
bruto | da mensagem Redis de entrada é enviada ao canal como true
carga útil. Por padrão, é um false
.TaskExecutor
, o serializador não pode ser uma string vazia porque as mensagens requerem alguma forma de desserialização (serialização JDK por padrão). Seu valor padrão é SimpleAsyncTaskExecutor
.true
(ou JDK 1.5+ Executor padrão) bean. É usado para a tarefa de escuta subjacente. Por padrão, a false
é usado.true
) ou pop esquerdo (quando false
) para ler mensagens da lista Redis. Se true
, a lista Redis atua como uma fila FIFO quando usada com um adaptador de canal de saída de fila Redis padrão. Defina como json-to-object-transformer
para usar com software que grava na lista com o botão direito ou para obter uma ordem de mensagem em pilha. Seu valor padrão é type='com.toptal.integration.spring.model.PostPublishedEvent'
.A parte importante é o 'ativador de serviço', que define qual serviço e método deve ser usado para processar o evento. '
Além disso, o SpringIntegrationConfig
precisa de um atributo de tipo para transformar JSON em objetos, definido acima para public interface EventProcessingService { void process(PostPublishedEvent event); } @Service('RedisEventProcessingService') public class RedisEventProcessingService implements EventProcessingService { @Override public void process(PostPublishedEvent event) { // TODO: Send emails here, retry strategy, etc :) } }
.
Novamente, para conectar essa configuração, precisaremos do 'BRPOP' 'my-event-queue' '1'
classe, que pode ser a mesma de antes. E, por último, precisamos de um serviço que realmente processe o evento.
|_+_|
Depois de executar o aplicativo, você pode ver no Redis:
|_+_|
Com Spring Integration e Redis, construir um aplicativo de microsserviços Spring não é tão assustador como normalmente seria. Com um pouco de configuração e uma pequena quantidade de código clichê, você pode construir as bases de sua arquitetura de microsserviço em nenhum momento.
Mesmo que você não planeje arranhar totalmente seu projeto Spring atual e mudar para uma nova arquitetura, com a ajuda do Redis, é muito simples obter grandes melhorias de desempenho com filas.
Um aplicativo baseado em microsserviços é um sistema distribuído executado em várias máquinas. Cada serviço no sistema se comunica passando mensagens para os outros.
Em um aplicativo monolítico, todos os componentes residem no mesmo processo e a comunicação geralmente é baseada em chamadas de método ou função dentro do mesmo processo.