RabbitMQ-Client源码分析(发送)

938 阅读4分钟

问题:

整合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()