上一节我们写了一段原生API来进行生产和消费的例子。实际上SpringBoot对原生RabbitMQ客户端做了二次封装,让我们使用API的代价更低。
依赖引入
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-amqpartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>com.google.code.gsongroupId>
<artifactId>gsonartifactId>
<version>2.8.6version>
dependency>
dependencies>
RabbitMQ的配置如下
spring:
rabbitmq:
host: 192.168.56.201
port: 5672
username: hello
password: world
#虚拟host
virtual-host: virtual01
template:
mandatory: true #当mandatory设置为true时,交换器无法根据自身的类型和路由键找到一个符合条件的队列,那么RabbitMQ会调用Basic.Return命令将消息返回给生产者。当为false时,则直接丢弃消息
publisher-confirm-type: correlated #生产者回调确认机制,由回调来确定消息是否发布成功
publisher-returns: true #是否开启生产者returns
listener:
simple:
acknowledge-mode: manual #手动回复方式,一般建议手动回复,即需要我们自己调用对应的ACK方法
prefetch: 10 #每个消费者可拉取的,还未ack的消息数量
concurrency: 3 #消费端(每个Listener)的最小线程数
max-concurrency: 10 #消费端(每个Listener)的最大线程数
每个配置的具体含义,详见配置
@Slf4j
@RestController
@RequestMapping("/rabbit")
public class RabbitSendController implements RabbitTemplate.ConfirmCallback, RabbitTemplate.ReturnCallback {
private static final String EXCHANGE_NAME = "my_exchange";
private static final String ROUTING_KEY = "my_routing";
@Autowired
private RabbitTemplate rabbitTemplate;
@RequestMapping("send")
public String send() {
for (int i = 0; i < 10; i++) {
OrderInfo orderInfo = new OrderInfo();
orderInfo.setAddress("成都市高新区");
orderInfo.setOrderId(String.valueOf(i));
orderInfo.setProductName("华为P60:" + i);
//设置回调关联的一个id
String messageId = UUID.randomUUID().toString();
log.info("开始发送消息,当前消息关联id为:{}", messageId);
CorrelationData correlationData = new CorrelationData(messageId);
MessageProperties messageProperties = new MessageProperties();
messageProperties.setDeliveryMode(MessageDeliveryMode.PERSISTENT);
Message message = MessageBuilder.withBody(new Gson().toJson(orderInfo).getBytes(StandardCharsets.UTF_8))
.andProperties(messageProperties).build();
//设置ack回调
rabbitTemplate.setConfirmCallback(this);
//退回消息的回调
rabbitTemplate.setReturnCallback(this);
rabbitTemplate.convertAndSend(EXCHANGE_NAME, ROUTING_KEY, message, correlationData);
}
return "ok";
}
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
if (correlationData == null) {
return;
}
String messageId = correlationData.getId();
if (ack) {
log.info("【confirm回调方法】,消息发布成功,messageId={}", messageId);
} else {
log.info("【confirm回调方法】,消息发布失败,messageId={}", messageId);
}
}
@Override
public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
log.info("【returnedMessage回调方法】,消息被退回,message={},replyCode:{},replyText:{},exchange:{},routingKey:{}",
new String(message.getBody()), replyCode, replyText, exchange, routingKey);
}
}
代码说明
运行生产者代码,输出日志如下
开始发送消息,当前消息关联id为:b60196e7-4ff2-4926-8a1f-bd0872b236f8
开始发送消息,当前消息关联id为:03232b2c-b755-4b46-9a8c-6b2bbf3b2bd6
【confirm回调方法】,消息发布成功,messageId=b60196e7-4ff2-4926-8a1f-bd0872b236f8
【confirm回调方法】,消息发布成功,messageId=03232b2c-b755-4b46-9a8c-6b2bbf3b2bd6
@Slf4j
@Component
public class RabbitOrderConsumer {
private static final String EXCHANGE_NAME = "my_exchange";
private static final String QUEUE_NAME = "my_queue";
private static final String ROUTING_KEY = "my_routing";
@RabbitListener(bindings = {@QueueBinding(value = @Queue(value = QUEUE_NAME, durable = "true"),
exchange = @Exchange(value = EXCHANGE_NAME, type = "topic", durable = "true"), key = ROUTING_KEY)})
public void handleMessage(Message message, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag) throws IOException {
log.info("接收到消息:{},deliveryTag:{}", new String(message.getBody(), StandardCharsets.UTF_8), tag);
channel.basicAck(tag, false);
}
}
代码说明
接收到消息:{"orderId":"1","productName":"华为P60:1","address":"成都市高新区"},deliveryTag:1
接收到消息:{"orderId":"4","productName":"华为P60:4","address":"成都市高新区"},deliveryTag:2
示例代码git仓库:https://gitee.com/syk1234/mqdmo
@Queue注解为我们提供了队列相关的一些属性,具体如下:
name: 队列的名称;
durable: 是否持久化;
exclusive: 是否独享、排外的;
autoDelete: 是否自动删除;
arguments:队列的其他属性参数,有如下可选项,可参看图2的arguments:
x-message-ttl:消息的过期时间,单位:毫秒;
x-expires:队列过期时间,队列在多长时间未被访问将被删除,单位:毫秒;
x-max-length:队列最大长度,超过该最大值,则将从队列头部开始删除消息;
x-max-length-bytes:队列消息内容占用最大空间,受限于内存大小,超过该阈值则从队列头部开始删除消息;
x-overflow:设置队列溢出行为。这决定了当达到队列的最大长度时消息会发生什么。有效值是drop-head、reject-publish或reject-publish-dlx。仲裁队列类型仅支持drop-head;
x-dead-letter-exchange:死信交换器名称,过期或被删除(因队列长度超长或因空间超出阈值)的消息可指定发送到该交换器中;
x-dead-letter-routing-key:死信消息路由键,在消息发送到死信交换器时会使用该路由键,如果不设置,则使用消息的原来的路由键值
x-single-active-consumer:表示队列是否是单一活动消费者,true时,注册的消费组内只有一个消费者消费消息,其他被忽略,false时消息循环分发给所有消费者(默认false)
x-max-priority:队列要支持的最大优先级数;如果未设置,队列将不支持消息优先级;
x-queue-mode(Lazy mode):将队列设置为延迟模式,在磁盘上保留尽可能多的消息,以减少RAM的使用;如果未设置,队列将保留内存缓存以尽可能快地传递消息;
x-queue-master-locator:在集群模式下设置镜像队列的主节点信息。
@RabbitListener 提供消费者配置