问题:
整合Springboot + RbabitMq。
并深入理解。
原理:
通过Channel往Rabbit服务端发送消息。
通过PRC申明交换机,队列,绑定等信息。
通过AMQP协议发送消息
文档:
rabbit的中文文档: http://rabbitmq.mr-ping.com/AMQP/AMQP_0-9-1_Model_Explained.html
Springboot整合Rabbit的源码分析: https://blog.csdn.net/hry2015/article/details/79597281
步骤:
0.下载安装并启动Rabbit这里就不说了。
1.引入Springboot整合的Rabbit依赖
2.手写获取Rabbit的连接,通道等信息
3.分析各大方法
4.Springboot整合的Rabbit
1.引入Springboot整合的Rabbit依赖
为什么用它,因为后面跟着要分享Springboot是怎么自动装配Rabbit的。
<!-- rabbit mq -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
2.手写获取Rabbit的连接,通道等信息
rabbit的原理就是。通过连接,获取通道,数据传输。
1.获取连接(Connection)
2.获取通道(channel)
3.创建交换机(Exchange)
4.创建队列(Queue)
5.队列通过key绑定交换机(Bind)
6.往交换机中的key发送消息
7.其他方法
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* @author dripy
* @date 2020/3/5 14:46
*/
public class PTest {
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory connectionFactory = new ConnectionFactory();
// 设置rabbitmq的服务器地址
connectionFactory.setHost("xxx.xxx.xxx.xxx");
connectionFactory.setUsername("admin");
connectionFactory.setPassword("admin");
connectionFactory.setPort(AMQP.PROTOCOL.PORT);
Connection conn = connectionFactory.newConnection();
String exchaneg = "dripy-Exchange";
String queueName = "dripy-Queue";
String key = "dripy-Exchange-key";
String msg = "这是一条消息";
// 创建一个channel 这个是核心
Channel channel = conn.createChannel();
// 创建一个直连交换机
channel.exchangeDeclare(exchaneg, BuiltinExchangeType.DIRECT);
// 创建一个队列
channel.queueDeclare(queueName, true, false, false, null);
// 绑定队列
channel.queueBind(queueName, exchaneg, key);
// 发送消息
channel.basicPublish(exchaneg, key, null, msg.getBytes());
channel.close();
conn.close();
}
}
3.分析各大方法
重点应该看Channel里的各大方法。
2.Channel channel = conn.createChannel();
通过debug,可以看到,获取到了一个AutorecoveringChannel对象。
3.
// 创建一个直连交换机
channel.exchangeDeclare(exchaneg, BuiltinExchangeType.DIRECT);
申明一个交换机。 下图可以看到AutorecoveringChannel类中的申明一个交换机有多个方法。
涉及到的属性:
exchange : 交换机的名称
Type: 交换机的类型
durable: 是否持久化
autoDelete:是否自动删除
internal: 暂时还不知道什么意思
arguments: 消息头上的额外参数
查看源码 就是封装了交换机的属性。 并通过RPC发送到Rabbit服务端。并记录了信息。
通过debug可以知道,当运行完成这一行。就能在RabbitWeb控制台看到这个交换机已经声明成功。
4
// 创建一个队列
channel.queueDeclare(queueName, true, false, false, null);
声明一个队列,下图可以看到只有两个方法
涉及到的属性:
queue: 队列名称
durable: 是否持久化
exclusive: 队列将仅由声明者的连接使用
autoDelete: 是否自动删除
arguments: 消息头上的额外参数
和交换机一样。封装队列信息并通过RPC发送到Rabbit服务端。并记录了信息。
通过debug可以知道,当运行完成这一行。就能在RabbitWeb控制台看到这个队列已经声明成功。
5
// 绑定队列
channel.queueBind(queueName, exchaneg, key);
队列通过key绑定交换机
涉及到的属性:
queue: 队列名称
exchange: 交换机名称
routingKey: 路由的key
同上。封装信息并通过RPC发送到Rabbit服务端。并记录了信息。
debug执行完这句之后。可以和步骤3对比。发现交换机已经和队列进行了绑定。
6
// 往交换机中的key发送消息
channel.basicPublish(exchaneg, key, null, msg.getBytes());
下图可以看到只有三个方法
涉及到的属性:
exchange: 交换机名称
routingKey: 路由名称
mandatory: true错误的路由消息会被监听到(比如没绑定到队列)
immediate: 是否立即,rabbit不支持。不知道啥意思。
props: 消息头上的额外参数
body: 需要发送的消息
看源码是把消息体写入到Connection里面去,并刷新。
Debug这行后,可以看到消息已经发送到rabbit服务去了
7
其他方法说明。
删除交换机,队列。
解除交换机与队列绑定。
清空队列中的消息。
手动获取队列中的消息等等。
扩展说明:
关于4种交换机。可以看官方文档说明。
发送消息非常简单。
当然,可以做一些扩展。
比如,给发送消息成功做监听
// 将channel 置为 confirm 模式
channel.confirmSelect();
// 设置为confirm模式后,能获取到成功的回调
channel.addConfirmListener(new ConfirmListener() {
@Override
public void handleAck(long deliveryTag, boolean multiple) throws IOException {
System.err.println("消息已经送达到broker deliverTay:" + deliveryTag);
}
@Override
public void handleNack(long deliveryTag, boolean multiple) throws IOException {
System.err.println("这个在broken异常,或者无法投递消息时出现 deliverTay:" + deliveryTag);
}
});
比如给失败监听。
发送消息的时候。给mandatory设置为True.
channel.basicPublish(exchaneg, key, true,null, msg.getBytes());
// 并设置失败的监听。
channel.addReturnListener(new ReturnListener() {
@Override
public void handleReturn(int replyCode, String replyText, String exchange, String routingKey, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("交换机和key没有绑定队列的时候:");
}
});
4.Springboot整合的Rabbit
Springboot 的自动装配,往容器中注入了Rabbit相关的Bean。
RabbitAutoConfiguration 类创建连接,创建RabbitTemplate
RabbitTemplate就相当于封装了channel的各种方法。
比如。rabbitTemplate.convertAndSend() 发送消息
实际上底层调用的是channel.basicPulish()