MQ消息队列:负责数据的传接受,存储和传递。性能要过于普通服务和技术
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lW0WwS9x-1659964424459)(C:\Users\OMEN\AppData\Roaming\Typora\typora-user-images\image-20220716111756495.png)]
为什么消息中间件不直接使用http协议呢
- 因为http请求报文头和响应报文头是比较复杂的,包含了cookie,数据的加密解密,状态码,响应码等附加的功能,但是对于一个消息而言,我们并不需要这么复杂,也没有这个必要性,它其实就是负责数据传递,存储,分发就行,一定要追求的是高性能。尽量简洁,快速。
- 大部分情况下http大部分都是短链接,在实际的交互过程中,一个请求到响应很有可能会中断,中断以后就不会就行持久化,就会造成请求的丢失。这样就不利于消息中间件的业务场景,因为消息中间件可能是一个长期的获取消息的过程,出现问题和故障要对数据或消息就行持久化等,目的是为了保证消息和数据的高可靠和稳健的运行。
协议
AMQP协议
AMQP:(全称: Advanced Message Queuing Protocol)是高级消息队列协议。由摩根大通集团联合其他公司共同设计。是一个提供统一消息服务的应用层标准高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,并不受客户端/中间件不同产品,不同的开发语言等条件的限制。Erlang中的实现有RabbitMQ等。
特性
- 分布式事务支持
- 消息的持久化支持
- 高性能和高可靠的消息处理优势。
MQTT协议
MQTT协议: (Message Queueing Telemetry Transport)消息队列是IBM开放的一个即时通讯协议,物联网系统架构中的重要组成部分。
特点:
- 轻量
- 结构简单
- 传输快,不支持事务
- 没有持久化设计。
应用场景:
-
适用于计算能力有限
-
低带宽
-
网络不稳定的场景。
Kafka协议
Kafka协议是基于TCP/IP的二进制协议。消息内部是通过长度来分割,由一些基本数据类型组成。
特点:
- 结构简单
- 解析速度快
- 无事务支持
- 有持久化设计
持久化方式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SQqSH9EB-1659964424461)(C:\Users\OMEN\AppData\Roaming\Typora\typora-user-images\image-20220716112854360.png)]
消息的分发策略机制
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-a32rGb95-1659964424462)(C:\Users\OMEN\AppData\Roaming\Typora\typora-user-images\image-20220716113022608.png)]
安装
第一步下载erlang
1.下载erlang的gz包,tar -zxvf 解压erlang包
2.进入到erlang目录下,使用make install安装
报错解决方式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bKt3nJLL-1659964424463)(C:\Users\OMEN\AppData\Roaming\Typora\typora-user-images\image-20220716132130091.png)]
解决方法:
yum install gcc-c++
错误:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KzkClLDL-1659964424463)(C:\Users\OMEN\AppData\Roaming\Typora\typora-user-images\image-20220716132154446.png)]
解决方法:
yum install perl
错误:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ThDiO5De-1659964424464)(C:\Users\OMEN\AppData\Roaming\Typora\typora-user-images\image-20220716132216606.png)]
解决方法:
yum -y install ncurses-devel
然后执行一次make编译
最后配置环境变量
erl -version查看是否安装上
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2o7go2CQ-1659964424465)(C:\Users\OMEN\AppData\Roaming\Typora\typora-user-images\image-20220716132343286.png)]
npm方式推荐
rpm -Uvh erlang-23.2.7-2.el8.x86_64.rpm
yum install -y erlang
erl -v 查看版本
第二步下载socat
yum install -y socat
第三步下载rabbitmq
rpm -Uvh rabbitmq-server-3.8.14-1.el8.noarch.rpm
yum install -y rabbitmq-server
启动 rabbitmq :systemctl start rabbitmq-server
查看 rabbitmq状态 : systemctl status rabbitmq-server
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VDEznyNb-1659964424466)(C:\Users\OMEN\AppData\Roaming\Typora\typora-user-images\image-20220716175716625.png)]
在/etc文件下使用rabbitmq-plugins enable rabbitmq_management开启web端访问
添加用户
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-syZzW4fu-1659964424467)(C:\Users\OMEN\AppData\Roaming\Typora\typora-user-images\image-20220716181120330.png)]
-
rabbitmqctl add_user 用户名 密码
-
set_user_tags 用户名 权限 设置用户权限
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ChK8708c-1659964424467)(C:\Users\OMEN\AppData\Roaming\Typora\typora-user-images\image-20220716181303859.png)]
change_password 用户 新密码
- list_users 用户列表
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T1TuHUKR-1659964424468)(C:\Users\OMEN\AppData\Roaming\Typora\typora-user-images\image-20220716184317302.png)]
- 给用户赋予访问权限
rabbitmqctl set_permissions -p / admin ‘.’ '.’ ‘.*’
给admin用户赋予‘/’目录的访问权限不然后续操作报错比如以下问题
com.rabbitmq.client.ShutdownSignalException: connection error;
java示例代码
依赖
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.10.0</version>
</dependency>
public class Producer extends Thread{
@Override
public void run() {
// 创建连接工程
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("tencent");
connectionFactory.setPort(5672);
connectionFactory.setUsername("admin");
connectionFactory.setPassword("admin");
connectionFactory.setVirtualHost("/");//消息放在根结点上
// 创建连接Connection
Connection connection = null;
// 通过连接创建通道Channel
Channel channel = null;
try {
connection = connectionFactory.newConnection("生产者");
channel = connection.createChannel();
// 通过通道创建交换机,声明队列,绑定关系,路由key,发送消息和接收消息
final String EXCHANGE_NAME = "testfanout";
//创建交换机
/**
* 1.exchange:交换机名称
* 2.type:交换机的类型
* DIRECT("direct"):定向
* FANOUT("fanout"):广播
* TOPIC("topic"):通配符方式
* HEADERS("headers"):参数匹配
* 3.durable:是否持久化
* 4.autoDelete:自动删除
* 5.internal:内部使用:false
* 6.arguments:参数
*/
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.FANOUT,false,false,false,null);
String queueName = "queue6";
String queueName2 = "queue7";
/**
* @param1 队列的名称
* @param2 是否持久化 false,消息是否存盘
* @param3 排他性,是否独占独立
* @param4 是否自动删除,随着最后一个消费者消息完毕信息以后是否把队列自动删除
* @param5 携带附属参数
*/
channel.queueDeclare(queueName,true,false,false,null);
channel.queueDeclare(queueName2,true,false,false,null);
/**
*
* 绑定交换机和队列的关系
* String queue, String exchange, String routingKey
* 参数
* 1.queue:队列名称
* 2.exchange:交换机名称
* 3.routingKey:路由键:绑定规则
* 如果交换机的类型是FANOUT,那么路由键为空
*/
channel.queueBind(queueName,EXCHANGE_NAME,"");
channel.queueBind(queueName2,EXCHANGE_NAME,"");
// 准备消息内容
String message = "hello rabbitmq";
// 发送消息给队列queue
/**
* @param1 交换机
* @param2 队列,路由
* @param3 消息的控制状态
* @param4 消息主题
* 不可能存在没有交换机的队列,虽然没有指定交换机但是一定会存在一个默认的交换机
*/
channel.basicPublish(EXCHANGE_NAME,"", null,message.getBytes());
System.out.println("消息发送成功");
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
} finally {
// 关闭通道
if(channel!=null && channel.isOpen()){
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
// 关闭连接
if(connection!=null && connection.isOpen()){
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
new Thread(new Producer()).start();
new Thread(new Producer()).start();
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v8jUKYuC-1659964424468)(C:\Users\OMEN\AppData\Roaming\Typora\typora-user-images\image-20220719203629453.png)]
Rabbitmq核心组成部分
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Bh9tfEBL-1659964424469)(C:\Users\OMEN\AppData\Roaming\Typora\typora-user-images\image-20220719210243636.png)]
- Server:又称Broker ,接受客户端的连接,实现AMQP实体服务。安装rabbitmq-server
- Connection:连接,应用程序与Broker的网络连接TCP/IP/三次握手和四次挥手
- Channel:网络信道,几乎所有的操作都在Channel中进行,Channel是进行消息读写的通道,客户可以建立对各Channel,每个Channel代表一个会话任务。
- Message :消息:服务与应用程序之间传送的数据,由Properties和body组成,Properties可是对消息进行修饰,比如消息的优先级,延迟等高级特性,Body则就是消息体的内容。
- Virtual Host 虚拟地址,用于进行逻辑隔离,最上层的消息路由,一个虚拟主机理由可以有若千个Exhange和Queueu,同一个虚拟主机里面不能有相同名字的Exchange
- Exchange:交换机,接受消息,根据路由键发送消息到绑定的队列。(不具备消息存储的能力)Bindings: Exchange和Queue之间的虚拟连接,binding中可以保护多个routing key.
- Routing key:是一个路由规则,虚拟机可以用它来确定如何路由一个特定消息。
- Queue:队列:也成为Message Queue,消息队列,保存消息并将它们转发给消费者。
运行流程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K0bRxQGo-1659964424469)(C:\Users\OMEN\AppData\Roaming\Typora\typora-user-images\image-20220720172116371.png)]
Spring整合RabbitMq
xml方式
生产者配置
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rabbit="http://www.springframework.org/schema/rabbit"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/rabbit http://www.springframework.org/schema/rabbit/spring-rabbit.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder location="classpath:rabbitmq.properties"/>
<!-- ${}获取配置 -->
<!-- 定义rabbitmq connectionFactory -->
<rabbit:connection-factory id="connectionFactory" host="${rabbitmq.host}"
port="${rabbitmq.port}"
username="${rabbitmq.username}"
password="${rabbitmq.password}"
virtual-host="${rabbitmq.virtual-host}"/>
<!-- 加上这个配置之后,如果交换机不存在,可以直接通过代码中创建交换机 -->
<!--定义管理交换机、队列-->
<rabbit:admin connection-factory="connectionFactory"/>
<!--定义持久化队列,不存在则自动创建;不绑定到交换机则绑定到默认交换机
默认交换机类型为direct,名字为:"",路由键为队列的名称
-->
<!--
id:bean的名称
name:queue的名称
auto-declare:自动创建
auto-delete:自动删除。 最后一个消费者和该队列断开连接后,自动删除队列
durable:是否持久化
-->
<rabbit:queue id="spring_queue" name="spring_queue" auto-declare="true"/>
<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~广播;所有队列都能收到消息~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<!--定义广播交换机中的持久化队列,不存在则自动创建-->
<rabbit:queue id="spring_fanout_queue_1" name="spring_fanout_queue_1" auto-declare="true" />
<!--定义广播交换机中的持久化队列,不存在则自动创建-->
<rabbit:queue id="spring_fanout_queue_2" name="spring_fanout_queue_2" auto-declare="true"/>
<!--定义广播类型交换机;并绑定上述两个队列-->
<rabbit:fanout-exchange id="spring_fanout_exchange" name="spring_fanout_exchange" auto-declare="true">
<rabbit:bindings>
<rabbit:binding queue="spring_fanout_queue_1" />
<rabbit:binding queue="spring_fanout_queue_2"/>
</rabbit:bindings>
</rabbit:fanout-exchange>
<!-- 定义队列-->
<rabbit:queue id="spring_direct_queue" name="spring_direct_queue" auto-declare="true"/>
<!--
定义 Routing 路由模式 交互机
-->
<rabbit:direct-exchange name="spring_direct_exchange" >
<rabbit:bindings>
<!--direct 类型的交换机绑定队列 key :路由key queue:队列名称-->
<rabbit:binding queue="spring_direct_queue" key="info"></rabbit:binding>
</rabbit:bindings>
</rabbit:direct-exchange>
<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~通配符;*匹配一个单词,#匹配多个单词 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<!--定义广播交换机中的持久化队列,不存在则自动创建-->
<rabbit:queue id="spring_topic_queue_star" name="spring_topic_queue_star" auto-declare="true"/>
<!--定义广播交换机中的持久化队列,不存在则自动创建-->
<rabbit:queue id="spring_topic_queue_well" name="spring_topic_queue_well" auto-declare="true"/>
<!--
声明 topic 类型的交换机
-->
<rabbit:topic-exchange id="spring_topic_exchange" name="spring_topic_exchange" auto-declare="true">
<rabbit:bindings>
<rabbit:binding pattern="com.*" queue="spring_topic_queue_star"/>
<rabbit:binding pattern="wjh.#" queue="spring_topic_queue_well"/>
</rabbit:bindings>
</rabbit:topic-exchange>
<!--定义rabbitTemplate对象操作可以在代码中方便发送消息-->
<rabbit:template id="rabbitTemplate" connection-factory="connectionFactory"/>
</beans>
消费者配置
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:rabbit="http://www.springframework.org/schema/rabbit"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/rabbit http://www.springframework.org/schema/rabbit/spring-rabbit.xsd">
<context:property-placeholder location="classpath:rabbitmq.properties"/>
<!-- ${}获取配置 -->
<!-- 定义rabbitmq connectionFactory -->
<rabbit:connection-factory id="connectionFactory" host="${rabbitmq.host}" port="${rabbitmq.port}" username="${rabbitmq.username}"
password="${rabbitmq.password}" virtual-host="${rabbitmq.virtual-host}"/>
<bean id = "springQueueListener" class="com.wjh.rabbitmq.Listener"/>
<rabbit:listener-container connection-factory="connectionFactory" >
<rabbit:listener queue-names="spring_queue" ref="springQueueListener"/>
</rabbit:listener-container>
</beans>
生产者代码
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
public void shouldAnswerWithTrue() throws JsonProcessingException {
ObjectMapper objectMapper =new ObjectMapper();
Student s = new Student();
s.setAge(1);
s.setName("wjh");
String msg = objectMapper.writeValueAsString(s);
rabbitTemplate.convertAndSend("spring_queue",msg);//往队列里面发送消息
}
消费者代码
public class Listener implements MessageListener {
@Override
public void onMessage(Message message) {
//打印消息
ObjectMapper objectMapper = new ObjectMapper();
try {
Student student = objectMapper.readValue(message.getBody(),Student.class);
System.out.println(student);
} catch (IOException e) {
e.printStackTrace();
}
}
}
boot整合RabbitMq
生产者配置
spring:
rabbitmq:
host: tencent
port: 5672
username: admin
password: admin
virtual-host: /
//开启手动应答,回调函数
publisher-confirm-type: correlated
publisher-returns: true
Exchange:
topicName: boot_Exchange
Queue:
name: boot_Queue
消费者配置
spring:
rabbitmq:
host: tencent
port: 5672
username: admin
password: admin
virtual-host: /
Exchange:
topicName: boot_Exchange
Queue:
name: boot_Queue
spring.rabbitmq.listener.simple.auto-startup=true: 是否启动时自动启动容器
spring.rabbitmq.listener.simple.acknowledge-mode: 表示消息确认方式,其有三种配置方式,分别是none、manual和auto;默认auto
spring.rabbitmq.listener.simple.concurrency: 最小的消费者数量
spring.rabbitmq.listener.simple.max-concurrency: 最大的消费者数量
spring.rabbitmq.listener.simple.prefetch: 一个消费者最多可处理的nack消息数量,如果有事务的话,必须大于等于transaction数量.
spring.rabbitmq.listener.simple.transaction-size: 当ack模式为auto时,一个事务(ack间)处理的消息数量,最好是小于等于prefetch的数量.若大于prefetch, 则prefetch将增加到这个值
spring.rabbitmq.listener.simple.default-requeue-rejected: 决定被拒绝的消息是否重新入队;默认是true(与参数acknowledge-mode有关系)
spring.rabbitmq.listener.simple.missing-queues-fatal=true 若容器声明的队列在代理上不可用,是否失败; 或者运行时一个多多个队列被删除,是否停止容器
spring.rabbitmq.listener.simple.idle-event-interval: 发布空闲容器的时间间隔,单位毫秒
spring.rabbitmq.listener.simple.retry.enabled=false: 监听重试是否可用
spring.rabbitmq.listener.simple.retry.max-attempts=3: 最大重试次数
spring.rabbitmq.listener.simple.retry.max-interval=10000ms: 最大重试时间间隔
spring.rabbitmq.listener.simple.retry.initial-interval=1000ms:第一次和第二次尝试传递消息的时间间隔
spring.rabbitmq.listener.simple.retry.multiplier=1: 应用于上一重试间隔的乘数
spring.rabbitmq.listener.simple.retry.stateless=true: 重试时有状态or无状态
spring.rabbitmq.listener.direct.acknowledge-mode= ack模式
spring.rabbitmq.listener.direct.auto-startup=true 是否在启动时自动启动容器
spring.rabbitmq.listener.direct.consumers-per-queue= 每个队列消费者数量.
spring.rabbitmq.listener.direct.default-requeue-rejected= 默认是否将拒绝传送的消息重新入队.
spring.rabbitmq.listener.direct.idle-event-interval= 空闲容器事件发布时间间隔.
spring.rabbitmq.listener.direct.missing-queues-fatal=false若容器声明的队列在代理上不可用,是否失败.
spring.rabbitmq.listener.direct.prefetch= 每个消费者可最大处理的nack消息数量.
spring.rabbitmq.listener.direct.retry.enabled=false 是否启用发布重试机制.
TTL
整个队列消息的存活时间
@Bean("bootQueue")
public Queue bootQueue(){
return QueueBuilder.durable(Queue_Name).ttl(30000).build();//设置ttl,单位ms
}
单个消息的存活时间
MessagePostProcessor messagePostProcessor = new MessagePostProcessor() {
@Override
public Message postProcessMessage(Message message) throws AmqpException {
message.getMessageProperties().setExpiration("3000");//消息过期时间
return message;
}
};
rabbitTemplate.convertAndSend(Exchange_Name,"boot.haha","boot mq hello",messagePostProcessor);
两者都设置了时间以时间短的为准
n(“bootQueue”)
public Queue bootQueue(){
return QueueBuilder.durable(Queue_Name).ttl(30000).build();//设置ttl,单位ms
}
单个消息的存活时间
```java
MessagePostProcessor messagePostProcessor = new MessagePostProcessor() {
@Override
public Message postProcessMessage(Message message) throws AmqpException {
message.getMessageProperties().setExpiration("3000");//消息过期时间
return message;
}
};
rabbitTemplate.convertAndSend(Exchange_Name,"boot.haha","boot mq hello",messagePostProcessor);
两者都设置了时间以时间短的为准