文章目录
RabbitMQ — docker安装RabbitMQ,Java + Python集成
一、简介
-
rabbitmq:
数据一致性
-
基于AMQP协议
二、安装
1. docker方式
-
docker拉取和启动:
推荐以下方式2和3,方式2和3安装配置完,在web管理界面可以直接通过 admin / admin进行登录-
方式1,官方命令:
docker run -it --rm --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:3-management
-
方式2,运行时配置用户和密码:
docker run -d --hostname my-rabbit --name myrabbit -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=admin -p 5672:5672 -p 15672:15672 rabbitmq:3-management -h "mars": 指定容器的hostname; 15672是管理界面的端口,5672是服务的端口
-
方式3,分开运行:
下载镜像 docker pull rabbitmq:3-management 创建容器并运行(15672是管理界面的端口,5672是服务的端口。这里顺便将管理系统的用户名和密码设置为admin admin) docker run -dit --name Myrabbitmq -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=admin -p 15672:15672 -p 5672:5672 rabbitmq:3-management
-
-
web访问:
http://192.168.142.63:15672/ 如果配置好了用户名和密码,则采用配置好的用户名和密码进行登录,否准为guest/guest
2. rabbitmq常用命令
2.1 授权账号和密码
- 参考:https://www.linuxhub.cn/2018/08/14/rabbitmq-user.html
-
创建用户
rabbitmqctl add_user admin abc123456 # 帐号:admin # 密码:abc123456
-
设置管理员
rabbitmqctl set_user_tags admin administrator
-
用户级别:
-
超级管理员(administrator)
可登陆管理控制台(启用management plugin的情况下),可查看所有的信息,并且可以对用户,策略(policy)进行操作。
-
监控者(monitoring)
可登陆管理控制台(启用management plugin的情况下),同时可以查看rabbitmq节点的相关信息(进程数,内存使用情况,磁盘使用情况等)
-
策略制定者(policymaker)
可登陆管理控制台(启用management plugin的情况下), 同时可以对policy进行管理。但无法查看节点的相关信息(上图红框标识的部分)。
与administrator的对比,administrator能看到这些内容
-
普通管理者(management)
仅可登陆管理控制台(启用management plugin的情况下),无法看到节点信息,也无法对策略进行管理。
-
其他
无法登陆管理控制台,通常就是普通的生产者和消费者。
-
-
-
设置权限
rabbitmqctl set_permissions -p / admin ".*" ".*" ".*"
-
列出用户
rabbitmqctl list_users
-
修改密码:
#修改guest用户密码为abc123456 rabbitmqctl change_password guest abc123456
-
删除用户:
rabbitmqctl delete_user username
三、rabbitmq基本知识
- 参考资料:https://blog.csdn.net/qq_35387940/article/details/100514134
rabbitmq消息推送到接收的流程:
-
交换机:
常用的交换机有以下三种,因为消费者是从队列获取信息的,队列是绑定交换机的(一般),所以对应的消息推送/接收模式也会有以下几种:
-
Direct Exchange:
直连型交换机,根据消息携带的路由键将消息投递给对应队列。
大致流程,有一个队列绑定到一个直连交换机上,同时赋予一个路由键 routing key 。
然后当一个消息携带着路由值为X,这个消息通过生产者发送给交换机时,交换机就会根据这个路由值X去寻找绑定值也是X的队列。 -
Fanout Exchange:
扇型交换机,这个交换机没有路由键概念,就算你绑了路由键也是无视的。 这个交换机在接收到消息后,会直接转发到绑定到它上面的所有队列。
-
Topic Exchange:
主题交换机,这个交换机其实跟直连交换机流程差不多,但是它的特点就是在它的路由键和绑定键之间是有规则的。
简单地介绍下规则:- *(星号) 用来表示一个单词 (必须出现的)
- #(井号) 用来表示任意数量(零个或多个)单词
通配的绑定键是跟队列进行绑定的,举个小例子:
- 队列Q1 绑定键为 *.TT.* 队列Q2绑定键为 TT.#
如果一条消息携带的路由键为 A.TT.B,那么队列Q1将会收到;
如果一条消息携带的路由键为TT.AA.BB,那么队列Q2将会收到;
主题交换机是非常强大的,为啥这么膨胀?
当一个队列的绑定键为 “#”(井号) 的时候,这个队列将会无视消息的路由键,接收所有的消息。
当 * (星号) 和 # (井号) 这两个特殊字符都未在绑定键中出现的时候,此时主题交换机就拥有的直连交换机的行为。
所以主题交换机也就实现了扇形交换机的功能,和直连交换机的功能。管理界面中交换机如图:
-
-
virtual host:
虚拟主机,类似于MySQL中的数据库,一个项目(一块业务)对应一个虚拟主机
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4m3av3fc-1619705906487)(C:\Users\penti\AppData\Roaming\Typora\typora-user-images\image-20210428143930025.png)]
管理界面中虚拟主机界面:
四、代码集成
1. Java
参考资料:https://www.cnblogs.com/jikeyi/p/13339124.html
-
依赖:
<dependency> <groupId>com.rabbitmq</groupId> <artifactId>amqp-client</artifactId> <version>4.10.0</version> </dependency>
-
代码编程:
-
工具类:
import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import com.rabbitmq.client.ConnectionFactory; /** * @projName: JavaSkillStack * @packgeName: indi.pentiumcm.rabbitmq * @className: ConnectionUtil * @author: pentiumCM * @email: 842679178@qq.com * @date: 2021/4/28 17:37 * @describe: RabbitMq帮助类 */ public class RabbitMqUtil { private static ConnectionFactory factory; static { // static:静态代码块类加载执行,执行一次 // 重量级资源 // 1.创建连接工厂 factory = new ConnectionFactory(); // 2.在工厂对象中设置MQ的连接信息(ip,port,virtualhost,username,password) factory.setHost("192.168.142.63"); // 设置端口号 factory.setPort(5672); // 设置连接哪个虚拟主机 factory.setVirtualHost("/test"); // 设置访问虚拟主机的用户名和密码 factory.setUsername("admin"); factory.setPassword("admin"); } /** * 定义提供连接对象 * * @return */ public static Connection getConnection() { try { // 3.通过工厂对象获取与MQ的链接 Connection connection = factory.newConnection(); return connection; } catch (Exception e) { e.printStackTrace(); } return null; } /** * 关闭通道和连接的工具方法 * * @param channel * @param connection */ public static void closeConnectionAndChanel(Channel channel, Connection connection) { try { if (channel != null) { channel.close(); } if (connection != null) { connection.close(); } } catch (Exception e) { e.printStackTrace(); } } }
-
生产者:
import com.rabbitmq.client.*; /** * @projName: JavaSkillStack * @packgeName: indi.pentiumcm.rabbitmq * @className: SendMsg * @author: pentiumCM * @email: 842679178@qq.com * @date: 2021/4/28 19:49 * @describe: 消息队列——生产者 */ public class Productor { public static void main(String[] args) throws Exception { String QUEUE_NAME = "hello"; // 1. 创建连接 Connection connection = RabbitMqUtil.getConnection(); // 2. 获取连接中的通道 Channel channel = connection.createChannel(); // 3. 声明队列,声明队列,如果该队列已经创建过,则不会重复创建 // 参数1:定义的队列名称 // 参数2:队列中的数据是否持久化(如果选择了持久化) // 参数3: 是否独占(当前队列是否为当前连接私有) // 参数4:自动删除(当此队列的连接数为0时,此队列会销毁(无论队列中是否还有数据)) // 参数5:设置当前队列的参数 channel.queueDeclare(QUEUE_NAME, true, false, false, null); String msg = "I am Java!"; // 4. 发布消息 // 参数1:交换机名称,如果直接发送信息到队列,则交换机名称为"" // 参数2:目标队列名称 // 参数3:设置当前这条消息的属性(设置过期时间 10) // 参数4:消息的内容 channel.basicPublish("", QUEUE_NAME, false, null, msg.getBytes()); System.out.println("生产者发送:" + msg); /* channel.close(); connection.close();*/ RabbitMqUtil.closeConnectionAndChanel(channel, connection); } }
-
消费者:
import com.rabbitmq.client.*; import java.io.IOException; import java.util.concurrent.TimeoutException; /** * @projName: JavaSkillStack * @packgeName: indi.pentiumcm.rabbitmq * @className: ReceiveMsg * @author: pentiumCM * @email: 842679178@qq.com * @date: 2021/4/28 20:12 * @describe: 消息队列——消费者 */ public class Consumer { public static void main(String[] args) throws IOException, TimeoutException { String QUEUE_NAME = "hello"; // 创建连接对象 Connection connection = RabbitMqUtil.getConnection(); // 创建通道 Channel channel = connection.createChannel(); // 通道绑定对应消息队列,声明队列,如果该队列已经创建过,则不会重复创建 channel.queueDeclare(QUEUE_NAME, true, false, false, null); DefaultConsumer defaultConsumer = new DefaultConsumer(channel) { @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { super.handleDelivery(consumerTag, envelope, properties, body); //body就是从队列中获取的数据 String message = new String(body, "UTF-8"); System.out.println("消费者接收:" + message); } }; // 消费信息 channel.basicConsume(QUEUE_NAME, true, defaultConsumer); // RabbitMqUtil.closeConnectionAndChanel(channel, connection); } }
-
2. Python
-
依赖:
python -m pip install pika --upgrade
-
代码编程:
-
生产者:
#!/usr/bin/env python # encoding: utf-8 ''' @Author : pentiumCM @Email : 842679178@qq.com @Software: PyCharm @File : productor.py @Time : 2021/4/29 20:34 @desc : 消息队列——生产者 ''' import pika import json host_ip = '192.168.142.63' port = 5672 virtual_host = '/test' QUEUE_NAME = "hello" # mq用户名和密码 credentials = pika.PlainCredentials('admin', 'admin') # 1. 建立连接 connection = pika.BlockingConnection( pika.ConnectionParameters(host=host_ip, port=port, virtual_host=virtual_host, credentials=credentials)) # 2. 建立通道 channel = connection.channel() # 3. 指定队列 channel.queue_declare(queue=QUEUE_NAME, durable=True) # 4. 发布消息 message = json.dumps({'name': 'pentiumcm'}) # 向队列插入数值 routing_key是队列名 channel.basic_publish(exchange='', routing_key=QUEUE_NAME, body=message) print("生产者发送完成:" + message) connection.close()
-
消费者:
#!/usr/bin/env python # encoding: utf-8 ''' @Author : pentiumCM @Email : 842679178@qq.com @Software: PyCharm @File : consumer.py @Time : 2021/4/29 20:34 @desc : 消息队列——消费者 ''' import pika import json host_ip = '192.168.142.63' port = 5672 virtual_host = '/test' QUEUE_NAME = "hello" # mq用户名和密码 credentials = pika.PlainCredentials('admin', 'admin') # 1. 建立连接 connection = pika.BlockingConnection(pika.ConnectionParameters(host=host_ip, port=port, virtual_host=virtual_host, credentials=credentials)) # 2. 建立通道 channel = connection.channel() # 3. 指定队列 channel.queue_declare(queue=QUEUE_NAME, durable=True) # 定义一个回调函数来处理消息队列中的消息 def callback(ch, method, properties, body): # 接收到生产者消息 msg = json.loads(body) print("消费者接收:" % msg['name']) # 4. 消费消息 channel.basic_consume(queue=QUEUE_NAME, auto_ack=True, on_message_callback=callback) channel.start_consuming()
-
五、业务设计
- 方式一:
业务中可设计两个队列:未处理,已处理
:
生产者先往未处理队列中发送消息,消费者从未处理的队列中去消息进行处理,当处理完毕之后将处理结果放到已处理的队列中
参考资料
-
docker镜像:https://registry.hub.docker.com/_/rabbitmq/
-
https://www.cnblogs.com/sgh1023/p/11217017.html