ApsaraMQ for RabbitMQ oferece um SDK para o framework Spring. Este tópico descreve como integrar o SDK do Spring para enviar e receber mensagens.
Pré-requisitos
Crie recursos como instância, vhost, exchange e fila no console do ApsaraMQ for RabbitMQ. Para mais informações, consulte Step 2: Create resources.
Projeto de demonstração
Clique em SpringBootDemo.zip para baixar o projeto de demonstração.
Etapa 1: Configurar parâmetros
No arquivo application.properties ou application.yml, defina os parâmetros de configuração. O exemplo a seguir utiliza o arquivo application.properties.
# The endpoint. Obtain the endpoint on the Instance Details page in the ApsaraMQ for RabbitMQ console.
spring.rabbitmq.host=XXXXXX.amqp.aliyuncs.com
# The port used to connect to ApsaraMQ for RabbitMQ.
spring.rabbitmq.port=5672
# The static username of the instance. View the username on the Static Accounts page in the ApsaraMQ for RabbitMQ console.
spring.rabbitmq.username=******
# The static password of the instance. View the password on the Static Accounts page in the ApsaraMQ for RabbitMQ console.
spring.rabbitmq.password=******
# The virtual host, which provides logical isolation. View the virtual host on the Vhosts page in the ApsaraMQ for RabbitMQ console.
spring.rabbitmq.virtual-host=test_vhost
# The message acknowledgment (Ack) mode.
# 1. none: After a consumer receives a message, the server considers the message successfully processed, regardless of whether the consumption is successful. This is the autoAck mode in RabbitMQ.
# 2. auto: The client automatically sends an ack after a message is successfully consumed. If the message fails to be processed, the client sends a nack or throws an exception. You do not need to explicitly call Channel.basicAck().
# 3. manual: Manually send an Ack. You must explicitly call Channel.basicAck() after a message is successfully consumed.
spring.rabbitmq.listener.simple.acknowledge-mode=manual
# Set the cache mode to CONNECTION. ApsaraMQ for RabbitMQ uses a distributed architecture. In CONNECTION mode, clients can connect to multiple service nodes in the cluster in a more balanced way. This method can effectively prevent load hot spots and improve the efficiency of message sending and consumption.
# Note: In CONNECTION mode, automatic declaration by RabbitAdmin (auto-creating Exchanges, Queues, and Bindings) does not take effect. You need to manually declare the required topology resources.
spring.rabbitmq.cache.connection.mode=connection
# Adjust the value as needed.
spring.rabbitmq.cache.connection.size=50
# Adjust the value as needed.
spring.rabbitmq.cache.channel.size=1
# The maximum number of unacknowledged (Ack) messages that a consumer can process at a time (QoS). The ApsaraMQ for RabbitMQ server uses min{prefetch, 100} as the QoS value. If the consumer has low processing capabilities, decrease this value.
spring.rabbitmq.listener.simple.prefetch=100
# The minimum number of concurrent consumers for the RabbitMQ listener. Adjust the value as needed.
spring.rabbitmq.listener.simple.concurrency=10
# The maximum number of concurrent consumers for the RabbitMQ listener. When the consumption rate is high enough, the client starts max-concurrency consumers to consume messages.
spring.rabbitmq.listener.simple.max-concurrency=20
Melhores práticas para Connection e Channel
O ApsaraMQ for RabbitMQ utiliza uma arquitetura distribuída com múltiplos nós no backend. Para evitar gargalos de desempenho em um único nó no lado do servidor, siga estas melhores práticas ao configurar as conexões do cliente.
Utilize o modo CONNECTION: Configure o cliente para estabelecer múltiplas conexões TCP com o servidor. Nesse modo, um único cliente distribui as conexões por vários nós de service do backend e aproveita os recursos de diversos nós do cluster. Recomendamos configurar o número de conexões (CONNECTION) por cliente na casa das dezenas e ajustar conforme o volume de tráfego.
Evite criação e encerramento frequente de conexões: Não crie nem feche conexões constantemente. A ciclagem frequente de conexões reduz a eficiência no envio e consumo de mensagens. Mantenha conexões persistentes em vez de criá-las para cada operação.
Limite o número de channels por conexão: Defina o número máximo de channels em uma única conexão como 50. Muitos channels em uma só conexão podem causar picos repentinos de tráfego TCP. Se combinado com reordenação ocasional de pacotes, esse cenário pode acionar o mecanismo Conntrack do Netfilter no Linux, classificar pacotes como INVALID e fazer com que o cliente envie ativamente um RST para redefinir a conexão.
Configure o modo de conexão e a quantidade de CONNECTION/CHANNEL pelos seguintes parâmetros:
spring.rabbitmq.cache.connection.mode- Defina comoconnectionpara usar o modo CONNECTION.spring.rabbitmq.cache.connection.size- Número de conexões. Recomendado: na casa das dezenas.spring.rabbitmq.cache.channel.size- Número de channels por conexão. Recomendado: no máximo 50.
Adicione as configurações opcionais abaixo conforme necessário:
Etapa 2: Usar o SDK para enviar e receber mensagens
Produzir mensagens
Em RabbitMQService, obtenha o RabbitTemplate por injeção de dependência e chame seu método send para enviar uma mensagem.
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.nio.charset.StandardCharsets;
import java.util.UUID;
@Service
public class RabbitMQService {
@Autowired
private RabbitTemplate rabbitTemplate;
public void sendMessage(String exchange, String routingKey, String content) {
// Set the MessageId.
String msgId = UUID.randomUUID().toString();
MessageProperties messageProperties = new MessageProperties();
messageProperties.setMessageId(msgId);
// Create a Message.
Message message = new Message(content.getBytes(StandardCharsets.UTF_8), messageProperties);
/*
* Call the send() interface to send the message.
* exchange: the name of the exchange.
* routingKey: the routing key.
* message: the message content.
* correlationData is used for publisher confirms.
*/
rabbitTemplate.send(exchange, routingKey, message, null);
}
}
Consumir mensagens
Utilize a anotação @RabbitListener para consumir mensagens:
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import java.util.Arrays;
@Component
public class MessageListener {
/**
* Receive a message.
* @param message The message.
* @param channel The channel.
* @throws IOException
* Replace queues with the name of the queue that you created.
*/
@RabbitListener(queues = "myQueue")
public void receiveFromMyQueue(Message message, Channel channel) throws IOException {
// Enter the business logic for message consumption.
...
// You must return an Ack within the Ack validity period (consumption timeout). Otherwise, the confirmation is invalid and the message is redelivered.
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
}
}
Veja a seguir as configurações opcionais comuns para RabbitListener:
Observações importantes
Verifique se o seu cliente utiliza o método receiveAndConvert do RabbitTemplate para consumo de mensagens.
Esse método aceita parâmetros como nome da fila e duração do tempo limite. Quando um tempo limite é especificado, a lógica interna de processamento apresenta o seguinte problema: antes de consumir uma mensagem, o método envia primeiro um comando BasicCancel para cancelar a assinatura do consumidor e, em seguida, envia um Ack para confirmar a mensagem.
No entanto, quando o ApsaraMQ for RabbitMQ recebe uma solicitação BasicCancel, ele cancela imediatamente o consumidor correspondente e recoloca na fila todas as mensagens não confirmadas. Isso significa que o Ack subsequente enviado pelo cliente torna-se efetivamente inválido — o servidor não o trata como uma confirmação válida, mas sim reenvia essas mensagens, causando consumo duplicado no lado do cliente.
Além disso, se a fila tiver um alto throughput de mensagens, operações frequentes de cancelamento e reenfileiramento podem levar ao acúmulo de mensagens e impactar ainda mais a estabilidade do consumo.