定制软件开发【学相伴】狂神说 RabbitMQ笔记(简单使用RabbitMQ)

目录


什么是

 RabbitMQ是一个由erlang开发的AMQP(Advanced Message Queue 定制软件开发高级消息队列协议 )定制软件开发的开源实现,定制软件开发能够实现定制软件开发异步消息处理
 RabbitMQ定制软件开发是一个消息代理:定制软件开发它接受和转发消息。
定制软件开发你可以把它想象成一个邮局:定制软件开发当你把你想要发布的邮定制软件开发件放在邮箱中时,定制软件开发你可以确定邮差先生最定制软件开发终将邮件发送给你的收件人。定制软件开发在这个比喻中,RabbitMQ定制软件开发是邮政信箱,定制软件开发邮局和邮递员。 
RabbitMQ定制软件开发和邮局的主要区别在于定制软件开发它不处理纸张,而是接受,定制软件开发存储和转发二进制数据块
        
优点:异步消息处理
           业务解耦(定制软件开发下订单操作:扣减库存、生成订单、发红包、发短信),定制软件开发将下单操作主流程:扣减库存、定制软件开发生成订单然后通过MQ定制软件开发消息队列完成通知,发红包、发短信
            错峰流控 (通知量 消息量 定制软件开发订单量大的情况实现MQ定制软件开发消息队列机制,定制软件开发淡季情况下访问量会少)
        
              定制软件开发灵活的路由(Flexible Routing) 
             定制软件开发在消息进入队列之前,通过 Exchange 定制软件开发来路由消息的。定制软件开发对于典型的路由功能,RabbitMQ 定制软件开发已经提供了一些内置的 Exchange 来实现。定制软件开发针对更复杂的路由功能,定制软件开发可以将多个 Exchange 定制软件开发绑定在一起,定制软件开发也通过插件机制实现自己的 Exchange 。
        RabbitMQ定制软件开发网站端口号:15672
        定制软件开发程序里面实现的端口为:5672


       

 

使用docker安装RabbitMQ,定制软件开发如果没有使用过的可以定制软件开发看这篇文章

1.拉取RabbitMQ镜像

docker pull rabbitmq:management

2.运行RabbitMQ镜像

docker run -itd --name rabbit01 --hostname myrabbit -e RABBITMQ_DEFAULT_USER=chenjinxian -e RABBITMQ_DEFAULT_PASS=chenjinxian -p 15672:15672 -p  5672:5672 -p 25672:25672 rabbitmq:management

注意:RABBITMQ_DEFAULT_USER=chenjinxian -e RABBITMQ_DEFAULT_PASS=chenjinxian 

   定制软件开发这里设置的是(RABBITMQ_DEFAULT_USER)定制软件开发登录的账号和( RABBITMQ_DEFAULT_PASS)密码,定制软件开发根据自身来修改

 定制软件开发这里看到容器已经开启成功了,定制软件开发然后就可以使用了

 

3.定制软件开发通过浏览器打开

定制软件开发如果你使用的是本地虚拟机,定制软件开发那么你直接使用虚拟机显示的ipv4定制软件开发地址加端口号就可以访问了;

定制软件开发如果你使用的是云服务器,定制软件开发那么你需要在对应服务器(阿里云,腾讯云等)定制软件开发的安全组中开放15672端口,定制软件开发并且在防火墙中也开放15672端口

 

 定制软件开发显示如上图那么就可以定制软件开发开始使用了

定制软件开发然后通过命令进入rabbitmq容器

docker exec -it rabbit01 /bin/bash

定制软件开发授权账号和密码

rabbitmqctl add_user admin admin

定制软件开发设置用户分配操作权限

rabbitmqctl set_user_tags admin administrator

用户级别:

  1. administrator:定制软件开发可以登录控制台、查看所有信息、可以对 rabbitmq进行管理

  2. monitoring:监控者 登录控制台,查看所有信息

  3. policymaker:策略制定者 登录控制台,指定策略

  4. managment 普通管理员 登录控制台

为用户添加资源权限

rabbitmqctl set_permissions -p / admin ".*"".*"".*"

也可以在界面操作进行添加用户

 

RabbitMQ支持的消息模型

1.简单模式 Simple  

 

     2.工作模式 Work

       

 

     3.发布订阅模式

 4.路由模式

5.主题 Topic模式

 

      6.参数模式

7.出版商确认模式

 

1.入门案例

1. RabbitMQ入门案例 - Simple 简单模式

  1. jdk1.8

  2. 构建一个 maven工程

  3. 定义生产者

  4. 定义消费者

  5. 观察消息的在 rabbitmq-server服务中的进程

 

01 构建一个maven工程

02 导入依赖

  1. <dependencies>
  2. <!--导入rabbitmq的依赖-->
  3. <dependency>
  4. <groupId>com.rabbitmq</groupId>
  5. <artifactId>amqp-client</artifactId>
  6. <version>5.13.0</version>
  7. </dependency>
  8. </dependencies>

3.代码编写 

 在上图的模型中,有以下概念:

  1. 生产者,也就是要发送消息的程序

  2. 消费者:消息的接受者,会一直等待消息到来。

  3. 消息队列:图中红色部分。类似一个邮箱,可以缓存消息;生产者向其中投递消息,消费者从其中取出消息。

生产者

  1. package com.chen.rabbitmq.simple;
  2. import com.rabbitmq.client.Channel;
  3. import com.rabbitmq.client.Connection;
  4. import com.rabbitmq.client.ConnectionFactory;
  5. import com.rabbitmq.client.MessageProperties;
  6. /**
  7. * @description: 简单模式Simple
  8. */
  9. public class Producer {
  10. public static void main(String[] args) {
  11. // 所有的中间件技术都是基于tcp/ip协议基础之上构建新型的协议规范,只不过rabbitmq遵循的是amqp
  12. // ip port
  13. // 1: 创建连接工程
  14. ConnectionFactory connectionFactory = new ConnectionFactory();
  15. connectionFactory.setHost("128.197.157.151");
  16. connectionFactory.setPort(5672);
  17. connectionFactory.setUsername("chenjinxian");//rabbitmq登录的账号
  18. connectionFactory.setPassword("chenjinxian");//rabbitmq登录的密码
  19. connectionFactory.setVirtualHost("/");
  20. //springboot ---rabbitmq
  21. Connection connection = null;
  22. Channel channel = null;
  23. try {
  24. // 2: 创建连接Connection Rabbitmq为什么是基于channel去处理而不是链接? 长连接----信道channel
  25. connection = connectionFactory.newConnection("生成者");
  26. // 3: 通过连接获取通道Channel
  27. channel = connection.createChannel();
  28. // 4: 通过通创建交换机,声明队列,绑定关系,路由key,发送消息,和接收消息
  29. String queueName = "queue1";
  30. /*
  31. * @params1 队列的名称
  32. * @params2 是否要持久化durable=false 所谓持久化消息是否存盘,如果false 非持久化 true是持久化? 非持久化会存盘吗? 会存盘,但是会随从重启服务会丢失。
  33. * @params3 排他性,是否是独占独立
  34. * @params4 是否自动删除,随着最后一个消费者消息完毕消息以后是否把队列自动删除
  35. * @params5 携带附属参数
  36. */
  37. channel.queueDeclare(queueName, true, false, false, null);
  38. // 5: 准备消息内容
  39. String message = "Hello chenjinxian!!!";
  40. // 6: 发送消息给队列queue
  41. // @params1: 交换机 @params2 队列、路由key @params 消息的状态控制 @params4 消息主题
  42. // 面试题:可以存在没有交换机的队列吗?不可能,虽然没有指定交换机但是一定会存在一个默认的交换机。
  43. channel.basicPublish("", queueName, null, message.getBytes());
  44. System.out.println("消息发送成功!!!");
  45. } catch (Exception ex) {
  46. ex.printStackTrace();
  47. } finally {
  48. // 7: 关闭通道
  49. if (channel != null && channel.isOpen()) {
  50. try {
  51. channel.close();
  52. } catch (Exception ex) {
  53. ex.printStackTrace();
  54. }
  55. }
  56. // 8: 关闭连接
  57. if (connection != null && connection.isOpen()) {
  58. try {
  59. connection.close();
  60. } catch (Exception ex) {
  61. ex.printStackTrace();
  62. }
  63. }
  64. }
  65. }
  66. }

 

 

