一,什么是RabbitMQ ?
AMQP,即Advanced Message Queuing Protocol,高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。消息中间件主要用于组件之间的解耦,消息的发送者无需知道消息使用者的存在,反之亦然。 AMQP的主要特征是面向消息、队列、路由(包括点对点和发布/订阅)、可靠性、安全。 RabbitMQ是一个开源的AMQP实现,服务器端用Erlang语言编写,支持多种客户端,如:Python、Ruby、.NET、Java、JMS、C、PHP、ActionScript、XMPP、STOMP等,支持AJAX。用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不俗。
二 ,RabbitMQ 的推送流程
简单来说就是 消息生产者 发送消息给交换机,交换机找到和他绑定的队列 -》找到是否有消费者在监听这个队列,有就把消息发送过去。
exchanges 交换机的几种类型:
路由键介绍:队列和交换金绑定时候的一种标识。通过这个标识当我们发送信息给交换机时候带着这个路由键,系统就能够找到这个路由键下的队列。
- Direct Exchage 直连型的交换机,(只发送给对应的路由键下的队列)
- fanoutExchage 扇形交换机 (无视路由键会发送给所有绑定在这个交换机上的队列)
- topic exchange exchange 主题交换机 (和直连线交换机差不多,但是他的路由键可以用表达式)
** *(星号) 用来表示一个单词 (必须出现的)
#(井号) 用来表示任意数量(零个或多个)单词 **
3. springboot下使用rabbitMQ 快速开发
依赖
<!--rabbitmq-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
配置文件
#rabbitMq
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=ljb
spring.rabbitmq.password=888888
#确认消息已发送到交换机(Exchange)(可选)
spring.rabbitmq.publisher-confirm-type=correlated
#确认消息已发送到队列(Queue)(可选)
spring.rabbitmq.publisher-returns=true
消息生产者
创建交换机和队列。
@Configuration
public class RabbitMQConfig {
//队列名字
@Bean
public Queue TestDirectQueue() {
return new Queue("TestDirectQueue",true); //true 是否持久
}
//Direct交换机 起名:TestDirectExchange
@Bean
DirectExchange TestDirectExchange() {
return new DirectExchange("TestDirectExchange");
}
//绑定 将队列和交换机绑定, 并设置用于匹配键:TestDirectRouting
@Bean
Binding bindingDirect() {
return BindingBuilder.bind(TestDirectQueue()).to(TestDirectExchange()).with("TestDirectRouting");
}
//队列名字
@Bean
public Queue ljbQueu() {
return new Queue("ljbQueu",true); //true 是否持久
}
//Direct交换机 起名:TestDirectExchange
@Bean
DirectExchange libExchange() {
return new DirectExchange("libExchange");
}
@Bean
Binding ljbBind() {
return BindingBuilder.bind(TestDirectQueue()).to(libExchange()).with("hello");
}
@Bean
Binding libExchang() {
return BindingBuilder.bind(ljbQueu()).to(libExchange()).with("hello");
}
//回调机制 防止丢失
@Bean
public RabbitTemplate createRabbitTemplate(ConnectionFactory connectionFactory){
RabbitTemplate rabbitTemplate = new RabbitTemplate();
rabbitTemplate.setConnectionFactory(connectionFactory);
//设置开启Mandatory,才能触发回调函数,无论消息推送结果怎么样都强制调用回调函数
rabbitTemplate.setMandatory(true);
rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
System.out.println("ConfirmCallback: "+"相关数据:"+correlationData);
System.out.println("ConfirmCallback: "+"确认情况:"+ack);
System.out.println("ConfirmCallback: "+"原因:"+cause);
}
});
rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
@Override
public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
System.out.println("ReturnCallback: "+"消息:"+message);
System.out.println("ReturnCallback: "+"回应码:"+replyCode);
System.out.println("ReturnCallback: "+"回应信息:"+replyText);
System.out.println("ReturnCallback: "+"交换机:"+exchange);
System.out.println("ReturnCallback: "+"路由键:"+routingKey);
}
});
return rabbitTemplate;
}
}
使用回调机制的除了和上面代码一样在配置文件中直接交给spring也能可以这样操作
package com.example.demo.RabbitMQ;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
/**
* @Author:linjunbo
* @Description: rabbitMq 信息发送回调
* @Date: 2020/3/5 14:51
*/
@Component
public class RabbitMQCallBack implements RabbitTemplate.ConfirmCallback , RabbitTemplate.ReturnCallback {
@Autowired
private RabbitTemplate rabbitTemplate;
/**
* PostConstruct: 用于在依赖关系注入完成之后需要执行的方法上,以执行任何初始化.
*/
@PostConstruct
public void init() {
//指定 ConfirmCallback
rabbitTemplate.setConfirmCallback(this);
//指定 ReturnCallback
rabbitTemplate.setReturnCallback(this);
}
/**
* 如果消息没有到达交换机,则该方法中ack = false,error为错误信息;
* 如果消息正确到达交换机,则该方法中ack = true;
* @param correlationData
* @param ack
* @param cause
*/
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
System.out.println("ConfirmCallback: "+"相关数据:"+correlationData);
System.out.println("ConfirmCallback: "+"确认情况:"+ack);
System.out.println("ConfirmCallback: "+"原因:"+cause);
}
/**
* 信息未正确到达队列则触发
* @param message
* @param replyCode
* @param replyText
* @param exchange
* @param routingKey
*/
@Override
public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
System.out.println("ReturnCallback: "+"消息:"+message);
System.out.println("ReturnCallback: "+"回应码:"+replyCode);
System.out.println("ReturnCallback: "+"回应信息:"+replyText);
System.out.println("ReturnCallback: "+"交换机:"+exchange);
System.out.println("ReturnCallback: "+"路由键:"+routingKey);
}
}
control层 生产者发送信息
@RestController
public class SendRabbitMQController {
@Autowired
private RabbitTemplate rabbitTemplate;
@GetMapping("/sendDirectMessage")
public String sendDirectMessage() {
String messageId = String.valueOf(1);
String messageData = "test message hello!";
String createTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
Map<String,Object> map=new HashMap<>();
map.put("messageId",messageId);
map.put("messageData",messageData);
map.put("createTime",createTime);
//将消息携带绑定键值:TestDirectRouting 发送到交换机TestDirectExchange
rabbitTemplate.convertAndSend("TestDirectExchange", "TestDirectRouting", map);
return "ok";
}
启动程序 调用sendDirectMessage接口,控制台输出
可以看到信息已经成功发送到rabbitMQ 并且成功回调comfig函数。
消费者:
依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
配置文件
server.port=8082
spring.rabbitmq.port=5672
spring.rabbitmq.host=localhost
spring.rabbitmq.username=guest
spring.activemq.password=guest
创建 DirectReceiver监听队列
@Component
public class DirectReceive {
@RabbitHandler
@RabbitListener(queues = "TestDirectQueue")//监听的队列名称 TestDirectQueue
public void process(Map testMessage) {
System.out.println("DirectReceiver消费者收到消息 (TestDirectQueue) : " + testMessage.toString());
}
}
运行起来 就能够生产者发送消息 客户端消费者监听的队列有信息就能接收到。
加入消费者确认机制。默认rabbitMQ是自动的 但是有时候希望自己来手动确认收到信息则可以这样。
1.修改DirectReceiver 监听队列 实现 ChannelAwareMessageListener 重写onMessage方法
添加消费者确认机制 消费者手动执行
basic.ack用于肯定确认
basic.nack用于否定确认(注意:这是AMQP 0-9-1的RabbitMQ扩展)
basic.reject用于否定确认,但与basic.nack相比有一个限制:一次只能拒绝单条消息
@Component
@RabbitListener(queues = "TestDirectQueue")//监听的队列名称 TestDirectQueue
public class DirectReceiver implements ChannelAwareMessageListener {
@Override
public void onMessage(Message message, Channel channel) throws Exception {
long deliveryTag = message.getMessageProperties().getDeliveryTag();
try {
String msg = message.toString();
String[] msgArray = msg.split("'");//可以点进Message里面看源码,单引号直接的数据就是我们的map消息数据
Map<String, String> msgMap = mapStringToMap(msgArray[1].trim());
String messageId=msgMap.get("messageId");
String messageData=msgMap.get("messageData");
System.out.println("messageId:"+messageId+" messageData:"+messageData);
//确定收到信息
channel.basicAck(deliveryTag, true);
} catch (Exception e) {
//true 重新回队里 (回到队里后会一直发送给消费端,直到确定收到)
channel.basicReject(deliveryTag, true);
e.printStackTrace();
}
}
//{key=value,key=value,key=value} 格式转换成map
private Map<String, String> mapStringToMap(String str) {
str = str.substring(1, str.length() - 1);
String[] strs = str.split(",");
Map<String, String> map = new HashMap<String, String>();
for (String string : strs) {
String key = string.split("=")[0].trim();
String value = string.split("=")[1];
map.put(key, value);
}
return map;
}
二 创建RabbitMQConfig
package com.example.demo.config;
import com.example.demo.RabbitMQ.DirectReceiver;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @Author:linjunbo
* @Description:
* @Date: 2020/3/4 11:42
*/
@Configuration
public class RabbitMQConfig {
@Autowired
private CachingConnectionFactory connectionFactory;
@Autowired
private DirectReceiver directReceiver;//Direct消息接收处理类
@Bean //我要监听的队列bean
public Queue TestDirectQueue() {
return new Queue("TestDirectQueue",true); //true 是否持久
}
//开启手动确认
@Bean
public SimpleMessageListenerContainer simpleMessageListenerContainer() {
//设置mq连接工厂对象
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
//初始化消费者数量
container.setConcurrentConsumers(1);
//最大消费者数量
container.setMaxConcurrentConsumers(1);
// RabbitMQ默认是自动确认,这里改为手动确认消息
container.setAcknowledgeMode(AcknowledgeMode.MANUAL);
//监听队列
container.setQueues(TestDirectQueue());
//消费者监听类
container.setMessageListener(directReceiver);
// container.addQueues(fanoutRabbitConfig.queueA());
// container.setMessageListener(fanoutReceiverA);
return container;
}
}
来源:CSDN
作者:林先生随手笔记
链接:https://blog.csdn.net/weixin_41930050/article/details/104673898