rabbitMQ的进阶使用(生产者发布消息与消费者订阅消息)
整合rabbitMQ
首先整合rabbitMQ 需要的初步参考
初步使用rabbitMQ
实现服务端的消息抵达通知
我们现在对配置文件进行修改
# RabbitMQ配置
rabbitmq:
host: 123.57.234.28
port: 5672
# 虚拟主机配置
virtual-host: /
publisher-returns: true
## 只要消息抵达Queue,就会异步发送优先回调returnfirm
template:
mandatory: true
# 手动ack消息,不使用默认的消费端确认
listener:
direct:
acknowledge-mode: manual
#spring.rabbitmq.publisher-confirm-type新版发布确认属性有三种确认类型
#NONE值是禁用发布确认模式,是默认值
#CORRELATED值是发布消息成功到交换器后会触发回调方法
#simple 的用法 除了包含CORRELATED
# 开启发送端消息抵达Queue确认
publisher-confirm-type: correlated
需要去设置回调方法
配置RabbitTemplate的回调
@Configuration
public class MyRabbitConfig {
private RabbitTemplate rabbitTemplate;
@Primary
@Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
this.rabbitTemplate = rabbitTemplate;
rabbitTemplate.setMessageConverter(messageConverter());
initRabbitTemplate();
return rabbitTemplate;
}
@Bean
public MessageConverter messageConverter() {
return new Jackson2JsonMessageConverter();
}
/**
* 定制RabbitTemplate
* 1、服务收到消息就会回调
* 1、spring.rabbitmq.publisher-confirms: true
* 2、设置确认回调
* 2、消息正确抵达队列就会进行回调
* 1、spring.rabbitmq.publisher-returns: true
* spring.rabbitmq.template.mandatory: true
* 2、设置确认回调ReturnCallback
* <p>
* 3、消费端确认(保证每个消息都被正确消费,此时才可以broker删除这个消息)
*/
// @PostConstruct //MyRabbitConfig对象创建完成以后,执行这个方法
public void initRabbitTemplate() {
/**
* 1、只要消息抵达Broker就ack=true
* correlationData:当前消息的唯一关联数据(这个是消息的唯一id)
* ack:消息是否成功收到
* cause:失败的原因
*/
//设置确认回调
rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
System.out.println("confirm...correlationData[" + correlationData + "]==>ack:[" + ack + "]==>cause:[" + cause + "]");
});
/**
* 只要消息没有投递给指定的队列,就触发这个失败回调
* message:投递失败的消息详细信息
* replyCode:回复的状态码
* replyText:回复的文本内容
* exchange:当时这个消息发给哪个交换机
* routingKey:当时这个消息用哪个路邮键
*/
rabbitTemplate.setReturnCallback((message,replyCode,replyText,exchange,routingKey) -> {
System.out.println("Fail Message["+message+"]==>replyCode["+replyCode+"]" +
"==>replyText["+replyText+"]==>exchange["+exchange+"]==>routingKey["+routingKey+"]");
});
}
}
测试接受消息
服务器端消息发送
首先在配置文件中加入要发送的exchange 和queue 以及绑定的路由
myRabbitmq:
exchange: hello-java-exchange
queue: hello-java-queue
routeKey: hello.java
编写发送测试类,这里用接口调用形式发送
@RestController
@Api(tags= "rabbit发送")
public class RabbitController {
@Autowired
private RabbitTemplate rabbitTemplate;
@Value("${myRabbitmq.exchange}")
private String exchange;
@Value("${myRabbitmq.routeKey}")
private String routeKey;
@ApiOperation(value = "发送")
@GetMapping("/sendMQ")
public String sendMQ(@RequestParam(value = "num", required = false, defaultValue = "10") Integer num){
OrderEntity entity = new OrderEntity();
entity.setId(1L);
entity.setCommentTime(new Date());
entity.setCreateTime(new Date());
entity.setConfirmStatus(0);
entity.setAutoConfirmDay(1);
entity.setGrowth(1);
entity.setMemberId(12L);
OrderItemEntity orderEntity = new OrderItemEntity();
orderEntity.setCategoryId(225L);
orderEntity.setId(1L);
orderEntity.setOrderSn("mall");
orderEntity.setSpuName("华为");
for (int i = 0; i < num; i++) {
if(i % 2 == 0){
entity.setReceiverName("FIRE-" + i);
rabbitTemplate.convertAndSend(this.exchange, this.routeKey, entity, new CorrelationData(UUID.randomUUID().toString().replace("-","")));
}else {
orderEntity.setOrderSn("mall-" + i);
//rabbitTemplate.convertAndSend(this.exchange, this.routeKey, orderEntity, new CorrelationData(UUID.randomUUID().toString().replace("-","")));
// 测试消息发送失败
rabbitTemplate.convertAndSend(this.exchange, this.routeKey + "test", orderEntity);
}
}
return "ok";
}
}
调用发送接口,设置num数为10
这样有5条会发送失败
接受端消息处理以及通知
我们用consumer接受队列里面的
接受消息需要把接受类放在spring容器内 @Component
@RabbitListener(queues = “${myRabbitmq.queue}”,concurrency=“4”)
实现监听队列消息
用 @RabbitHandler 标注方法真正处理消息
channel.basicAck(deliveryTag, false)告诉mq已接收到消息,不用重新入队了
@Component
@RabbitListener(queues = "${myRabbitmq.queue}",concurrency="4")
public class TestConsumer {
/**
* 1.Message message: 原生消息类型 详细信息
* 2.T<发送消息的类型> OrderEntity orderEntity [Spring自动帮我们转换]
* 3.Channel channel: 当前传输数据的通道
*
* // 同一个消息只能被一个人收到
*
*
* @RabbitListener: 只能标注在类、方法上 配合 @RabbitHandler
* @RabbitHandler: 只能标注在方法上 [重载区分不同的消息]
*/
@RabbitHandler
public void receiveMessageA(Message message, OrderEntity orderEntity, Channel channel){
System.out.println("接受到消息: " + message + "\n内容:" + orderEntity);
try {
Thread.sleep(200);
} catch (InterruptedException e) { }
// 这个是一个数字 通道内自增
long deliveryTag = message.getMessageProperties().getDeliveryTag();
try {
// 只签收当前货物 不批量签收
channel.basicAck(deliveryTag, false);
// deliveryTag: 货物的标签 multiple: 是否批量拒收 requeue: 是否重新入队
// channel.basicNack(deliveryTag, false,true);
// 批量拒绝
// channel.basicReject();
} catch (IOException e) {
System.out.println("网络中断");
}
System.out.println(orderEntity.getReceiverName() + " 消息处理完成");
}
@RabbitHandler
public void receiveMessageB(Message message, OrderItemEntity orderEntity, Channel channel){
System.out.println("接受到消息: " + message + "\n内容:" + orderEntity);
try {
Thread.sleep(2000);
} catch (InterruptedException e) { }
long deliveryTag = message.getMessageProperties().getDeliveryTag();
try {
channel.basicAck(deliveryTag, false);
} catch (IOException e) {
System.out.println("网络中断");
}
System.out.println(orderEntity.getOrderSn() + " 消息处理完成");
}
}