消费者

  1. package com.chen.rabbitmq.simple;
  2. import com.rabbitmq.client.*;
  3. import java.io.IOException;
  4. public class Consumer {
  5. public static void main(String[] args) {
  6. // 所有的中间件技术都是基于tcp/ip协议基础之上构建新型的协议规范,只不过rabbitmq遵循的是amqp
  7. // ip port
  8. // 1: 创建连接工程
  9. ConnectionFactory connectionFactory = new ConnectionFactory();
  10. connectionFactory.setHost("128.197.157.151");//服务器IP
  11. connectionFactory.setPort(5672);
  12. connectionFactory.setUsername("chenjinxian");
  13. connectionFactory.setPassword("chenjinxian");
  14. connectionFactory.setVirtualHost("/");
  15. Connection connection = null;
  16. Channel channel = null;
  17. try {
  18. // 2: 创建连接Connection
  19. connection = connectionFactory.newConnection("消费者");
  20. // 3: 通过连接获取通道Channel
  21. channel = connection.createChannel();
  22. // 4: 通过通创建交换机,声明队列,绑定关系,路由key,发送消息,和接收消息
  23. // true = ack 正常的逻辑是没问题 死循环 rabbit 重发策略
  24. // false = nack 消息这在消费消息的时候可能会异常和故障
  25. final Channel channel2 = channel;
  26. channel2.basicConsume("queue1", false, new DeliverCallback() {
  27. public void handle(String consumerTag, Delivery message) throws IOException {
  28. try {
  29. System.out.println("收到消息是" + new String(message.getBody(), "UTF-8"));
  30. channel2.basicAck(message.getEnvelope().getDeliveryTag(),false);
  31. }catch (Exception ex){
  32. ex.printStackTrace();
  33. // 三次确认 -- reject + sixin
  34. }
  35. }
  36. }, new CancelCallback() {
  37. public void handle(String consumerTag) throws IOException {
  38. System.out.println("接受失败了...");
  39. }
  40. });
  41. System.out.println("开始接受消息");
  42. System.in.read();
  43. } catch (Exception ex) {
  44. ex.printStackTrace();
  45. } finally {
  46. // 7: 关闭通道
  47. if (channel != null && channel.isOpen()) {
  48. try {
  49. channel.close();
  50. } catch (Exception ex) {
  51. ex.printStackTrace();
  52. }
  53. }
  54. // 8: 关闭连接
  55. if (connection != null && connection.isOpen()) {
  56. try {
  57. connection.close();
  58. } catch (Exception ex) {
  59. ex.printStackTrace();
  60. }
  61. }
  62. }
  63. }
  64. }

 

2. 什么是AMQP

01 什么是AMQP

AMQP全称:Advanced Message Queuing Protocol(高级消息队列协议)。是应用层协议的一个开发标准,为面向消息的中间件设计

02 AMQP生产者流转过程

 

03 AMQP消费者流转过程

 

3. RabbitMQ的核心组成部分

01 RabbitMQ的核心组成部分

 

