rabbitmq知识汇总
一、什么是消息中间件
1. 基于消息中间件的分布式系统的架构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v81ZRat2-1624005504828)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210610153533157.png)]
2. 消息中间件应用的场景
- 跨系统数据传递
- 高并发的流量削峰
- 数据的分发和异步处理
- 大数据的分析与传递
- 分布式事务
3. RabbitMQ 为什么需要信道?为什么不是TCP直接通信?
- TCP的创建和销毁,开销大,创建需要三次握手,销毁需要四次分手
- 如果不使用信道,那么引用程序就会使用TCP的方式连接到rabbitmq,高峰时每秒成千上万条连接会造成资源的巨大浪费(一条tcp消耗资源,成千上万的tcp会非常消耗资源),而且操作系统每秒处理TCP连接数量也是有限的,必定会造成性能瓶颈
- 信道的原理是一条线程一条信道,多条线程多条信道共同使用一条TCP连接。一条TCP连接可以容纳无限的信道,及时每秒造成成千上万的请求也不会造成性能瓶颈
4. 消息持久化,常见的持久化方式
ActiveMq | Rabbitmq | kafka | Rockmq | |
---|---|---|---|---|
文件存储 | 支持 | 支持 | 支持 | 支持 |
数据库 | 支持 | / | / | / |
5.消息分发策略的机制和对比
ActiveMq | Rabbitmq | kafka | Rockmq | |
---|---|---|---|---|
发布订阅 | 支持 | 支持 | 支持 | 支持 |
轮询分发 | 支持 | 支持 | 支持 | / |
公平分发 | / | 支持 | 支持 | / |
重发 | 支持 | 支持 | / | 支持 |
消息拉取 | / | 支持 | 支持 | 支持 |
6.消息中间件的结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-90wKXvzZ-1624005504831)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210617090341769.png)]
核心概念:
- Server: 又称Broke,接受客户端的连接,实现AMQP实体服务,安装rabbitmq-server
- Connection: 连接,应用程序与Broke的网络连接TCP/IP 三次握手和四次挥手
- Channel:网络信道,几乎所有的操作都在Channel中进行,Channel是进行消息读写的通道,客户端可以建立对各Channel,每个Channel代表一个会话任务
- Message: 消息,服务与应用程序之间传送的数据,由Properties和body组成,Properties可是对消息进行修饰,比如消息的优先级、延迟等高等特性,Body则就是消息体的内容
- Virtual Host:虚拟地址,用于进行逻辑隔离,最上层的消息路由,一个虚拟机可以有若干个Exchange和Queue,同一个虚拟机主机里面不能有相同名字和Exchange
- Exchange:交换机,接收消息,根据路由键发送消息到绑定的队列(不具备消息存储的能力)
- Binding:Exchange和Queue之间的虚拟连接,binding中可以保护多个routing key
- routing key:是一个路由规则,虚拟机可以用它来确定如何路由一个特定消息
- Queue:队列,也成为Message Queue。消息队列,保存消息并将它们转发给消费者。
rabbitmq的运行流程:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lLfQsC2m-1624005504832)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210617142927721.png)]
二、安装过程
1.安装erlang环境
-
查看erlang与rabbitmq的版本对应关系:
https://www.rabbitmq.com/which-erlang.html
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-um99vqEL-1624005504834)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210611091932922.png)]
2.去erlang官网下载对应版本rpm文件,并上传至linux服务器在安装(推荐),在线安装比较慢,一般需要fanqiang下载:
https://www.erlang-solutions.com/downloads/#
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HQ8IDsCo-1624005504835)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210611092130951.png)]
使用 rpm -Uvh esl-erlang_24.0-1_centos_7_amd64.rpm 会报如下错误:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GEs1ziim-1624005504836)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210611092300062.png)]
需要先执行下列命令即可:
sudo yum install epel-release sudo yum install unixODBC unixODBC-devel wxBase wxGTK SDL wxGTK-gl 或者执行: rpm -Uvh esl-erlang_23.2-1_centos_7_amd64.rpm --force --nodeps
使用 erl -v 检查是否安装成功,如下
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LcztHNQ2-1624005504837)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210611092749327.png)]
-
安装socat,如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-q6zpmKIT-1624005504837)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210611092932720.png)]
至此,erlang环境安装完毕!
2.安装rabbitmq
-
执行rpm -Uvh rabbitmq-server-3.8.16-1.el7.noarch.rpm
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h2JOSa9y-1624005504838)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210611093602214.png)]
-
执行yum install rabbitmq-server -y
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hLwubiNA-1624005504839)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210611093622407.png)]
-
执行systemctl start rabbitmq-server启动
-
执行systemctl status rabbitmq-server查看,如下
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gRBIS748-1624005504839)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210611094013631.png)]
-
执行systemctl enable rabbitmq-server开机自启动
-
执行systemctl stop rabbitmq-server开机自启动关闭
3. 执行命令安装rabbitmqweb
rabbitmq-plugins enable rabbitmq_management
相关配置命令如下:
1:查看防火状态 systemctl status firewalld service iptables status 2:暂时关闭防火墙 systemctl stop firewalld service iptables stop 3:永久关闭防火墙 systemctl disable firewalld chkconfig iptables off 4:重启防火墙 systemctl enable firewalld service iptables restart 5:永久关闭后重启 chkconfig iptables on
4.授权账号和密码
新增用户:
rabbitmqctl add_user admin admin
设置用户分配操作权限:
rabbitmqctl set_user_tags admin administrator
用户级别如下:
- administrator 可以登录控制台、查看所有信息、可以对rabbitmq进行管理
- monitoring 监控者登录控制台、查看所有信息
- policymaker 策略制定者 登录控制台指定策略
- managment 普通管理员 登录控制台
为用户赋权:
sudo rabbitmqctl set_permissions -p / admintest '.*' '.*' '.*'
-
三、spring boot集成
1.普通maven下的简单模式(simple),举例
<!-- https://mvnrepository.com/artifact/com.rabbitmq/amqp-client -->
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.12.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>
package com.study.dyq.rabbitmq_01.simple;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* maven 简单模式simple的demo
* 注释:所有的中间件技术都是基于tcp/ip协议基础之上的,rabbitmq遵循的是amqp协议,便于在消息头传递一些附加的的信息
*/
public class Productor {
public static void main(String[] args) {
// 1.创建连接工程
ConnectionFactory cf = new ConnectionFactory();
cf.setHost("10.200.4.126");
cf.setPort(5672);
cf.setUsername("admintest");
cf.setPassword("admintest");
cf.setVirtualHost("/");
Connection connection = null;// 声明连接
Channel channel = null;// 声明通道
try {
// 2.创建连接connection
connection = cf.newConnection("生产者");
// 3.通过连接获取通道Channel
channel = connection.createChannel();
// 4.通过通过创建交换机,声明队列,绑定关系、路由key,发送消息和接收消息
String queueName = "queue1";
/**
* @Param1 队列的名称
* @Param2 是否持久化,true:持久化,false:不持久化
* @Param3 排他性,是否是独占队列
* @Param4 是否自动删除,随着最后一个消费者消费完消息后队列是否要自动删除
* @Param5 携带附属参数
*/
channel.queueDeclare(queueName, true, false, false, null);
// 5.准备消息内容,可以为json格式
JSONObject ob = new JSONObject();
ob.put("消息内容", "我是第一个测试的消息队列呀!");
String msg = JSON.toJSONString(ob);
// 6.发送消息给队列queue
channel.basicPublish("", queueName, MessageProperties.PERSISTENT_TEXT_PLAIN, msg.getBytes());
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
} finally {
// 7.关闭连接
if (channel != null && channel.isOpen()) {
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
// 8.关闭通道
if (connection != null && connection.isOpen()) {
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
package com.study.dyq.rabbitmq_01.simple;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Cunsumer {
public static void main(String[] args) {
// 1.创建连接工程
ConnectionFactory cf = new ConnectionFactory();
cf.setHost("10.200.4.126");
cf.setPort(5672);
cf.setUsername("admintest");
cf.setPassword("admintest");
cf.setVirtualHost("/");
Connection connection = null;// 声明连接
Channel channel = null;// 声明通道
try {
// 2.创建连接connection
connection = cf.newConnection("生产者");
// 3.通过连接获取通道Channel
channel = connection.createChannel();
// 4.通过通过创建交换机,声明队列,绑定关系、路由key,发送消息和接收消息
String queueName = "queue1";
channel.basicConsume(queueName, true, new DeliverCallback() {
@Override
public void handle(String s, Delivery delivery) throws IOException {
System.out.println("收到的消息是:"+new String(delivery.getBody(), "utf-8"));
}
}, new CancelCallback() {
@Override
public void handle(String s) throws IOException {
System.out.println("收到消息失败了!");
}
});
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
} finally {
// 7.关闭连接
if (channel != null && channel.isOpen()) {
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
// 8.关闭通道
if (connection != null && connection.isOpen()) {
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
1.简单模式(simple)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ome12qut-1624005504840)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210618095504495.png)]
1.生产者服务:
package com.study.dyq.rabbitmq_02.configration;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author dyq
* @description 一对一简单模式
* @data 2021/6/18
*/
@Configuration
public class SimpleRabbitConfigration {
@Bean
public Queue simpleQueue(){
return new Queue("queue_simple");
}
}
/**
* 简单模式
*/
public void addSimpleOrder(String userId, String productId, int num, String queueSimple){
// 保存订单
String orderId = UUID.randomUUID().toString();
System.out.println("创建生产者成功,订单号为:"+orderId);
// 通知其他服务,通过mq来完成
rabbitTemplate.convertAndSend(queueSimple, userId+"买了"+num+"套"+productId+",订单号:"+orderId);
}
2.消费者服务:
package com.study.dyq.rabbitmq_03.service.simple;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Service;
/**
* @author dyq
* @description simple模拟测试
* @data 2021/6/17
*/
@Service
@RabbitListener(queues = {"queue_simple"})
public class smsSimpleService {
@RabbitHandler
public void receiveMessage(String message){
System.out.println("sms simple-----接收到了订单的信息是:"+message);
}
}
2.工作模式(work)
主要有两种模式:
- 轮询模式的分发:一个消费者消费一条,按均分配。
- 公平分发:根据消费者能力进行分发,按劳分配。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nM9QsMjI-1624005504841)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210618094917971.png)]
3.发布/订阅模式(fanout)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-okhSYFmF-1624005504841)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210617151719526.png)]
sprintboot下的整合:
1.生产者服务:
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-amqp -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
package com.study.dyq.rabbitmq_02.configration;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author dyq
* @description rebbitmq消息中间件配置类
* @data 2021/6/17
*/
@Configuration
public class RabbitConfigration {
/**
* 1.声明注册lanout模式的交换机
* @return
*/
@Bean
public FanoutExchange fanoutExchange(){
return new FanoutExchange("fanout_order_exchange", true, false);
}
/**
* 2.声明队列
*/
@Bean
public Queue smsQueue(){
// @param1 队列名称 @param2 是否持久化
return new Queue("smsQueue", true);
}
@Bean
public Queue duanxinQueue(){
// @param1 队列名称 @param2 是否持久化
return new Queue("duanxinQueue", true);
}
@Bean
public Queue emailQueue(){
// @param1 队列名称 @param2 是否持久化
return new Queue("emailQueue", true);
}
// 3.设置绑定关系
@Bean
public Binding smsBinding(){
return BindingBuilder.bind(smsQueue()).to(fanoutExchange());
}
@Bean
public Binding duanxinBinding(){
return BindingBuilder.bind(duanxinQueue()).to(fanoutExchange());
}
@Bean
public Binding emailBinding(){
return BindingBuilder.bind(emailQueue()).to(fanoutExchange());
}
}
package com.study.dyq.rabbitmq_02.service;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.UUID;
/**
* @author dyq
* @description 模拟订单类
* @data 2021/6/17
*/
@Service
public class OrderService {
@Autowired
private RabbitTemplate rabbitTemplate;
/**
*
*/
public void addOrder(String userId, String productId, int num){
// 保存订单
String orderId = UUID.randomUUID().toString();
System.out.println("创建生产者成功,订单号为:"+orderId);
// 通知其他服务,通过mq来完成
String exchangeName = "fanout_order_exchange";
rabbitTemplate.convertAndSend(exchangeName, "", userId+"买了"+num+"套"+productId+",订单号:"+orderId);
}
}
# 服务端口
server:
port: 8091
# 配置rabbitmq服务
spring:
rabbitmq:
username: admintest
password: admintest
virtual-host: /
host: 10.200.4.126
port: 5672
2.消费者服务
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-amqp -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
package com.study.dyq.rabbitmq_03.service;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Service;
/**
* @author dyq
* @description fanout模拟测试
* @data 2021/6/17
*/
@Service
@RabbitListener(queues = {"duanxinQueue"})
public class duanxinFanoutService {
@RabbitHandler
public void receiveMessage(String message){
System.out.println("duanxin fanout-----接收到了订单的信息是:"+message);
}
}
package com.study.dyq.rabbitmq_03.service;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Service;
/**
* @author dyq
* @description fanout模拟测试
* @data 2021/6/17
*/
@Service
@RabbitListener(queues = {"emailQueue"})
public class emailFanoutService {
@RabbitHandler
public void receiveMessage(String message){
System.out.println("email fanout-----接收到了订单的信息是:"+message);
}
}
package com.study.dyq.rabbitmq_03.service;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Service;
/**
* @author dyq
* @description fanout模拟测试
* @data 2021/6/17
*/
@Service
@RabbitListener(queues = {"smsQueue"})
public class smsFanoutService {
@RabbitHandler
public void receiveMessage(String message){
System.out.println("sms fanout-----接收到了订单的信息是:"+message);
}
}
4.路由模式(routing)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HUhVFilj-1624005504842)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210617172836192.png)]
1.生产者服务
package com.study.dyq.rabbitmq_02.configration;
import com.rabbitmq.client.AMQP;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author dyq
* @description rabbit测试direct模式
* @data 2021/6/17
*/
@Configuration
public class DirectRabbitConfigration {
/**
* 1.声明注册direct模式的交换机
* @return
*/
@Bean
public DirectExchange directExchange(){
return new DirectExchange("direct_order_exchange", true, false);
}
/**
* 2.声明队列
*/
@Bean
public Queue smsDirectQueue(){
// @param1 队列名称 @param2 是否持久化
return new Queue("smsDirectQueue", true);
}
@Bean
public Queue duanxinDirectQueue(){
// @param1 队列名称 @param2 是否持久化
return new Queue("duanxinDirectQueue", true);
}
@Bean
public Queue emailDirectQueue(){
// @param1 队列名称 @param2 是否持久化
return new Queue("emailDirectQueue", true);
}
// 3.设置绑定关系
@Bean
public Binding smsDirectBinding(){
return BindingBuilder.bind(smsDirectQueue()).to(directExchange()).with("sms");
}
@Bean
public Binding duanxinDirectBinding(){
return BindingBuilder.bind(duanxinDirectQueue()).to(directExchange()).with("duanxin");
}
@Bean
public Binding emailDirectBinding(){
return BindingBuilder.bind(emailDirectQueue()).to(directExchange()).with("email");
}
}
public void addDirectOrder(String userId, String productId, int num, String exchangeName){
// 保存订单
String orderId = UUID.randomUUID().toString();
System.out.println("创建生产者成功,订单号为:"+orderId);
// 通知其他服务,通过mq来完成
rabbitTemplate.convertAndSend(exchangeName, "sms", userId+"买了"+num+"套"+productId+",订单号:"+orderId);
rabbitTemplate.convertAndSend(exchangeName, "email", userId+"买了"+num+"套"+productId+",订单号:"+orderId);
}
2.消费者服务
package com.study.dyq.rabbitmq_03.service.direct;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Service;
/**
* @author dyq
* @description fanout模拟测试
* @data 2021/6/17
*/
@Service
@RabbitListener(queues = {"smsDirectQueue"})
public class SmsDirectService {
@RabbitHandler
public void receiveMessage(String message){
System.out.println("sms direct-----接收到了订单的信息是:"+message);
}
}
package com.study.dyq.rabbitmq_03.service.direct;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Service;
/**
* @author dyq
* @description fanout模拟测试
* @data 2021/6/17
*/
@Service
@RabbitListener(queues = {"emailDirectQueue"})
public class EmailDirectService {
@RabbitHandler
public void receiveMessage(String message){
System.out.println("email Direct-----接收到了订单的信息是:"+message);
}
}
package com.study.dyq.rabbitmq_03.service.direct;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Service;
/**
* @author dyq
* @description fanout模拟测试
* @data 2021/6/17
*/
@Service
@RabbitListener(queues = {"duanxinDirectQueue"})
public class DuanxinDirectService {
@RabbitHandler
public void receiveMessage(String message){
System.out.println("duanxin Direct-----接收到了订单的信息是:"+message);
}
}
5.主题模式(topic)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-elytWTEG-1624005504843)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210617172903060.png)]
" * " : 表示任何一个词
" # ": 表示0或1个词