MQ的基本概念
MQ全程Message Queue(消息队列),是在消息的传输过程中保存消息的容器。多用于分布式系统之间进行通信。
小结:
(1)MQ,消息队列,存储消息的中间件
(2)分布式系统通信两种方式:直接远程调用和借助第三方完成通信,MQ属于第二种。
(3)发送方称为生产者,接收方称为消费者。
MQ的优劣势:
优势:异步+削峰+解耦
(1)异步-提速:【提升用户体验(响应速度高)和系统吞吐量(单位时间内系统处理的请求数目)】
同步的结果,耗时时间长。920ms
引入MQ的异步提速:25ms
(2)解耦-应用:耦合性低,提供了容错性,可维护性就变高。
库存系统调用失败,订单系统也会失败,整个系统的容错性低。
如果再在订单链路上增加一个x系统,那么就只能修改订单系统的代码,再去访问x系统。以此往复,订单系统的可维护性低。
如果进行解耦:
订单系统一旦进行了下单,直接存放到MQ当中,即可返回成功。这是达到了异步的目的。
如果库存系统失败,则不会影响到订单系统,库存系统在短时间内修复好再从MQ中取得数据,则不会影响最终结果。
如果再加入了X提供,只需要在X系统中订阅MQ的消息即可,和订单系统没有任何关系,提高了可维护性。
(3)削峰填谷:提高了系统的稳定性
未使用MQ的高并发后果
使用MQ进行削峰:
使用了MQ之后,限制消费消息的速度为1000,这样一来,高峰期所产生的数据势必会积压到MQ中,高峰被削掉了,但是因为消息积压,在高峰期过后的一段时间之内,消费消息的速度还会维持在1000,直到消费完毕所有积压的消息,这就叫做填谷。
劣势:可用性+复杂性+一致性
(1)系统可用性降低
系统引入的外部依赖越多,系统的稳定性就越差,一旦MQ宕机,就会对业务造成影响。如何保证MQ的高可用????
(2)系统复杂度提高
MQ的引入大大增加了系统的复杂度,以前是系统间同步的远程调用,现在是通过MQ的异步调用。如何保证消息没有重复消费,怎么处理消息丢失的情况?怎么保证消息传递的顺序性,幂等性是啥==一致性。
(3)一致性问题
A系统处理完成业务,通过MQ给BCD三个系统发送消息数据,如果B系统、C系统处理成功,D系统处理失败,那么如何保证消息处理的一致性?
小结:使用MQ的条件
既然MQ有优点和缺点,那么满足什么样的条件才能够去使用MQ呢?
(1)生产者不需要从消费者处获得反馈。引入消息队列之前的直接调用,其接口的返回值应该为空,这才让明明下层的动作还没有做,上层却当做动作一定做完了继续往后走,即所谓的异步成为了可能。
(2)允许短暂的不一致性。
(3)确实感知到了效果。即解耦合,异步,削峰这方面的收益要大于管理MQ的成本。
常见的MQ的产品
RabbitMQ | ActiveMQ | RocketMQ | Kafka | |
---|---|---|---|---|
公司/社区 | rabbit | apache | alibaba | apache |
开发语言 | Erlang | java | java | scale&java |
协议支持 | AMQP,XMPP,SMTP,STOMP | OpenWire,STOMP,REST,XMPP,AMQP | 自定义 | 自定义协议,社区封装了http协议支持 |
客户端支持语言 | 官方支持Erlang、java、Ruby;社区几乎支持所有语言 | java,c,c++,Python,PHP,Perl,.nel | java,C++(不成熟) | 官方支持java,社区PHP+Python |
单击吞吐量 | 万级(其次) | 万级(最差) | 十万级(最好) | 十万级(次之) |
消息延迟 | 微秒级别 | 毫秒级别 | 毫秒级别 | 毫秒以内 |
功能特性 | 并发能力强,性能及其好,延时低,社区活跃,管理界面丰富 | 老牌产品,成熟度高,文档多 | MQ功能比较完备,扩展性强 | 只支持主要的MQ功能,毕竟是为了大数据领域做准备的。 |
RabbitMQ简介
AMQP,即Advanced Message Queuing Protocol(高级消息队列协议),是一个网络协议,是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议的客户端和消息中间件可以传递消息,并不受客户端/中间件不同产品,不同开发语言等条件限制。2006年,AMQP规范发布,类比HTTP。
2007年,Rabbit公司基于AMQP标准开发的RabbitMQ1.0发布,RabbitMQ采用Erlang语言开发,Erlang语言由Ericson设计,专门为开发高并发和分布式系统的一种语言,在电信领域使用广泛。
rabbitMQ的架构图
Broker:中介;作为RabbitMQ的服务端,接收和分发消息的应用,RabbitMQ Server就是Message Broker。
producer:客户端;
consumer:客户端;
connection:客户端和服务端建立tcp连接;
因为每进行一次通信都要建立一次TCP连接,浪费资源,所以connection里边有很多的channel,通过channel这种比较小的connection来完成通信,节约资源。
channel是在connection内部建立的逻辑连接,如果应用程序支持多线程,通常每个thread创建单独的channel进行通讯,AMQP method包含了channel id帮助客户端和message broker识别channel,所以channel之间是完全隔离的。channel作为轻量级的connection极大减少了操作系统为了建立tcp连接的开销。
virtual host:虚拟机,在server里边有很多的虚拟机,理解在为mysql里边可以有很多的数据库,在数据库与数据库之间是相互隔离的。
在每个虚拟机里边都有很多exchange和queue,
exchange:交换机,message到达broker的第一站,根据分发规则,匹配查询表中的routing key,分发消息到queue中去,常用的类型有direct(point-to-point),topic(publish-subscribe) and fanout (multicast)。交换机可以绑定到不同的队列上,就有很多的队列了,至于绑定到哪个队列上,取决于binding的过程。
Queue:消息最终被送到这里,等待consumer取走。
Binding:exchange和queue之间的虚拟连接,binding中可以包含routing key,Binding信息被保存到exchange中的查询表中,用于Message的分发依据。
RabbitMQ提供了集中工作模式(生产消息 和消费消息的一种方式)
简答模式:
work queues:
publish/subscribe发布与定于模式:
Routing路由模式:
Topic主题模式:
RPC远程调用模式(远程调用,不太算MQ)
官网介绍:RabbitMQ Tutorials — RabbitMQ
RabbitMQ的安装和配置
1、在线安装依赖环境
yum install build-essential openssl openssl-devel unixODBC unixODBC-devel make gcc gcc-c++ kernel-devel m4 ncurses-devel tk tc xz
2、安装Erlang,socat和rabbitmq
erlang-18.3-1.el7.centos.x86_64.rpm socat-1.7.3.2-5.el7.lux.x86_64.rpm rabbitmq-server-3.6.5-1.noarch.rpm
rpm -ivh erlang-18.3-1.el7.centos.x86_64.rpm rpm -ivh socat-1.7.3.2-1.1.el7.x86_64.rpm rpm -ivh rabbitmq-server-3.6.5-1.noarch.rpm
3、该配置,启动
# 开启管理界面 rabbitmq-plugins enable rabbitmq_management # 修改默认配置信息 vim /usr/lib/rabbitmq/lib/rabbitmq_server-3.6.5/ebin/rabbit.app # 比如修改密码、配置等等,例如:loopback_users 中的 <<"guest">>,只保留guest
service rabbitmq-server start # 启动服务 service rabbitmq-server stop # 停止服务 service rabbitmq-server restart # 重启服务
设置配置文件
cd /usr/share/doc/rabbitmq-server-3.6.5/ cp rabbitmq.config.example /etc/rabbitmq/rabbitmq.config
rabbitMQ安装踩坑:
最终结果:
至于 config file 的not found
则使用
cd /usr/share/doc/rabbitmq-server-3.6.5/ cp rabbitmq.config.example /etc/rabbitmq/rabbitmq.config
RabbitMQ的快速入门
dependencies> <!--rabbitmq java 客户端--> <dependency> <groupId>com.rabbitmq</groupId> <artifactId>amqp-client</artifactId> <version>5.6.0</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.0</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build>
package com.pshdhx; import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import com.rabbitmq.client.ConnectionFactory; import java.io.IOException; import java.util.concurrent.TimeoutException; /** * @Author pshdhx * @Description 生产者主类 * @Date 2022/5/19 0019 14:46 */ public class producerMain { public static void main(String[] args) throws IOException, TimeoutException { //1.创建连接工厂 ConnectionFactory factory = new ConnectionFactory(); //2. 设置参数 factory.setHost("192.168.48.128");//ip 默认值 localhost factory.setPort(5672); //端口 默认值 5672 factory.setVirtualHost("/pshdhx_virtual_hosts");//虚拟机 默认值/ factory.setUsername("csdn");//用户名 默认 guest factory.setPassword("pshdhx");//密码 默认值 guest //3. 创建连接 Connection Connection connection = factory.newConnection(); //4. 创建Channel Channel channel = connection.createChannel(); //5. 创建队列Queue /* queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments) 参数: 1. queue:队列名称 2. durable:是否持久化,当mq重启之后,还在 3. exclusive: * 是否独占。只能有一个消费者监听这队列 * 当Connection关闭时,是否删除队列 * 4. autoDelete:是否自动删除。当没有Consumer时,自动删除掉 5. arguments:参数。 */ //如果没有一个名字叫hello_world的队列,则会创建该队列,如果有则不会创建 channel.queueDeclare("hello_world",true,false,false,null); /* basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body) 参数: 1. exchange:交换机名称。简单模式下交换机会使用默认的 "" 2. routingKey:路由名称 3. props:配置信息 4. body:发送消息数据 */ String body = "hello rabbitmq~~~"; //6. 发送消息 channel.basicPublish("","hello_world",null,body.getBytes()); //7.释放资源 channel.close(); connection.close(); } }
消费者代码
package com.pshdhx; import com.rabbitmq.client.*; import java.io.IOException; import java.util.concurrent.TimeoutException; /** * @Author pshdhx * @Description 消费者主类 * @Date 2022/5/19 0019 14:46 */ public class consumerMain { public static void main(String[] args) throws IOException, TimeoutException { //1.创建连接工厂 ConnectionFactory factory = new ConnectionFactory(); //2. 设置参数 factory.setHost("192.168.48.128");//ip 默认值 localhost factory.setPort(5672); //端口 默认值 5672 factory.setVirtualHost("/pshdhx_virtual_hosts");//虚拟机 默认值/ factory.setUsername("csdn");//用户名 默认 guest factory.setPassword("pshdhx");//密码 默认值 guest //3. 创建连接 Connection Connection connection = factory.newConnection(); //4. 创建Channel Channel channel = connection.createChannel(); //5. 创建队列Queue /* queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments) 参数: 1. queue:队列名称 2. durable:是否持久化,当mq重启之后,还在 3. exclusive: * 是否独占。只能有一个消费者监听这队列 * 当Connection关闭时,是否删除队列 * 4. autoDelete:是否自动删除。当没有Consumer时,自动删除掉 5. arguments:参数。 */ //如果没有一个名字叫hello_world的队列,则会创建该队列,如果有则不会创建 channel.queueDeclare("hello_world",true,false,false,null); /* basicConsume(String queue, boolean autoAck, Consumer callback) 参数: 1. queue:队列名称 2. autoAck:是否自动确认 3. callback:回调对象 */ // 接收消息 Consumer consumer = new DefaultConsumer(channel){ /* 回调方法,当收到消息后,会自动执行该方法 1. consumerTag:标识 2. envelope:获取一些信息,交换机,路由key... 3. properties:配置信息 4. body:数据 */ @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { System.out.println("consumerTag:"+consumerTag); System.out.println("Exchange:"+envelope.getExchange()); System.out.println("RoutingKey:"+envelope.getRoutingKey()); System.out.println("properties:"+properties); System.out.println("body:"+new String(body)); } }; channel.basicConsume("hello_world",true,consumer); //关闭资源?不要 } }
以上就是简单工作模式的代码;
RabbitMQ的工作模式
工作队列模式:
c1和c2是竞争关系的。
work queues:与入门的程序相比,多了一个或者多个消费端,多个消费端共同消费同一个队列中的消息。
应用场景:对于任务过重或者是任务较多情况使用工作队列可以提高处理的速度。例如:短信服务部署多个,只需要有一个节点成功发送即可。
C1和C2是循环消费的。
Pub/Sub订阅模式
前面2个案例中,只有3个角色:
-
P:生产者,也就是要发送消息的程序
-
C:消费者:消息的接受者,会一直等待消息到来。
-
queue:消息队列,图中红色部分
而在订阅模型中,多了一个exchange角色,而且过程略有变化:
-
P:生产者,也就是要发送消息的程序,但是不再发送到队列中,而是发给X(交换机)
-
C:消费者,消息的接受者,会一直等待消息到来。
-
Queue:消息队列,接收消息、缓存消息。
-
Exchange:交换机,图中的X。一方面,接收生产者发送的消息。另一方面,知道如何处理消息,例如递交给某个特别队列、递交给所有队列、或是将消息丢弃。到底如何操作,取决于Exchange的类型。Exchange有常见以下3种类型:
-
Fanout:广播,将消息交给所有绑定到交换机的队列
-
Direct:定向,把消息交给符合指定routing key 的队列
-
Topic:通配符,把消息交给符合routing pattern(路由模式) 的队列
-
Exchange(交换机)只负责转发消息,不具备存储消息的能力,因此如果没有任何队列与Exchange绑定,或者没有符合路由规则的队列,那么消息会丢失!
路由模式:
路由模式特点:
-
队列与交换机的绑定,不能是任意绑定了,而是要指定一个
RoutingKey
(路由key) -
消息的发送方在 向 Exchange发送消息时,也必须指定消息的
RoutingKey
。 -
Exchange不再把消息交给每一个绑定的队列,而是根据消息的
Routing Key
进行判断,只有队列的Routingkey
与消息的Routing key
完全一致,才会接收到消息
图解:
-
P:生产者,向Exchange发送消息,发送消息时,会指定一个routing key。
-
X:Exchange(交换机),接收生产者的消息,然后把消息递交给 与routing key完全匹配的队列
-
C1:消费者,其所在队列指定了需要routing key 为 error 的消息
-
C2:消费者,其所在队列指定了需要routing key 为 info、error、warning 的消息
topics通配符模式
*代表一个单词;
#代表一个或者多个单词。【这个牛一点】
Spring整合RabbitMQ
生产者代码:
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>RabbitMQ_Simple_Use</artifactId> <groupId>com.pshdhx</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>spring-rabbitmq-producer</artifactId> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.1.7.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.amqp</groupId> <artifactId>spring-rabbit</artifactId> <version>2.1.8.RELEASE</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.1.7.RELEASE</version> </dependency> </dependencies> </project>
rabbitmq.properties
rabbitmq.host=192.168.48.128 rabbitmq.port=5672 rabbitmq.username=csdn rabbitmq.password=pshdhx rabbitmq.virtual-host=/pshdhx_virtual_hosts
spring-rabbitmq-producer.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: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}"/> <!--定义管理交换机、队列--> <rabbit:admin connection-factory="connectionFactory"/> <!--定义持久化队列,不存在则自动创建;不绑定到交换机则绑定到默认交换机 默认交换机类型为direct,名字为:"",路由键为队列的名称 --> <!-- id:bean的名称 name:queue的名称 auto-declare:自动创建 auto-delete:自动删除。 最后一个消费者和该队列断开连接后,自动删除队列 exclusive:是否独占 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:direct-exchange name="aa" > <rabbit:bindings> <!–direct 类型的交换机绑定队列 key :路由key queue:队列名称–> <rabbit:binding queue="spring_queue" key="xxx"></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"/> <!--定义广播交换机中的持久化队列,不存在则自动创建--> <rabbit:queue id="spring_topic_queue_well2" name="spring_topic_queue_well2" auto-declare="true"/> <rabbit:topic-exchange id="spring_topic_exchange" name="spring_topic_exchange" auto-declare="true"> <rabbit:bindings> <rabbit:binding pattern="heima.*" queue="spring_topic_queue_star"/> <rabbit:binding pattern="heima.#" queue="spring_topic_queue_well"/> <rabbit:binding pattern="itcast.#" queue="spring_topic_queue_well2"/> </rabbit:bindings> </rabbit:topic-exchange> <!--定义rabbitTemplate对象操作可以在代码中方便发送消息--> <rabbit:template id="rabbitTemplate" connection-factory="connectionFactory"/> </beans>
测试代码:
import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:spring-rabbitmq-producer.xml") public class ProducerTest { //1.注入 RabbitTemplate @Autowired private RabbitTemplate rabbitTemplate; @Test public void testHelloWorld(){ //2.发送消息 rabbitTemplate.convertAndSend("spring_queue","hello world spring...."); } /** * 发送fanout消息 */ @Test public void testFanout(){ //2.发送消息 rabbitTemplate.convertAndSend("spring_fanout_exchange","","spring fanout...."); } /** * 发送topic消息 */ @Test public void testTopics(){ //2.发送消息 rabbitTemplate.convertAndSend("spring_topic_exchange","heima.hehe.haha","spring topic...."); } }
消费者代码:
spring-rabbitmq-consumer.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: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.itheima.rabbitmq.listener.SpringQueueListener"/> <!-- <bean id="fanoutListener1" class="com.itheima.rabbitmq.listener.FanoutListener1"/>--> <!-- <bean id="fanoutListener2" class="com.itheima.rabbitmq.listener.FanoutListener2"/>--> <!-- <bean id="topicListenerStar" class="com.itheima.rabbitmq.listener.TopicListenerStar"/>--> <!-- <bean id="topicListenerWell" class="com.itheima.rabbitmq.listener.TopicListenerWell"/>--> <!-- <bean id="topicListenerWell2" class="com.itheima.rabbitmq.listener.TopicListenerWell2"/>--> <rabbit:listener-container connection-factory="connectionFactory" auto-declare="true"> <rabbit:listener ref="springQueueListener" queue-names="spring_queue"/> <!-- <rabbit:listener ref="fanoutListener1" queue-names="spring_fanout_queue_1"/>--> <!-- <rabbit:listener ref="fanoutListener2" queue-names="spring_fanout_queue_2"/>--> <!-- <rabbit:listener ref="topicListenerStar" queue-names="spring_topic_queue_star"/>--> <!-- <rabbit:listener ref="topicListenerWell" queue-names="spring_topic_queue_well"/>--> <!-- <rabbit:listener ref="topicListenerWell2" queue-names="spring_topic_queue_well2"/>--> </rabbit:listener-container> </beans>
rabbitmq.properties不变,和生产者一样。
消费者监听代码,一启动就可以获取到生产者的消息
package com.itheima.rabbitmq.listener; import org.springframework.amqp.core.Message; import org.springframework.amqp.core.MessageListener; /** * @Author pshdhx * @Description FanoutListener1.java * @Date 2022/5/20 0020 0:41 */ public class FanoutListener1 implements MessageListener { public void onMessage(Message message) { try { String msg = new String(message.getBody(), "utf-8"); System.out.printf("广播监听器1:接收路由名称为:%s,路由键为:%s,队列名为:%s的消息:%s \n", message.getMessageProperties().getReceivedExchange(), message.getMessageProperties().getReceivedRoutingKey(), message.getMessageProperties().getConsumerQueue(), msg); } catch (Exception e) { e.printStackTrace(); } } }
测试代码:
import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:spring-rabbitmq-consumer.xml") public class ConsumerTest { @Test public void test1(){ boolean flag = true; while (true){ } } }
SpringBoot整合RabbitMQ
生产者代码
项目名称:spring-rabbitmq-producer
<dependencies> <!-- <dependency>--> <!-- <groupId>org.springframework.boot</groupId>--> <!-- <artifactId>spring-boot-starter-parent</artifactId>--> <!-- <version>2.1.4.RELEASE</version>--> <!-- </dependency>--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency> </dependencies>
这是本项目引用,springboot在父项目里边
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.pshdhx</groupId> <artifactId>RabbitMQ_Simple_Use</artifactId> <packaging>pom</packaging> <version>1.0-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.4.RELEASE</version> </parent> <modules> <module>rabbitmq-producer</module> <module>rabbitmq-consumer</module> <module>spring-rabbitmq-consumer</module> <module>spring-rabbitmq-producer</module> <module>springboot-rabbitmq-provider</module> <module>springboot-rabbitmq-consumer</module> </modules> <dependencies> <!--rabbitmq java 客户端--> <dependency> <groupId>com.rabbitmq</groupId> <artifactId>amqp-client</artifactId> <version>5.6.0</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.0</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build> </project>
这是父项目的额所有依赖。
application.yml配置文件:
spring: rabbitmq: host: 192.168.48.128 port: 5672 virtual-host: /pshdhx_virtual_hosts username: csdn password: pshdhx
配置文件
package com.provider.com.config; import org.springframework.amqp.core.*; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @Author pshdhx * @Description 生产者配置类 * @Date 2022/5/20 0020 9:13 */ @Configuration public class ProviderConfig { //交换机名称 public static final String ITEM_TOPIC_EXCHANGE = "item_topic_exchange"; //队列名称 public static final String ITEM_QUEUE = "item_queue"; //声明交换机 @Bean("itemTopicExchange") public Exchange topicExchange(){ return ExchangeBuilder.topicExchange(ITEM_TOPIC_EXCHANGE).durable(true).build(); } //声明队列 @Bean("itemQueue") public Queue itemQueue(){ return QueueBuilder.durable(ITEM_QUEUE).build(); } //绑定队列和交换机 @Bean public Binding itemQueueExchange(@Qualifier("itemQueue") Queue queue, @Qualifier("itemTopicExchange") Exchange exchange){ return BindingBuilder.bind(queue).to(exchange).with("item.#").noargs(); } }
测试类:
import com.provider.App; import com.provider.com.config.ProviderConfig; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; /** * @Author pshdhx * @Description springboot测试rabbitmq * @Date 2022/5/20 0020 9:23 */ @RunWith(SpringRunner.class) @SpringBootTest(classes = App.class) public class RabbitMQTest { @Autowired private RabbitTemplate rabbitTemplate; @Test public void test(){ rabbitTemplate.convertAndSend(ProviderConfig.ITEM_TOPIC_EXCHANGE, "item.insert", "商品新增,routing key 为item.insert"); rabbitTemplate.convertAndSend(ProviderConfig.ITEM_TOPIC_EXCHANGE, "item.update", "商品修改,routing key 为item.update"); rabbitTemplate.convertAndSend(ProviderConfig.ITEM_TOPIC_EXCHANGE, "item.delete", "商品删除,routing key 为item.delete"); } }
主启动类:略;
消费者代码
项目名称:springboot-rabbitmq-consumer
pom文件:略;yml文件:略;主启动:略;
监听文件ConsumerListener
package com.pshdhx.consumer.listener; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.stereotype.Component; /** * @Author pshdhx * @Description 消息监听处理类 * @Date 2022/5/20 0020 9:22 */ @Component public class ConsumerListener{ /** * 监听某个队列的消息 * @param message 接收到的消息 */ @RabbitListener(queues = "item_queue") public void myListener1(String message){ System.out.println("消费者接收到的消息为:" + message); } }
主启动类启动即可获取生产者发送的消息。
源码地址+笔记地址
所有代码均已运行校验过。