核心概念: 核心概念:
Server :又称Broker ,接受客户端的连接,实现AMQP实体服务。安装rabbitmq-serverConnection:连接,应用程序与Broker的网络连接TCP/IP/三次握手和四次挥手 服务器:又称Broker,接受客户端的连接,实现AMQP实体服务。安装Rabbitmq-serverConnection:连接,应用程序与Broker的网络连接tcp/ip/三次握手和四次挥手
Channel:网络信道,几乎所有的操作都在Channel中进行,Channel是进 息读写的通道,客户端可以建立对恪Channel,每个Channel代表一个会话任务。 频道:网络信道,几乎所有的操作都在频道中进行频道,是进息读写的通道,客户端可以建立对恪频道频道,每个频道代表一个会话任务频道。
Message :消息:服务与应用程序之间传送的数据,由Properties和body组成,Properties可是对消息进行修饰,如消息的优先级,延迟等高级特性,Body则就是消息体的内容。 消息:消息:服务与应用程序之间传送的数据,由Properties和Body组成,Properties可是对消息进行修饰,如消息的优先级,延迟等高级特性,Body则就是消息体的内容。
Virtual Host虚拟地址,用于进行逻辑隔离,最上层的消息路由,一个虚拟主机理由可以有若干个Exhange和Queueu,同一个虚拟主机里面不能有相同名字的Exchange 虚拟主机虚拟地址,用于进行逻辑隔离,最上层的消息路由,一个虚拟主机理由可以有若干个Exhange和Queueu,同一个虚拟主机里面不能有相同名字的Exchange
Exchange :交换机,接受消息,根据路由键发送消息到绑定的队列。(==不具备消息存储的能力==)Bindings : Exchange和Queue之间的虚拟连接,binding中可以保护多个routing key. 交换:交换机,接受消息,根据路由键发送消息到绑定的队列.(=不具备消息存储的能力==)绑定:Exchange和Queue之间的虚拟连接,Binding中可以保护多个路由密钥。
Routing key :是一个路由规则,虚拟机可以用它来确定如何路由一个特疋消恳.bttos:/bloq.csdn.net/qg _4485823(Queue:队列:也成为Message Queue,消息队列,保存消息并将它们转发给消费苦。"gwa" 路由密钥:是一个路由规则,虚拟机可以用它来确定如何路由一个特征消息(队列:队列:也成为消息队列,消息队列,保存消息并将它们转发给消费者.

 

02 RabbitMQ整体架构是什么样子的?

03 RabbitMQ的运行流程

 

 

4. RabbitMQ入门案例 - fanout 模式

01 RabbitMQ的模式之发布订阅模式

 

发布订阅模式的具体实现

  1. 类型:fanout

  2. 特点:Fanout - 发布与订阅模式,是一种广播机制,它是没有路由 key的模式

(注意这里已经在可视化界面让队列绑定了交换机)

 

生产者

  1. package com.chen.rabbitmq.fanout;
  2. import com.rabbitmq.client.Channel;
  3. import com.rabbitmq.client.Connection;
  4. import com.rabbitmq.client.ConnectionFactory;
  5. /**
  6. 发布订阅模式的具体实现
  7. 类型:fanout
  8. 特点:Fanout - 发布与订阅模式,是一种广播机制,它是没有路由 key的模式
  9. */
  10. public class Producer {
  11. public static void main(String[] args) {
  12. // 1: 创建连接工厂
  13. ConnectionFactory connectionFactory = new ConnectionFactory();
  14. // 2: 设置连接属性
  15. connectionFactory.setHost("128.156.157.161");
  16. connectionFactory.setPort(5672);
  17. connectionFactory.setVirtualHost("/");
  18. connectionFactory.setUsername("chenjinxian");
  19. connectionFactory.setPassword("chenjinxian");
  20. Connection connection = null;
  21. Channel channel = null;
  22. try {
  23. // 3: 从连接工厂中获取连接
  24. connection = connectionFactory.newConnection("生产者");
  25. // 4: 从连接中获取通道channel
  26. channel = connection.createChannel();
  27. // 5: 准备发送消息的内容
  28. String message = "hello xuexi!!!";
  29. // 6:准备交换机
  30. String exchangeName = "fanout_change";
  31. // 8: 指定交换机的类型
  32. String type = "fanout";
  33. // 7: 发送消息给中间件rabbitmq-server
  34. // @params1: 交换机exchange
  35. // @params2: 队列名称/routingkey
  36. // @params3: 属性配置
  37. // @params4: 发送消息的内容
  38. // #.course.* queue3
  39. // *.order.# queue2 ta
  40. // com.order.course.xxx collecion
  41. channel.basicPublish(exchangeName,"", null, message.getBytes());
  42. System.out.println("消息发送成功!");
  43. } catch (Exception ex) {
  44. ex.printStackTrace();
  45. System.out.println("发送消息出现异常...");
  46. } finally {
  47. // 7: 释放连接关闭通道
  48. if (channel != null && channel.isOpen()) {
  49. try {
  50. channel.close();
  51. } catch (Exception ex) {
  52. ex.printStackTrace();
  53. }
  54. }
  55. if (connection != null) {
  56. try {
  57. connection.close();
  58. } catch (Exception ex) {
  59. ex.printStackTrace();
  60. }
  61. }
  62. }
  63. }
  64. }

消费者

  1. package com.chen.rabbitmq.fanout;
  2. import com.rabbitmq.client.*;
  3. import java.io.IOException;
  4. /**
  5. 发布订阅模式的具体实现
  6. 类型:fanout
  7. 特点:Fanout - 发布与订阅模式,是一种广播机制,它是没有路由 key的模式
  8. */
  9. public class Consumer {
  10. private static Runnable runnable = new Runnable() {
  11. public void run() {
  12. // 1: 创建连接工厂
  13. ConnectionFactory connectionFactory = new ConnectionFactory();
  14. // 2: 设置连接属性
  15. connectionFactory.setHost("128.156.157.151");
  16. connectionFactory.setPort(5672);
  17. connectionFactory.setVirtualHost("/");
  18. connectionFactory.setUsername("chenjinxian");
  19. connectionFactory.setPassword("chenjinxian");
  20. //获取队列的名称
  21. final String queueName = Thread.currentThread().getName();
  22. Connection connection = null;
  23. Channel channel = null;
  24. try {
  25. // 3: 从连接工厂中获取连接
  26. connection = connectionFactory.newConnection("生产者");
  27. // 4: 从连接中获取通道channel
  28. channel = connection.createChannel();
  29. // 5: 申明队列queue存储消息
  30. /*
  31. * 如果队列不存在,则会创建
  32. * Rabbitmq不允许创建两个相同的队列名称,否则会报错。
  33. *
  34. * @params1: queue 队列的名称
  35. * @params2: durable 队列是否持久化
  36. * @params3: exclusive 是否排他,即是否私有的,如果为true,会对当前队列加锁,其他的通道不能访问,并且连接自动关闭
  37. * @params4: autoDelete 是否自动删除,当最后一个消费者断开连接之后是否自动删除消息。
  38. * @params5: arguments 可以设置队列附加参数,设置队列的有效期,消息的最大长度,队列的消息生命周期等等。
  39. * */
  40. // 这里如果queue已经被创建过一次了,可以不需要定义
  41. //channel.queueDeclare("queue1", false, false, false, null);
  42. // 6: 定义接受消息的回调
  43. Channel finalChannel = channel;
  44. finalChannel.basicConsume(queueName, true, new DeliverCallback() {
  45. @Override
  46. public void handle(String s, Delivery delivery) throws IOException {
  47. System.out.println(queueName + ":收到消息是:" + new String(delivery.getBody(), "UTF-8"));
  48. }
  49. }, new CancelCallback() {
  50. @Override
  51. public void handle(String s) throws IOException {
  52. }
  53. });
  54. System.out.println(queueName + ":开始接受消息");
  55. System.in.read();
  56. } catch (Exception ex) {
  57. ex.printStackTrace();
  58. System.out.println("发送消息出现异常...");
  59. } finally {
  60. // 7: 释放连接关闭通道
  61. if (channel != null && channel.isOpen()) {
  62. try {
  63. channel.close();
  64. } catch (Exception ex) {
  65. ex.printStackTrace();
  66. }
  67. }
  68. if (connection != null && connection.isOpen()) {
  69. try {
  70. connection.close();
  71. } catch (Exception ex) {
  72. ex.printStackTrace();
  73. }
  74. }
  75. }
  76. }
  77. };
  78. public static void main(String[] args) {
  79. // 启动三个线程去执行
  80. new Thread(runnable, "queue1").start();
  81. new Thread(runnable, "queue2").start();
  82. new Thread(runnable, "queue3").start();
  83. new Thread(runnable, "queue4").start();
  84. //new Thread(runnable, "queue5").start();
  85. }
  86. }

 

 

5. RabbitMQ入门案例 - Direct 模式

 (注意这里已经在可视化界面让队列绑定了交换机)

生产者

  1. package com.chen.rabbitmq.routing;
  2. import com.rabbitmq.client.Channel;
  3. import com.rabbitmq.client.Connection;
  4. import com.rabbitmq.client.ConnectionFactory;
  5. /**
  6. Direct 模式
  7. */
  8. public class Producer {
  9. public static void main(String[] args) {
  10. // 1: 创建连接工厂
  11. ConnectionFactory connectionFactory = new ConnectionFactory();
  12. // 2: 设置连接属性
  13. connectionFactory.setHost("128.176.157.151");
  14. connectionFactory.setPort(5672);
  15. connectionFactory.setVirtualHost("/");
  16. connectionFactory.setUsername("chenjinxian");
  17. connectionFactory.setPassword("chenjinxian");
  18. Connection connection = null;
  19. Channel channel = null;
  20. try {
  21. // 3: 从连接工厂中获取连接
  22. connection = connectionFactory.newConnection("生产者");
  23. // 4: 从连接中获取通道channel
  24. channel = connection.createChannel();
  25. // 5: 准备发送消息的内容
  26. String message = "hello direct_exchange!!!";
  27. // 6:准备交换机
  28. String exchangeName = "direct_exchange";
  29. // 7: 定义路由key
  30. String routeKey = "email";
  31. // 8: 指定交换机的类型
  32. String type = "direct";
  33. // 7: 发送消息给中间件rabbitmq-server
  34. // @params1: 交换机exchange
  35. // @params2: 队列名称/routingkey
  36. // @params3: 属性配置
  37. // @params4: 发送消息的内容
  38. // #.course.* queue3
  39. // *.order.# queue2 ta
  40. // com.order.course.xxx collecion
  41. channel.basicPublish(exchangeName, routeKey, null, message.getBytes());
  42. System.out.println("消息发送成功!");
  43. } catch (Exception ex) {
  44. ex.printStackTrace();
  45. System.out.println("发送消息出现异常...");
  46. } finally {
  47. // 7: 释放连接关闭通道
  48. if (channel != null && channel.isOpen()) {
  49. try {
  50. channel.close();
  51. } catch (Exception ex) {
  52. ex.printStackTrace();
  53. }
  54. }
  55. if (connection != null) {
  56. try {
  57. connection.close();
  58. } catch (Exception ex) {
  59. ex.printStackTrace();
  60. }
  61. }
  62. }
  63. }
  64. }

消费者

  1. package com.chen.rabbitmq.routing;
  2. import com.rabbitmq.client.*;
  3. import java.io.IOException;
  4. /**
  5. Direct 模式
  6. */
  7. public class Consumer {
  8. private static Runnable runnable = new Runnable() {
  9. public void run() {
  10. // 1: 创建连接工厂
  11. ConnectionFactory connectionFactory = new ConnectionFactory();
  12. // 2: 设置连接属性
  13. connectionFactory.setHost("123.156.147.151");
  14. connectionFactory.setPort(5672);
  15. connectionFactory.setVirtualHost("/");
  16. connectionFactory.setUsername("chenjinxian");
  17. connectionFactory.setPassword("chenjinxian");
  18. //获取队列的名称
  19. final String queueName = Thread.currentThread().getName();
  20. Connection connection = null;
  21. Channel channel = null;
  22. try {
  23. // 3: 从连接工厂中获取连接
  24. connection = connectionFactory.newConnection("生产者");
  25. // 4: 从连接中获取通道channel
  26. channel = connection.createChannel();
  27. // 5: 申明队列queue存储消息
  28. /*
  29. * 如果队列不存在,则会创建
  30. * Rabbitmq不允许创建两个相同的队列名称,否则会报错。
  31. *
  32. * @params1: queue 队列的名称
  33. * @params2: durable 队列是否持久化
  34. * @params3: exclusive 是否排他,即是否私有的,如果为true,会对当前队列加锁,其他的通道不能访问,并且连接自动关闭
  35. * @params4: autoDelete 是否自动删除,当最后一个消费者断开连接之后是否自动删除消息。
  36. * @params5: arguments 可以设置队列附加参数,设置队列的有效期,消息的最大长度,队列的消息生命周期等等。
  37. * */
  38. // 这里如果queue已经被创建过一次了,可以不需要定义
  39. //channel.queueDeclare("queue1", false, false, false, null);
  40. // 6: 定义接受消息的回调
  41. Channel finalChannel = channel;
  42. finalChannel.basicConsume(queueName, true, new DeliverCallback() {
  43. @Override
  44. public void handle(String s, Delivery delivery) throws IOException {
  45. System.out.println(queueName + ":收到消息是:" + new String(delivery.getBody(), "UTF-8"));
  46. }
  47. }, new CancelCallback() {
  48. @Override
  49. public void handle(String s) throws IOException {
  50. }
  51. });
  52. System.out.println(queueName + ":开始接受消息");
  53. System.in.read();
  54. } catch (Exception ex) {
  55. ex.printStackTrace();
  56. System.out.println("发送消息出现异常...");
  57. } finally {
  58. // 7: 释放连接关闭通道
  59. if (channel != null && channel.isOpen()) {
  60. try {
  61. channel.close();
  62. } catch (Exception ex) {
  63. ex.printStackTrace();
  64. }
  65. }
  66. if (connection != null && connection.isOpen()) {
  67. try {
  68. connection.close();
  69. } catch (Exception ex) {
  70. ex.printStackTrace();
  71. }
  72. }
  73. }
  74. }
  75. };
  76. public static void main(String[] args) {
  77. // 启动三个线程去执行
  78. new Thread(runnable, "queue1").start();
  79. new Thread(runnable, "queue2").start();
  80. new Thread(runnable, "queue3").start();
  81. new Thread(runnable, "queue4").start();
  82. // new Thread(runnable, "queue5").start();
  83. }
  84. }

6. RabbitMQ入门案例 - Topic 模式

  (注意这里已经在可视化界面让队列绑定了交换机)

 生产者

  1. package com.chen.rabbitmq.topics;
  2. import com.rabbitmq.client.Channel;
  3. import com.rabbitmq.client.Connection;
  4. import com.rabbitmq.client.ConnectionFactory;
  5. /**
  6. Topic模式
  7. */
  8. public class Producer {
  9. public static void main(String[] args) {
  10. // 1: 创建连接工厂
  11. ConnectionFactory connectionFactory = new ConnectionFactory();
  12. // 2: 设置连接属性
  13. connectionFactory.setHost("125.156.157.151");
  14. connectionFactory.setPort(5672);
  15. connectionFactory.setVirtualHost("/");
  16. connectionFactory.setUsername("chenjinxian");
  17. connectionFactory.setPassword("chenjinxian");
  18. Connection connection = null;
  19. Channel channel = null;
  20. try {
  21. // 3: 从连接工厂中获取连接
  22. connection = connectionFactory.newConnection("生产者");
  23. // 4: 从连接中获取通道channel
  24. channel = connection.createChannel();
  25. // 5: 准备发送消息的内容
  26. String message = "hello topic_exchange!!!";
  27. // 6:准备交换机
  28. String exchangeName = "topic_exchange";
  29. // 7: 定义路由key
  30. String routeKey = "com.order.user";
  31. // 8: 指定交换机的类型
  32. String type = "topic";
  33. // 7: 发送消息给中间件rabbitmq-server
  34. // @params1: 交换机exchange
  35. // @params2: 队列名称/routingkey
  36. // @params3: 属性配置
  37. // @params4: 发送消息的内容
  38. // #.course.* queue3
  39. // *.order.# queue2 ta
  40. // com.order.course.xxx collecion
  41. channel.basicPublish(exchangeName, routeKey, null, message.getBytes());
  42. System.out.println("消息发送成功!");
  43. } catch (Exception ex) {
  44. ex.printStackTrace();
  45. System.out.println("发送消息出现异常...");
  46. } finally {
  47. // 7: 释放连接关闭通道
  48. if (channel != null && channel.isOpen()) {
  49. try {
  50. channel.close();
  51. } catch (Exception ex) {
  52. ex.printStackTrace();
  53. }
  54. }
  55. if (connection != null) {
  56. try {
  57. connection.close();
  58. } catch (Exception ex) {
  59. ex.printStackTrace();
  60. }
  61. }
  62. }
  63. }
  64. }

 消费者不变

完整案例(创建交换机,创建队列,交换机与队列绑定)

  1. package com.chen.rabbitmq.all;
  2. import com.rabbitmq.client.Channel;
  3. import com.rabbitmq.client.Connection;
  4. import com.rabbitmq.client.ConnectionFactory;
  5. /**
  6. 完整案例
  7. */
  8. public class Producer {
  9. public static void main(String[] args) {
  10. // 1: 创建连接工厂
  11. ConnectionFactory connectionFactory = new ConnectionFactory();
  12. // 2: 设置连接属性
  13. connectionFactory.setHost("151.156.157.151");
  14. connectionFactory.setPort(5672);
  15. connectionFactory.setVirtualHost("/");
  16. connectionFactory.setUsername("chenjinxian");
  17. connectionFactory.setPassword("chenjinxian");
  18. Connection connection = null;
  19. Channel channel = null;
  20. try {
  21. // 3: 从连接工厂中获取连接
  22. connection = connectionFactory.newConnection("生产者");
  23. // 4: 从连接中获取通道channel
  24. channel = connection.createChannel();
  25. // 6: 准备发送消息的内容
  26. String message = " 你好,小白";
  27. // 交换机
  28. String exchangeName = "direct_message_exchange";
  29. // 交换机的类型 direct/topic/fanout/headers
  30. String exchangeType = "direct";
  31. // 如果你用界面把queueu 和 exchange的关系先绑定话,你代码就不需要在编写这些声明代码可以让代码变得更加简洁,但是不容读懂
  32. // 如果用代码的方式去声明,我们要学习一下
  33. // 7: 声明交换机 所谓的持久化就是指,交换机会不会随着服务器重启造成丢失,如果是true代表不丢失,false重启就会丢失
  34. channel.exchangeDeclare(exchangeName,exchangeType,true);
  35. // 8: 声明队列
  36. channel.queueDeclare("queue5",true,false,false,null);
  37. channel.queueDeclare("queue6",true,false,false,null);
  38. channel.queueDeclare("queue7",true,false,false,null);
  39. // 9:绑定队列和交换机的关系
  40. channel.queueBind("queue5",exchangeName,"order");
  41. channel.queueBind("queue6",exchangeName,"order");
  42. channel.queueBind("queue7",exchangeName,"course");
  43. channel.basicPublish(exchangeName, "course", null, message.getBytes());
  44. System.out.println("消息发送成功!");
  45. } catch (Exception ex) {
  46. ex.printStackTrace();
  47. System.out.println("发送消息出现异常...");
  48. } finally {
  49. // 7: 释放连接关闭通道
  50. if (channel != null && channel.isOpen()) {
  51. try {
  52. channel.close();
  53. } catch (Exception ex) {
  54. ex.printStackTrace();
  55. }
  56. }
  57. if (connection != null) {
  58. try {
  59. connection.close();
  60. } catch (Exception ex) {
  61. ex.printStackTrace();
  62. }
  63. }
  64. }
  65. }
  66. }

 执行完后生成队列和交换机

 

 

7. RabbitMQ入门案例 - Work模式

01 Work模式轮询模式(Round-Robin)

 

当有多个消费者时,我们的消息会被哪个消费者消费呢,我们又该如何均衡消费者消费信息的多少呢?

主要有两种模式:

  1. 轮询模式的分发:一个消费者一条,按均分配

  2. 公平分发:根据消费者的消费能力进行公平分发,处理快的处理的多,处理慢的处理的少;按劳分配

01轮询模式

生产者

  1. package com.chen.rabbitmq.work.lunxun;
  2. import com.rabbitmq.client.Channel;
  3. import com.rabbitmq.client.Connection;
  4. import com.rabbitmq.client.ConnectionFactory;
  5. /**
  6. 轮询模式
  7. */
  8. public class Producer {
  9. public static void main(String[] args) {
  10. // 1: 创建连接工厂
  11. ConnectionFactory connectionFactory = new ConnectionFactory();
  12. // 2: 设置连接属性
  13. connectionFactory.setHost("123.156.147.151");
  14. connectionFactory.setPort(5672);
  15. connectionFactory.setVirtualHost("/");
  16. connectionFactory.setUsername("chenjinxian");
  17. connectionFactory.setPassword("chenjinxian");
  18. Connection connection = null;
  19. Channel channel = null;
  20. try {
  21. // 3: 从连接工厂中获取连接
  22. connection = connectionFactory.newConnection("生产者");
  23. // 4: 从连接中获取通道channel
  24. channel = connection.createChannel();
  25. // 6: 准备发送消息的内容
  26. //===============================end topic模式==================================
  27. for (int i = 1; i <= 20; i++) {
  28. //消息的内容
  29. String msg = "学相伴:" + i;
  30. // 7: 发送消息给中间件rabbitmq-server
  31. // @params1: 交换机exchange
  32. // @params2: 队列名称/routingkey
  33. // @params3: 属性配置
  34. // @params4: 发送消息的内容
  35. channel.basicPublish("", "queue1", null, msg.getBytes());
  36. }
  37. System.out.println("消息发送成功!");
  38. } catch (Exception ex) {
  39. ex.printStackTrace();
  40. System.out.println("发送消息出现异常...");
  41. } finally {
  42. // 7: 释放连接关闭通道
  43. if (channel != null && channel.isOpen()) {
  44. try {
  45. channel.close();
  46. } catch (Exception ex) {
  47. ex.printStackTrace();
  48. }
  49. }
  50. if (connection != null) {
  51. try {
  52. connection.close();
  53. } catch (Exception ex) {
  54. ex.printStackTrace();
  55. }
  56. }
  57. }
  58. }
  59. }

消费者

  1. package com.chen.rabbitmq.work.lunxun;
  2. import com.rabbitmq.client.*;
  3. import java.io.IOException;
  4. /**
  5. 轮询模式
  6. */
  7. public class Work1 {
  8. public static void main(String[] args) {
  9. // 1: 创建连接工厂
  10. ConnectionFactory connectionFactory = new ConnectionFactory();
  11. // 2: 设置连接属性
  12. connectionFactory.setHost("123.156.147.155");
  13. connectionFactory.setPort(5672);
  14. connectionFactory.setVirtualHost("/");
  15. connectionFactory.setUsername("chenjinxian");
  16. connectionFactory.setPassword("chenjinxian");
  17. Connection connection = null;
  18. Channel channel = null;
  19. try {
  20. // 3: 从连接工厂中获取连接
  21. connection = connectionFactory.newConnection("消费者-Work1");
  22. // 4: 从连接中获取通道channel
  23. channel = connection.createChannel();
  24. // 5: 申明队列queue存储消息
  25. /*
  26. * 如果队列不存在,则会创建
  27. * Rabbitmq不允许创建两个相同的队列名称,否则会报错。
  28. *
  29. * @params1: queue 队列的名称
  30. * @params2: durable 队列是否持久化
  31. * @params3: exclusive 是否排他,即是否私有的,如果为true,会对当前队列加锁,其他的通道不能访问,并且连接自动关闭
  32. * @params4: autoDelete 是否自动删除,当最后一个消费者断开连接之后是否自动删除消息。
  33. * @params5: arguments 可以设置队列附加参数,设置队列的有效期,消息的最大长度,队列的消息生命周期等等。
  34. * */
  35. // 这里如果queue已经被创建过一次了,可以不需要定义
  36. // channel.queueDeclare("queue1", false, false, false, null);
  37. // 同一时刻,服务器只会推送一条消息给消费者
  38. // 6: 定义接受消息的回调
  39. Channel finalChannel = channel;
  40. //finalChannel.basicQos(1);
  41. finalChannel.basicConsume("queue1", true, new DeliverCallback() {
  42. @Override
  43. public void handle(String s, Delivery delivery) throws IOException {
  44. try{
  45. System.out.println("Work1-收到消息是:" + new String(delivery.getBody(), "UTF-8"));
  46. Thread.sleep(200);
  47. }catch(Exception ex){
  48. ex.printStackTrace();
  49. }
  50. }
  51. }, new CancelCallback() {
  52. @Override
  53. public void handle(String s) throws IOException {
  54. }
  55. });
  56. System.out.println("Work1-开始接受消息");
  57. System.in.read();
  58. } catch (Exception ex) {
  59. ex.printStackTrace();
  60. System.out.println("发送消息出现异常...");
  61. } finally {
  62. // 7: 释放连接关闭通道
  63. if (channel != null && channel.isOpen()) {
  64. try {
  65. channel.close();
  66. } catch (Exception ex) {
  67. ex.printStackTrace();
  68. }
  69. }
  70. if (connection != null && connection.isOpen()) {
  71. try {
  72. connection.close();
  73. } catch (Exception ex) {
  74. ex.printStackTrace();
  75. }
  76. }
  77. }
  78. }
  79. }
  1. package com.chen.rabbitmq.work.lunxun;
  2. import com.rabbitmq.client.*;
  3. import java.io.IOException;
  4. /**
  5. 轮询模式
  6. */
  7. public class Work2 {
  8. public static void main(String[] args) {
  9. // 1: 创建连接工厂
  10. ConnectionFactory connectionFactory = new ConnectionFactory();
  11. // 2: 设置连接属性
  12. connectionFactory.setHost("123.195.157.151");
  13. connectionFactory.setPort(5672);
  14. connectionFactory.setVirtualHost("/");
  15. connectionFactory.setUsername("chenjinxian");
  16. connectionFactory.setPassword("chenjinxian");
  17. Connection connection = null;
  18. Channel channel = null;
  19. try {
  20. // 3: 从连接工厂中获取连接
  21. connection = connectionFactory.newConnection("消费者-Work2");
  22. // 4: 从连接中获取通道channel
  23. channel = connection.createChannel();
  24. // 5: 申明队列queue存储消息
  25. /*
  26. * 如果队列不存在,则会创建
  27. * Rabbitmq不允许创建两个相同的队列名称,否则会报错。
  28. *
  29. * @params1: queue 队列的名称
  30. * @params2: durable 队列是否持久化
  31. * @params3: exclusive 是否排他,即是否私有的,如果为true,会对当前队列加锁,其他的通道不能访问,并且连接自动关闭
  32. * @params4: autoDelete 是否自动删除,当最后一个消费者断开连接之后是否自动删除消息。
  33. * @params5: arguments 可以设置队列附加参数,设置队列的有效期,消息的最大长度,队列的消息生命周期等等。
  34. * */
  35. // 这里如果queue已经被创建过一次了,可以不需要定义
  36. //channel.queueDeclare("queue1", false, true, false, null);
  37. // 同一时刻,服务器只会推送一条消息给消费者
  38. //channel.basicQos(1);
  39. // 6: 定义接受消息的回调
  40. Channel finalChannel = channel;
  41. //finalChannel.basicQos(1);
  42. finalChannel.basicConsume("queue1", true, new DeliverCallback() {
  43. @Override
  44. public void handle(String s, Delivery delivery) throws IOException {
  45. try{
  46. System.out.println("Work2-收到消息是:" + new String(delivery.getBody(), "UTF-8"));
  47. Thread.sleep(100);
  48. }catch(Exception ex){
  49. ex.printStackTrace();
  50. }
  51. }
  52. }, new CancelCallback() {
  53. @Override
  54. public void handle(String s) throws IOException {
  55. }
  56. });
  57. System.out.println("Work2-开始接受消息");
  58. System.in.read();
  59. } catch (Exception ex) {
  60. ex.printStackTrace();
  61. System.out.println("发送消息出现异常...");
  62. } finally {
  63. // 7: 释放连接关闭通道
  64. if (channel != null && channel.isOpen()) {
  65. try {
  66. channel.close();
  67. } catch (Exception ex) {
  68. ex.printStackTrace();
  69. }
  70. }
  71. if (connection != null && connection.isOpen()) {
  72. try {
  73. connection.close();
  74. } catch (Exception ex) {
  75. ex.printStackTrace();
  76. }
  77. }
  78. }
  79. }
  80. }

 

02 Work模式公平分发模式 

生产者

  1. package com.chen.rabbitmq.work.fair;
  2. import com.rabbitmq.client.Channel;
  3. import com.rabbitmq.client.Connection;
  4. import com.rabbitmq.client.ConnectionFactory;
  5. /**
  6. 公平分发模式
  7. */
  8. public class Producer {
  9. public static void main(String[] args) {
  10. // 1: 创建连接工厂
  11. ConnectionFactory connectionFactory = new ConnectionFactory();
  12. // 2: 设置连接属性
  13. connectionFactory.setHost("125.156.157.151");
  14. connectionFactory.setPort(5672);
  15. connectionFactory.setVirtualHost("/");
  16. connectionFactory.setUsername("chenjinxian");
  17. connectionFactory.setPassword("chenjinxian");
  18. Connection connection = null;
  19. Channel channel = null;
  20. try {
  21. // 3: 从连接工厂中获取连接
  22. connection = connectionFactory.newConnection("生产者");
  23. // 4: 从连接中获取通道channel
  24. channel = connection.createChannel();
  25. // 6: 准备发送消息的内容
  26. //===============================end topic模式==================================
  27. for (int i = 1; i <= 20; i++) {
  28. //消息的内容
  29. String msg = "学相伴:" + i;
  30. // 7: 发送消息给中间件rabbitmq-server
  31. // @params1: 交换机exchange
  32. // @params2: 队列名称/routingkey
  33. // @params3: 属性配置
  34. // @params4: 发送消息的内容
  35. channel.basicPublish("", "queue1", null, msg.getBytes());
  36. }
  37. System.out.println("消息发送成功!");
  38. } catch (Exception ex) {
  39. ex.printStackTrace();
  40. System.out.println("发送消息出现异常...");
  41. } finally {
  42. // 7: 释放连接关闭通道
  43. if (channel != null && channel.isOpen()) {
  44. try {
  45. channel.close();
  46. } catch (Exception ex) {
  47. ex.printStackTrace();
  48. }
  49. }
  50. if (connection != null) {
  51. try {
  52. connection.close();
  53. } catch (Exception ex) {
  54. ex.printStackTrace();
  55. }
  56. }
  57. }
  58. }
  59. }

消费者

  1. package com.chen.rabbitmq.work.fair;
  2. import com.rabbitmq.client.*;
  3. import java.io.IOException;
  4. /**
  5. 公平分发模式
  6. */
  7. public class Work1 {
  8. public static void main(String[] args) {
  9. // 1: 创建连接工厂
  10. ConnectionFactory connectionFactory = new ConnectionFactory();
  11. // 2: 设置连接属性
  12. connectionFactory.setHost("123.156.146.151");
  13. connectionFactory.setPort(5672);
  14. connectionFactory.setVirtualHost("/");
  15. connectionFactory.setUsername("chenjinxian");
  16. connectionFactory.setPassword("chenjinxian");
  17. Connection connection = null;
  18. Channel channel = null;
  19. try {
  20. // 3: 从连接工厂中获取连接
  21. connection = connectionFactory.newConnection("消费者-Work1");
  22. // 4: 从连接中获取通道channel
  23. channel = connection.createChannel();
  24. // 5: 申明队列queue存储消息
  25. /*
  26. * 如果队列不存在,则会创建
  27. * Rabbitmq不允许创建两个相同的队列名称,否则会报错。
  28. *
  29. * @params1: queue 队列的名称
  30. * @params2: durable 队列是否持久化
  31. * @params3: exclusive 是否排他,即是否私有的,如果为true,会对当前队列加锁,其他的通道不能访问,并且连接自动关闭
  32. * @params4: autoDelete 是否自动删除,当最后一个消费者断开连接之后是否自动删除消息。
  33. * @params5: arguments 可以设置队列附加参数,设置队列的有效期,消息的最大长度,队列的消息生命周期等等。
  34. * */
  35. // 这里如果queue已经被创建过一次了,可以不需要定义
  36. // channel.queueDeclare("queue1", false, false, false, null);
  37. // 同一时刻,服务器只会推送一条消息给消费者
  38. // 6: 定义接受消息的回调
  39. final Channel finalChannel = channel;
  40. finalChannel.basicQos(1);
  41. finalChannel.basicConsume("queue1", false, new DeliverCallback() {
  42. @Override
  43. public void handle(String s, Delivery delivery) throws IOException {
  44. try{
  45. System.out.println("Work1-收到消息是:" + new String(delivery.getBody(), "UTF-8"));
  46. Thread.sleep(1000);
  47. // 改成手动应答
  48. finalChannel.basicAck(delivery.getEnvelope().getDeliveryTag(),false);
  49. }catch(Exception ex){
  50. ex.printStackTrace();
  51. }
  52. }
  53. }, new CancelCallback() {
  54. @Override
  55. public void handle(String s) throws IOException {
  56. }
  57. });
  58. System.out.println("Work1-开始接受消息");
  59. System.in.read();
  60. } catch (Exception ex) {
  61. ex.printStackTrace();
  62. System.out.println("发送消息出现异常...");
  63. } finally {
  64. // 7: 释放连接关闭通道
  65. if (channel != null && channel.isOpen()) {
  66. try {
  67. channel.close();
  68. } catch (Exception ex) {
  69. ex.printStackTrace();
  70. }
  71. }
  72. if (connection != null && connection.isOpen()) {
  73. try {
  74. connection.close();
  75. } catch (Exception ex) {
  76. ex.printStackTrace();
  77. }
  78. }
  79. }
  80. }
  81. }
  1. package com.chen.rabbitmq.work.fair;
  2. import com.rabbitmq.client.*;
  3. import java.io.IOException;
  4. /**
  5. 公平分发模式
  6. */
  7. public class Work2 {
  8. public static void main(String[] args) {
  9. // 1: 创建连接工厂
  10. ConnectionFactory connectionFactory = new ConnectionFactory();
  11. // 2: 设置连接属性
  12. connectionFactory.setHost("121.156.157.131");
  13. connectionFactory.setPort(5672);
  14. connectionFactory.setVirtualHost("/");
  15. connectionFactory.setUsername("chenjinxian");
  16. connectionFactory.setPassword("chenjinxian");
  17. Connection connection = null;
  18. Channel channel = null;
  19. try {
  20. // 3: 从连接工厂中获取连接
  21. connection = connectionFactory.newConnection("消费者-Work2");
  22. // 4: 从连接中获取通道channel
  23. channel = connection.createChannel();
  24. // 5: 申明队列queue存储消息
  25. /*
  26. * 如果队列不存在,则会创建
  27. * Rabbitmq不允许创建两个相同的队列名称,否则会报错。
  28. *
  29. * @params1: queue 队列的名称
  30. * @params2: durable 队列是否持久化
  31. * @params3: exclusive 是否排他,即是否私有的,如果为true,会对当前队列加锁,其他的通道不能访问,并且连接自动关闭
  32. * @params4: autoDelete 是否自动删除,当最后一个消费者断开连接之后是否自动删除消息。
  33. * @params5: arguments 可以设置队列附加参数,设置队列的有效期,消息的最大长度,队列的消息生命周期等等。
  34. * */
  35. // 这里如果queue已经被创建过一次了,可以不需要定义
  36. //channel.queueDeclare("queue1", false, true, false, null);
  37. // 同一时刻,服务器只会推送一条消息给消费者
  38. //channel.basicQos(1);
  39. // 6: 定义接受消息的回调
  40. final Channel finalChannel = channel;
  41. finalChannel.basicQos(1);
  42. finalChannel.basicConsume("queue1", false, new DeliverCallback() {
  43. @Override
  44. public void handle(String s, Delivery delivery) throws IOException {
  45. try{
  46. System.out.println("Work2-收到消息是:" + new String(delivery.getBody(), "UTF-8"));
  47. Thread.sleep(200);
  48. // 一定使用我们的手动应答
  49. finalChannel.basicAck(delivery.getEnvelope().getDeliveryTag(),false);
  50. }catch(Exception ex){
  51. ex.printStackTrace();
  52. }
  53. }
  54. }, new CancelCallback() {
  55. @Override
  56. public void handle(String s) throws IOException {
  57. }
  58. });
  59. System.out.println("Work2-开始接受消息");
  60. System.in.read();
  61. } catch (Exception ex) {
  62. ex.printStackTrace();
  63. System.out.println("发送消息出现异常...");
  64. } finally {
  65. // 7: 释放连接关闭通道
  66. if (channel != null && channel.isOpen()) {
  67. try {
  68. channel.close();
  69. } catch (Exception ex) {
  70. ex.printStackTrace();
  71. }
  72. }
  73. if (connection != null && connection.isOpen()) {
  74. try {
  75. connection.close();
  76. } catch (Exception ex) {
  77. ex.printStackTrace();
  78. }
  79. }
  80. }
  81. }
  82. }

8. RabbitMQ使用场景

 

01 解耦、削峰、异步

 

同步异步的问题(串行)

串行方式:将订单信息写入数据库成功后,发送注册邮件,再发送注册短信。以上三个任务全部完成后,返回给客户端

 

并行方式 异步线程池

并行方式:将订单信息写入数据库成功后,发送注册邮件的同时,发送注册短信。以上三个任务完成后,返回给客户端。与串行的差别是,并行的方式可以提高处理的时间

存在问题

  1. 耦合度高

  2. 需要自己写线程池自己维护成本太高

  3. 出现了消息可能会丢失,需要你自己做消息补偿

  4. 如何保证消息的可靠性你自己写

  5. 如果服务器承载不了,你需要自己去写高可用

异步消息队列的方式

 

 

好处:

  1. 完全解耦,用 MQ建立桥接

  2. 有独立的线程池和运行模型

  3. 出现了消息可能会丢失,MQ有持久化功能

  4. 如何保证消息的可靠性,死信队列和消息转移等

  5. 如果服务器承载不了,你需要自己去写高可用,HA镜像模型高可用

按照以上约定,用户的响应时间相当于是订单信息写入数据库的时间,也就是50毫秒。注册邮件,发送短信写入消息队列后,直接返回,因此写入消息队列的速度很快,基本可以忽略,因此用户的响应时间可能是50毫秒。因此架构改变后,系统的吞吐量提高到每秒20QPS。比串行提高了3倍,比并行提高了两倍

 

02 高内聚,低耦合

 

 

好处:

  1. 完全解耦,用 MQ建立桥接

  2. 有独立的线程池和运行模型

  3. 出现了消息可能会丢失,MQ有持久化功能

  4. 如何保证消息的可靠性,死信队列和消息转移等

  5. 如果服务器承载不了,你需要自己去写高可用,HA镜像模型高可用

按照以上约定,用户的响应时间相当于是订单信息写入数据库的时间,也就是50毫秒。注册邮件,发送短信写入消息队列后,直接返回,因此写入消息队列的速度很快,基本可以忽略,因此用户的响应时间可能是50毫秒。因此架构改变后,系统的吞吐量提高到每秒20QPS。比串行提高了3倍,比并行提高了两倍

四、Springboot案例

1. Fanout 模式

生产者

导入依赖

  1. <!--rabbitmq starter 依赖-->
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-amqp</artifactId>
  5. </dependency>

application.yml

  1. # 服务端口
  2. server:
  3. port: 8080
  4. # 配置rabbitmq服务
  5. spring:
  6. rabbitmq:
  7. username: admin
  8. password: admin
  9. virtual-host: /
  10. host: 127.0.0.1
  11. port: 5672

目录结构 

 创建配置类RabbitMqConfiguration.java

  1. package com.chen.springbootorderrabbitmqproducer.config;
  2. import org.springframework.amqp.core.Binding;
  3. import org.springframework.amqp.core.BindingBuilder;
  4. import org.springframework.amqp.core.FanoutExchange;
  5. import org.springframework.amqp.core.Queue;
  6. import org.springframework.context.annotation.Bean;
  7. import org.springframework.context.annotation.Configuration;
  8. /**
  9. */
  10. @Configuration
  11. public class RabbitMqConfiguration {
  12. // 1: 声明交换机
  13. @Bean
  14. public FanoutExchange fanoutExchange() {
  15. return new FanoutExchange("fanout_order_ex", true, false);
  16. }
  17. // 2: 声明队列 duanxin.fanout.queue
  18. @Bean
  19. public Queue duanxinqueue() {
  20. return new Queue("duanxin.fanout.queue", true);
  21. }
  22. // 2: 声明队列 duanxin.fanout.queue
  23. @Bean
  24. public Queue smsqueue() {
  25. return new Queue("sms.fanout.queue", true);
  26. }
  27. // 2: 声明队列 duanxin.fanout.queue
  28. @Bean
  29. public Queue emailqueue() {
  30. return new Queue("email.fanout.queue", true);
  31. }
  32. // 3: 确定绑定关系
  33. @Bean
  34. public Binding bindduanxin(){
  35. return BindingBuilder.bind(duanxinqueue()).to(fanoutExchange());
  36. }
  37. // 3: 确定绑定关系
  38. @Bean
  39. public Binding bindsms(){
  40. return BindingBuilder.bind(smsqueue()).to(fanoutExchange());
  41. }
  42. // 3: 确定绑定关系
  43. @Bean
  44. public Binding bindemail(){
  45. return BindingBuilder.bind(emailqueue()).to(fanoutExchange());
  46. }
  47. }

 编写实现类OrderService.java

  1. package com.chen.springbootorderrabbitmqproducer.service;
  2. import org.springframework.amqp.AmqpException;
  3. import org.springframework.amqp.core.Message;
  4. import org.springframework.amqp.core.MessagePostProcessor;
  5. import org.springframework.amqp.rabbit.core.RabbitTemplate;
  6. import org.springframework.beans.factory.annotation.Autowired;
  7. import org.springframework.stereotype.Service;
  8. import java.util.UUID;
  9. /**
  10. */
  11. @Service
  12. public class OrderService {
  13. @Autowired
  14. private RabbitTemplate rabbitTemplate;
  15. // 交换机
  16. private String exchangeName = "fanout_order_ex";
  17. // 路由key
  18. private String routingKey = "";
  19. /**
  20. * @Author xuke
  21. * @Description 模拟用户购买商品下单的业务
  22. * @Date 22:26 2021/3/5
  23. * @Param [userId, productId, num]
  24. * @return void
  25. **/
  26. public void makeOrder(String userId,String productId,int num){
  27. // 1: 根据商品id查询库存是否充足
  28. // 2: 保存订单
  29. String orderId = UUID.randomUUID().toString();
  30. System.out.println("保存订单成功:id是:" + orderId);
  31. // 3: 发送消息
  32. rabbitTemplate.convertAndSend(exchangeName,routingKey,orderId);
  33. }
  34. }

编写测试类

  1. package com.chen.springbootorderrabbitmqproducer.rabbitmq.springbootorderrabbitmqproducer;
  2. import com.chen.springbootorderrabbitmqproducer.service.OrderService;
  3. import org.junit.jupiter.api.Test;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.boot.test.context.SpringBootTest;
  6. @SpringBootTest
  7. class SpringbootOrderRabbitmqProducerApplicationTests {
  8. @Autowired
  9. private OrderService orderService;
  10. @Test
  11. public void contextLoads() {
  12. orderService.makeOrder("100","100",10);
  13. }
  14. @Test
  15. public void testDirect() {
  16. orderService.makeOrderDirect("100","100",10);
  17. }
  18. @Test
  19. public void testDirectTTl() {
  20. orderService.makeOrderDirectTtl("100","100",10);
  21. }
  22. // @Test
  23. // public void testTopic() {
  24. // orderService.makeOrderTopic("100","100",10);
  25. // }
  26. }

消费者

application.yml

  1. # 服务端口
  2. server:
  3. port: 8080
  4. # 配置rabbitmq服务
  5. spring:
  6. rabbitmq:
  7. username: admin
  8. password: admin
  9. virtual-host: /
  10. host: 127.0.0.1
  11. port: 5672

接受消息

  1. package com.chen.direct;
  2. import org.springframework.amqp.rabbit.annotation.RabbitHandler;
  3. import org.springframework.amqp.rabbit.annotation.RabbitListener;
  4. import org.springframework.stereotype.Service;
  5. /**
  6. */
  7. @Service
  8. @RabbitListener(queues ={"duanxin.direct.queue"})
  9. public class DirectDuanxinConsumber {
  10. // 告诉你的接收服务器的消息,没有返回值
  11. @RabbitHandler
  12. public void reviceMessage(String message){
  13. System.out.println("duanxin--direct--->接收到订单消息,订单id是: " + message);
  14. }
  15. }
  1. package com.chen.direct;
  2. import org.springframework.amqp.rabbit.annotation.RabbitHandler;
  3. import org.springframework.amqp.rabbit.annotation.RabbitListener;
  4. import org.springframework.stereotype.Service;
  5. /**
  6. */
  7. @Service
  8. @RabbitListener(queues ={"email.direct.queue"})
  9. public class DirectEmailConsumber {
  10. @RabbitHandler
  11. public void reviceMessage(String message){
  12. System.out.println("email---direct-->接收到订单消息,订单id是: " + message);
  13. }
  14. }
  1. package com.chen.direct;
  2. import org.springframework.amqp.rabbit.annotation.RabbitHandler;
  3. import org.springframework.amqp.rabbit.annotation.RabbitListener;
  4. import org.springframework.stereotype.Service;
  5. /**
  6. */
  7. @Service
  8. @RabbitListener(queues ={"sms.direct.queue"})
  9. public class DirectSmsConsumber {
  10. @RabbitHandler
  11. public void reviceMessage(String message){
  12. System.out.println("sms--direct--->接收到订单消息,订单id是: " + message);
  13. }
  14. }

2. Direct 模式

生产者

配置类

  1. package com.chen.springbootorderrabbitmqproducer.config;
  2. import org.springframework.amqp.core.*;
  3. import org.springframework.context.annotation.Bean;
  4. import org.springframework.context.annotation.Configuration;
  5. import java.util.HashMap;
  6. import java.util.Map;
  7. /**
  8. */
  9. @Configuration
  10. public class RabbitMqConfiguration2 {
  11. // 1: 声明交换机
  12. @Bean
  13. public DirectExchange directExchange() {
  14. return new DirectExchange("direct_order_ex", true, false);
  15. }
  16. // 2: 声明队列 duanxin.direct.queue
  17. @Bean
  18. public Queue duanxinqueue() {
  19. return new Queue("duanxin.direct.queue", true);
  20. }
  21. // 2: 声明队列 duanxin.direct.queue
  22. @Bean
  23. public Queue smsqueue() {
  24. return new Queue("sms.direct.queue", true);
  25. }
  26. // 2: 声明队列 duanxin.direct.queue
  27. @Bean
  28. public Queue emailqueue() {
  29. return new Queue("email.direct.queue", true);
  30. }
  31. // 3: 确定绑定关系
  32. @Bean
  33. public Binding bindduanxin(){
  34. return BindingBuilder.bind(duanxinqueue()).to(directExchange()).with("msg");
  35. }
  36. // 3: 确定绑定关系
  37. @Bean
  38. public Binding bindsms(){
  39. return BindingBuilder.bind(smsqueue()).to(directExchange()).with("sms");
  40. }
  41. // 3: 确定绑定关系
  42. @Bean
  43. public Binding bindemail(){
  44. return BindingBuilder.bind(emailqueue()).to(directExchange()).with("email");
  45. }
  46. }

实现类

  1. package com.chen.springbootorderrabbitmqproducer.service;
  2. import org.springframework.amqp.AmqpException;
  3. import org.springframework.amqp.core.Message;
  4. import org.springframework.amqp.core.MessagePostProcessor;
  5. import org.springframework.amqp.rabbit.core.RabbitTemplate;
  6. import org.springframework.beans.factory.annotation.Autowired;
  7. import org.springframework.stereotype.Service;
  8. import java.util.UUID;
  9. /**
  10. */
  11. @Service
  12. public class OrderService {
  13. @Autowired
  14. private RabbitTemplate rabbitTemplate;
  15. // 交换机
  16. private String exchangeName = "fanout_order_ex";
  17. // 路由key
  18. private String routingKey = "";
  19. /**
  20. * @Description 模拟用户购买商品下单的业务
  21. * @Param [userId, productId, num]
  22. * @return void
  23. **/
  24. public void makeOrderDirect(String userId,String productId,int num){
  25. // 1: 根据商品id查询库存是否充足
  26. // 2: 保存订单
  27. String orderId = UUID.randomUUID().toString();
  28. System.out.println("保存订单成功:id是:" + orderId);
  29. // 3: 发送消息
  30. rabbitTemplate.convertAndSend("direct_order_ex","email",orderId);
  31. rabbitTemplate.convertAndSend("direct_order_ex","sms",orderId);
  32. }
  33. }

测试:

  1. package com.chen.springbootorderrabbitmqproducer.rabbitmq.springbootorderrabbitmqproducer;
  2. import com.chen.springbootorderrabbitmqproducer.service.OrderService;
  3. import org.junit.jupiter.api.Test;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.boot.test.context.SpringBootTest;
  6. @SpringBootTest
  7. class SpringbootOrderRabbitmqProducerApplicationTests {
  8. @Autowired
  9. private OrderService orderService;
  10. @Test
  11. public void testDirect() {
  12. orderService.makeOrderDirect("100","100",10);
  13. }
  14. }

消费者

  1. package com.chen.direct;
  2. import org.springframework.amqp.rabbit.annotation.RabbitHandler;
  3. import org.springframework.amqp.rabbit.annotation.RabbitListener;
  4. import org.springframework.stereotype.Service;
  5. /**
  6. * @description:
  7. */
  8. @Service
  9. @RabbitListener(queues ={"duanxin.direct.queue"})
  10. public class DirectDuanxinConsumber {
  11. // 告诉你的接收服务器的消息,没有返回值
  12. @RabbitHandler
  13. public void reviceMessage(String message){
  14. System.out.println("duanxin--direct--->接收到订单消息,订单id是: " + message);
  15. }
  16. }
  1. package com.chen.direct;
  2. import org.springframework.amqp.rabbit.annotation.RabbitHandler;
  3. import org.springframework.amqp.rabbit.annotation.RabbitListener;
  4. import org.springframework.stereotype.Service;
  5. /**
  6. */
  7. @Service
  8. @RabbitListener(queues ={"email.direct.queue"})
  9. public class DirectEmailConsumber {
  10. @RabbitHandler
  11. public void reviceMessage(String message){
  12. System.out.println("email---direct-->接收到订单消息,订单id是: " + message);
  13. }
  14. }
  1. package com.chen.direct;
  2. import org.springframework.amqp.rabbit.annotation.RabbitHandler;
  3. import org.springframework.amqp.rabbit.annotation.RabbitListener;
  4. import org.springframework.stereotype.Service;
  5. /**
  6. @Service
  7. @RabbitListener(queues ={"sms.direct.queue"})
  8. public class DirectSmsConsumber {
  9. @RabbitHandler
  10. public void reviceMessage(String message){
  11. System.out.println("sms--direct--->接收到订单消息,订单id是: " + message);
  12. }
  13. }

3. Topic 模式

生产者

  1. public class OrderService{
  2. @Autowired
  3. private RabbitTemplate rabbitTemplate;
  4. //模拟用户下单
  5. public void makeOrder(String userid,String productid,int num){
  6. public void makeOrderTopic(String userId,String productId,int num){
  7. // 1: 根据商品id查询库存是否充足
  8. // 2: 保存订单
  9. String orderId = UUID.randomUUID().toString();
  10. System.out.println("保存订单成功:id是:" + orderId);
  11. // 3: 发送消息
  12. //com.# duanxin
  13. //#.email.* email
  14. //#.sms.# sms
  15. // 设置消息确认机制
  16. rabbitTemplate.convertAndSend("topic_order_ex","com.email.sms.xxx",orderId);
  17. }
  18. }
  1. @Test
  2. public void testTopic() {
  3. orderService.makeOrderTopic("100","100",10);
  4. }

 

消费者(采用注解)

FanoutSmsConsumer.java

  1. @Component
  2. @RabbitListener(bindings = @QueueBinding(
  3. value = @Queue(value = "sms.topic.queue",durable = "true",antoDelete = "false"),
  4. exchange = @Exchange(value = "topic_order_exchange",type = "ExchangeTypes.TOPIC")
  5. key = "#.sms.#"
  6. ))
  7. public class TopicSmsConsumer{
  8. @RabbitHandler
  9. public void reviceMessage(String message){
  10. sout("sms接收到了的订单信息是:"+message);
  11. }
  12. }

FanoutDuanxinConsumer.java

  1. @Component
  2. @RabbitListener(bindings = @QueueBinding(
  3. value = @Queue(value = "duanxin.topic.queue",durable = "true",antoDelete = "false"),
  4. exchange = @Exchange(value = "topic_order_exchange",type = "ExchangeTypes.TOPIC")
  5. key = "#.duanxin.#"
  6. ))
  7. public classTopicDuanxinConsumer{
  8. @RabbitHandler
  9. public void reviceMessage(String message){
  10. sout("duanxin接收到了的订单信息是:"+message);
  11. }
  12. }

FanoutEmailConsumer.java

  1. @Component
  2. @RabbitListener(bindings = @QueueBinding(
  3. value = @Queue(value = "email.topic.queue",durable = "true",antoDelete = "false"),
  4. exchange = @Exchange(value = "topic_order_exchange",type = "ExchangeTypes.TOPIC")
  5. key = "#.email.#"
  6. ))
  7. public class TopicEmailConsumer{
  8. @RabbitHandler
  9. public void reviceMessage(String message){
  10. sout("email接收到了的订单信息是:"+message);
  11. }
  12. }

五、RabbitMQ高级

1. 过期时间TTL

概述

过期时间 TTl表示可以对消息设置预期的时间,在这个时间内都可以被消费者接收获取;过了之后消息将自动被删除。RabbitMQ可以对消息和队列设置 TTL,目前有两种方法可以设置

  1. 第一种方法是通过队列属性设置,队列中所有消息都有相同的过期时间

  2. 第二种方法是对消息进行单独设置,每条消息 TTL可以不同

如果上述两种方法同时使用,则消息的过期时间以两者 TTL较小的那个数值为准。消息在队列的生存时间一旦超过设置的 TTL值,就称为 dead message被投递到死信队列,消费者将无法再收到该消息

设置队列TTL

 RabbitMqConfiguration.java

  1. @Configuration
  2. public class TTLRabbitMQConfiguration{
  3. //1.声明注册direct模式的交换机
  4. @Bean
  5. public DirectExchange ttldirectExchange(){
  6. return new DirectExchange("ttl_direct_exchange",true,false);}
  7. //2.队列的过期时间
  8. @Bean
  9. public Queue directttlQueue(){
  10. //设置过期时间
  11. Map<String,Object> args = new HashMap<>();
  12. args.put("x-message-ttl",5000);//这里一定是int类型
  13. return new Queue("ttl.direct.queue",true,false,false,args);}
  14. @Bean
  15. public Binding ttlBingding(){
  16. return BindingBuilder.bin(directttlQueue()).to(ttldirectExchange()).with("ttl");
  17. }
  18. }

设置消息TTL

  1. public class OrderService{
  2. @Autowired
  3. private RabbitTemplate rabbitTemplate;
  4. //模拟用户下单
  5. public void makeOrder(String userid,String productid,int num){
  6. //1.根据商品id查询库存是否足够
  7. //2.保存订单
  8. String orderId = UUID.randomUUID().toString();
  9. sout("订单生产成功:"+orderId);
  10. //3.通过MQ来完成消息的分发
  11. //参数1:交换机 参数2:路由key/queue队列名称 参数3:消息内容
  12. String exchangeName = "ttl_order_exchange";
  13. String routingKey = "ttlmessage";
  14. //给消息设置过期时间
  15. MessagePostProcessor messagePostProcessor = new MessagePostProcessor(){
  16. public Message postProcessMessage(Message message){
  17. //这里就是字符串
  18. message.getMessageProperties().setExpiration("5000");
  19. message.getMessageProperties().setContentEncoding("UTF-8");
  20. return message;
  21. }
  22. }
  23. rabbitTemplate.convertAndSend(exchangeName,routingKey,orderId,messagePostProcessor);
  24. }
  25. }

RabbitMqConfiguration.java

  1. @Configuration
  2. public class TTLRabbitMQConfiguration{
  3. //1.声明注册direct模式的交换机
  4. @Bean
  5. public DirectExchange ttldirectExchange(){
  6. return new DirectExchange("ttl_direct_exchange",true,false);}
  7. //2.队列的过期时间
  8. @Bean
  9. public Queue directttlQueue(){
  10. //设置过期时间
  11. Map<String,Object> args = new HashMap<>();
  12. args.put("x-message-ttl",5000);//这里一定是int类型
  13. return new Queue("ttl.direct.queue",true,false,false,args);}
  14. @Bean
  15. public Queue directttlMessageQueue(){
  16. return new Queue("ttlMessage.direct.queue",true,false,false,args);}
  17. @Bean
  18. public Binding ttlBingding(){
  19. return BindingBuilder.bin(directttlMessageQueue()).to(ttldirectExchange()).with("ttlmessage");
  20. }
  21. }

2. 死信队列

概述

DLX,全称 Dead-Letter-Exchange,可以称之为死信交换机,也有人称之为死信邮箱。当消息再一个队列中变成死信之后,它能被重新发送到另一个交换机中,这个交换机就是 DLX,绑定 DLX的队列就称之为死信队列。消息变成死信,可能是由于以下原因:

  1. 消息被拒绝

  2. 消息过期

  3. 队列达到最大长度

DLX也是一个正常的交换机,和一般的交换机没有区别,它能在任何的队列上被指定,实际上就是设置某一个队列的属性,当这个队列中存在死信时,Rabbitmq就会自动地将这个消息重新发布到设置的 DLX上去,进而被路由到另一个队列,即死信队列。

要想使用死信队列,只需要在定义队列的时候设置队列参数x-dead-letter-exchange指定交换机即可

代码

DeadRabbitMqConfiguration.java

  1. @Configuration
  2. public class DeadRabbitMqConfiguration{
  3. //1.声明注册direct模式的交换机
  4. @Bean
  5. public DirectExchange deadDirect(){
  6. return new DirectExchange("dead_direct_exchange",true,false);}
  7. //2.队列的过期时间
  8. @Bean
  9. public Queue deadQueue(){
  10. return new Queue("dead.direct.queue",true);}
  11. @Bean
  12. public Binding deadbinds(){
  13. return BindingBuilder.bind(deadDirect()).to(deadQueue()).with("dead");
  14. }
  15. }

RabbitMqConfiguration.java

  1. @Configuration
  2. public class TTLRabbitMQConfiguration{
  3. //1.声明注册direct模式的交换机
  4. @Bean
  5. public DirectExchange ttldirectExchange(){
  6. return new DirectExchange("ttl_direct_exchange",true,false);}
  7. //2.队列的过期时间
  8. @Bean
  9. public Queue directttlQueue(){
  10. //设置过期时间
  11. Map<String,Object> args = new HashMap<>();
  12. //args.put("x-max-length",5);
  13. args.put("x-message-ttl",5000);//这里一定是int类型
  14. args.put("x-dead-letter-exchange","dead_direct_exchange");
  15. args.put("x-dead-letter-routing-key","dead");//fanout不需要配置
  16. return new Queue("ttl.direct.queue",true,false,false,args);}
  17. @Bean
  18. public Binding ttlBingding(){
  19. return BindingBuilder.bin(directttlQueue()).to(ttldirectExchange()).with("ttlmessage");
  20. }
  21. }

 

3. 内存磁盘的监控

01 RabbitMQ内存警告

02 RabbitMQ的内存控制

参考帮助文档:http://www.rabbbitmq.com/configure.html

当出现警告的时候,可以通过配置去修改和调整

命令的方式

rabbitmqctl set_vm_memory_high_watermark <fraction>
rabbitmqctl set_vm_memory_high_watermark absolute 50MB

fraction/value 为内存阈值。默认情况是:0.4/2GB,代表的含义是:当 RabbitMQ的内存超过40%时,就会产生警告并且会阻塞所有生产者的连接。通过此命令修改阈值在 Broker重启以后将会失效,通过修改配置文件设置的阈值则不会随着重启而消失,但修改了配置文件一样要重启 Broker才会生效  

配置文件方式 rabbitmq.conf

03 RabbitMQ的内存换页

 

 

04 RabbitMQ的磁盘预警

 

4. 集群(docker集群rabbitmq)

1.先创建三个rabbitmq容器

  1. docker run -itd --name rabbit01 --hostname myrabbit01 -v /home/software/rabbitmqcluster/rabbitmq01:/var/lib/rabbitmq -e RABBITMQ_DEFAULT_USER=chenjinxian -e RABBITMQ_DEFAULT_PASS=chenjinxian -e RABBITMQ_ERLANG_COOKIE='rabbitmqCookie' -p 15672:15672 -p 5672:5672 -p 25672:25672 rabbitmq:management
  2. docker run -itd --name rabbit02 --hostname myrabbit02 -v /home/software/rabbitmqcluster/rabbitmq01:/var/lib/rabbitmq -e RABBITMQ_DEFAULT_USER=chenjinxian -e RABBITMQ_DEFAULT_PASS=chenjinxian -e RABBITMQ_ERLANG_COOKIE='rabbitmqCookie' --link rabbit01:myrabbit01 -p 15673:15672 -p 5673:5672 -p 25673:25672 rabbitmq:management
  3. docker run -itd --name rabbit03 --hostname myrabbit03 -v /home/software/rabbitmqcluster/rabbitmq01:/var/lib/rabbitmq -e RABBITMQ_DEFAULT_USER=chenjinxian -e RABBITMQ_DEFAULT_PASS=chenjinxian -e RABBITMQ_ERLANG_COOKIE='rabbitmqCookie' --link rabbit01:myrabbit01 --link rabbit02:myrabbit02 -p 15674:15672 -p 5674:5672 -p 25674:25672 rabbitmq:management

启动容器成功后,读者可以访问

 分别通过浏览器访问:ip(自己的IP地址):15672;ip:15673;ip:15674都可以访问

2.容器节点加入集群

执行如下命令,进入第一个rabbitmq节点容器:

docker exec -it rabbit01 /bin/bash

进入容器后,操作rabbitmq,执行如下命令:

  1. rabbitmqctl stop_app
  2. rabbitmqctl reset
  3. rabbitmqctl start_app
  4. exit

执行如下命令,进入第二个rabbitmq节点容器:

docker exec -it rabbit02 /bin/bash

进入第二个rabbitmq节点容器,执行如下命令:

  1. rabbitmqctl stop_app
  2. rabbitmqctl reset
  3. rabbitmqctl join_cluster --ram rabbit@myrabbit01
  4. rabbitmqctl start_app
  5. exit

,进入第三个rabbitmq节点容器,执行如下命令:

  1. docker exec -it rabbit03 /bin/bash
  2. rabbitmqctl stop_app
  3. rabbitmqctl reset
  4. rabbitmqctl join_cluster --ram rabbit@myrabbit01
  5. rabbitmqctl start_app
  6. exit

最后可以看到节点信息

 

网站建设定制开发 软件系统开发定制 定制软件开发 软件开发定制 定制app开发 app开发定制 app开发定制公司 电商商城定制开发 定制小程序开发 定制开发小程序 客户管理系统开发定制 定制网站 定制开发 crm开发定制 开发公司 小程序开发定制 定制软件 收款定制开发 企业网站定制开发 定制化开发 android系统定制开发 定制小程序开发费用 定制设计 专注app软件定制开发 软件开发定制定制 知名网站建设定制 软件定制开发供应商 应用系统定制开发 软件系统定制开发 企业管理系统定制开发 系统定制开发