一. 概念
定义:
RabbitMQ是流行的开源消息队列系统,用erlang语言开发。RabbitMQ是AMQP(高级消息队列协议)的标准实现。
消息生产与消费机制如下:
常见MQ横向比较:
RabbitMQ:它比kafka成熟,支持AMQP事务处理,在可靠性上,RabbitMq超过kafka,在性能方面超过ActiveMQ;
同时rabbitmq支持回调,也就是客户端可以知道消息的执行情况!
ActiveMQ: 历史悠久的开源项目,已经在很多产品中得到应用,实现了JMS1.1规范,可以和spring-jms轻松融合,实现了多种协议,支持持久化到数据库,对队列数较多的情况支持不好;
Kafka:不支持AMQP事务处理,性能(吞吐量、tps)比RabbitMq要强,可靠性不如rabbitMQ,适合做大数据处理;
故RabbitMQ在一般分布式项目, 或其他有异步处理和模块解耦要求的场景下, 具有降大优势;
常见三种消息模式:
1.direct:指定将消息发给指定节点
2.fanout:指定将消息发给指定exchanger. 该exchanger将消息分发给绑定在其上的所有的queue
3.topic:消息都会被转发到所有关心RouteKey中指定话题的queue上
二. 安装:
linux和windows下均可安装, 本博安装在windows下,安装较为简单, 不作介绍(注意需要先安装erlang语言环境);
常见bug解决:
1.RabbitMQ学习--“java.lang.NoSuchMethodError: com.rabbitmq.client.ConnectionFactory.newConnection”问题解决
2.java.lang.NoSuchMethodError: com.rabbitmq.client.ConnectionFactory.newConnection(Ljava/util/concurrent/ExecutorService;Ljava/lang/String;)Lcom/rabbitmq/client/Connection
解决办法: 引入spring-rabbit包后不可再引入amqp-client, 否则引发包冲突;
在成功安装后, 到安装目录打开cmd命令行, 如下依次:
1.开启rabbitmq服务
2.创建虚拟机 test_vhost
3.创建用户 设置密码 root root
4.将用户设置为管理员
5.为用户赋权 操作相关虚拟机
D:\rabbitMQ\rabbitmq_server-3.7.4\sbin>rabbitmq-service.bat start
The requested service has already been started.
D:\rabbitMQ\rabbitmq_server-3.7.4\sbin>rabbitmqctl add_vhost test_vhost
Adding vhost "test_vhost" ...
D:\rabbitMQ\rabbitmq_server-3.7.4\sbin>rabbitmqctl add_user root root
Adding user "root" ...
D:\rabbitMQ\rabbitmq_server-3.7.4\sbin>rabbitmqctl set_user_tags root administrator
Setting tags for user "root" to [administrator] ...
D:\rabbitMQ\rabbitmq_server-3.7.4\sbin>rabbitmqctl set_permissions -p test_vhost root ".*" ".*" ".*"
Setting permissions for user "root" in vhost "test_vhost" ...
三. 开始整合:
创建maven项目, 并引入依赖如下:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
<version>1.7.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
编写消费者代码(类似可以编写多个, 不再重复):
package cn.com.bitic.rabbitmq.listener;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageListener;
import java.io.UnsupportedEncodingException;
public class DirectMessageListener implements MessageListener {
public void onMessage(Message message) {
try {
String s = new String(message.getBody(), "UTF-8");//body为字节数组, 所以需要解码
System.out.println(s);
} catch (Exception ignore) { }
}
}
编写配置文件spring_rabbitmq.xml:
1.头文件
<?xml version="1.0" encoding="UTF-8"?>
<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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/rabbit
http://www.springframework.org/schema/rabbit/spring-rabbit-1.2.xsd">
</bean>
2.添加连接工厂配置 注: 因rabbitConnectionFactory为默认工厂名称, 所以在消费者和生产者中都不需要再定义连接工厂引用
<!--配置connection-factory,指定连接rabbit server参数 -->
<rabbit:connection-factory id="rabbitConnectionFactory" virtual-host="test_vhost"
username="root" password="root" host="127.0.0.1" port="5672"/>
3.添加自动创建queue和exchanger的配置
<!--通过指定下面的admin信息,当前producer中的exchange和queue会在rabbitmq服务器上自动生成 -->
<rabbit:admin id="connectAdmin" connection-factory="rabbitConnectionFactory" />
4.配置消息队列
<rabbit:queue name="queue_1" declared-by="connectAdmin" />
5.配置路由器exchanger direct模式, 并绑定queue_1
<!-- 定义direct exchange,绑定queue_1 -->
<rabbit:direct-exchange name="exchange_1" declared-by="connectAdmin">
<rabbit:bindings>
<rabbit:binding queue="queue_1"/>
</rabbit:bindings>
</rabbit:direct-exchange>
6.定义rabbotTemplate
<!--定义rabbit template用于数据的接收和发送 -->
<rabbit:template id="amqpTemplate" connection-factory="rabbitConnectionFactory" exchange="exchange_1" />
7.因为在一个项目中演示, 所以继续配置消费者
<!-- 消息接收者 -->
<bean id="messageReceiver" class="cn.com.bitic.rabbitmq.listener.DirectMessageListener"/>
<!-- queue litener 观察 监听模式 当有消息到达时会通知监听在对应的队列上的监听对象 -->
<rabbit:listener-container>
<rabbit:listener queues="queue_1" ref="messageReceiver" />
</rabbit:listener-container>
applicationContext.xml配置:
<?xml version="1.0" encoding="UTF-8"?>
<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:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--引入配置文件-->
<import resource="classpath:spring_rabbitmq.xml"/>
</beans>
基于如上,便配置完基本的direct模式的配置,测试代码如下:
package test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.annotation.Resource;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class RabbitTest01 {
@Resource(name = "amqpTemplate")
private AmqpTemplate amqpTemplate;
@Resource(name = "rabbitTemplate")
private AmqpTemplate rabbitTemplate;
@Test
public void test01() throws InterruptedException {
amqpTemplate.convertAndSend("queue_1_key","hello");
rabbitTemplate.convertAndSend("文维");
Thread.sleep(5000L);
}
}
基于以上, 已经正确演示direct消息的发送和消费, 实际项目中可以基于以上逻辑, 实现相关业务;
继续演示fanout模式消息:
在spring_rabbitmq.xml文件中添加配置:
生产者 基于direct消息的配置, 可以理解如下:
<rabbit:queue name="queue_2" declared-by="connectAdmin"/>
<rabbit:queue name="queue_3" declared-by="connectAdmin"/>
<rabbit:fanout-exchange name="exchange_2" declared-by="connectAdmin">
<rabbit:bindings>
<rabbit:binding queue="queue_2"/>
<rabbit:binding queue="queue_3"/>
</rabbit:bindings>
</rabbit:fanout-exchange>
<rabbit:template id="rabbitTemplate" connection-factory="rabbitConnectionFactory" exchange="exchange_2"/>
消费者
<!--定义监听器-->
<bean id="messageListener_2" class="cn.com.bitic.rabbitmq.listener.FanoutMessageListener"/>
<rabbit:listener-container>
<rabbit:listener queues="queue_2" ref="messageListener_2"/>
</rabbit:listener-container>
<!--定义监听器-->
<bean id="messageListener_3" class="cn.com.bitic.rabbitmq.listener.FanoutMessageListener_2"/>
<rabbit:listener-container>
<rabbit:listener queues="queue_3" ref="messageListener_3"/>
</rabbit:listener-container>
在测试代码中添加消息发布:
rabbitTemplate.convertAndSend("文维");
基于以上, 已经正确演示fanout消息的发送和消费;
继续演示topic模式消息:
topic消息特点是,可以基于routingkey定义在指定exchanger下哪些queue接收到消息:
在配置文件spring_rabbitmq.xml中:
<rabbit:template id="rabbitTemplate" connection-factory="rabbitConnectionFactory" exchange="exchange_2"/>
<rabbit:queue name="queue_4" declared-by="connectAdmin"/>
<rabbit:queue name="queue_5" declared-by="connectAdmin"/>
<rabbit:queue name="queue_6" declared-by="connectAdmin"/>
<rabbit:topic-exchange name="exchange_topic">
<rabbit:bindings>
<rabbit:binding queue="queue_4" pattern="*.*.test"/>
<rabbit:binding queue="queue_5" pattern="com.bitic.test"/>
<rabbit:binding queue="queue_6" pattern="*.bitic.test"/>
</rabbit:bindings>
</rabbit:topic-exchange>
<rabbit:template id="rabbitTemplate_topic" connection-factory="rabbitConnectionFactory" exchange="exchange_topic"/>
消费者:
<!--定义监听器-->
<bean id="messageListener_4" class="cn.com.bitic.rabbitmq.listener.FanoutMessageListener_4"/>
<rabbit:listener-container>
<rabbit:listener queues="queue_4" ref="messageListener_4"/>
</rabbit:listener-container>
<!--定义监听器-->
<bean id="messageListener_5" class="cn.com.bitic.rabbitmq.listener.FanoutMessageListener_5"/>
<rabbit:listener-container>
<rabbit:listener queues="queue_5" ref="messageListener_5"/>
</rabbit:listener-container>
<!--定义监听器-->
<bean id="messageListener_6" class="cn.com.bitic.rabbitmq.listener.FanoutMessageListener_6"/>
<rabbit:listener-container>
<rabbit:listener queues="queue_6" ref="messageListener_6"/>
</rabbit:listener-container>
在测试类添加如下测试代码 分别观察执行结果:
// rabbitTemplate_topic.convertAndSend("exchange_topic","com.bitic.test","Topic模式");
// rabbitTemplate_topic.convertAndSend("exchange_topic","cn.bitic.test","Topic模式");
rabbitTemplate_topic.convertAndSend("exchange_topic","cn.bitic_1.test","Topic模式");
以上就是RabbitMQ的基本概念和与spring整合的基本操作;
我们要始终明白使用消息中间件的目的:
1.模块解耦, 各个模块各司其职, 避免业务耦合;
应用场景:商品上下架时,可以将Solr索引库的同步请求发送到消息队列, 让搜索微服务自己去同步Solr库, 而商品模块可以直接认为商品同步成功, 并且此处的Solr索引同步延迟是可以接受的;
2.异步处理,将不需要同步处理的业务进行异步处理, 提高接口响应速度;
典型场景,后端生成验证码之后不必等待短信系统发送完再将结果返回, 当提交给短信发送微服务后即可认为发送成功, 故可直接通知用户短信已发送;
3.削峰填谷, 均衡整个系统的负载,:
rabbitmq是默认单线程消费某个队列的消息, 也可以通过设置, 进行并发消费,总之其实可控的,避免高并发下的系统假死;
可以设置最大消息数, 当超过时直接丢弃, 保证系统不至于过载;
应用场景:秒杀;
----------------------------------------------------------------------------完--------------------------------------------------------------------------------------------
作者:划船一哥
来源:CSDN
https://me.csdn.net/weixin_42711325
版权声明:本文为博主原创文章,转载请附上博文链接!