消息中间件:
ActiveMQ,RabbitMQ,Kafka等都是常见的消息中间件,安全性从高到低,速度从低到高,Kafka在大数据中使用较多,RabbitMQ在银行系统中使用较多。主要应用在异步解耦、消息缓冲等场景中。
RabbitMQ简介:
由Erlang语言开发,实现AMQP(Advanced Message Queuing Protocol)的一种消息中间件。支持多种客户端,例如:Python、Ruby、.NET、Java、JMS、C、PHP等。
个人学习总结:
链接:【springboot、springcloud、docker 等,学习目录】
结构:
来自百度百科:
提到消息中间件,我们常听到的是:消息生产者、消费者、队列。如图,RabbitMQ多了一个Exchange交换器,Exchange位于消息生产者和队列之间。
相关名词:
-
Producer: 消息生产者,图中最左边。将消息发送到Exchange中。
-
Consumer:消息消费者,图中最右边。订阅队列中的消息。
-
RabbitMQ Server:传输服务,维护着生产者到消费者之间的线路,保证消息按照指定的方式传输。
-
Exchange:交换器,将接收到的producer消息路由到指定队列(一或多个)。具有direct、fanout、topic、headers四种类型,每种类型对应不同的路由规则
-
Queue:队列,用于存放Exchange路由过来的消息,consumer从队列中订阅消息。
-
RoutingKey:路由规则,生产者发送消息到Exchange时指定,Exchange根据路由规则将消息发送到哪一个Queue中存储。
direct、fanout、topic 初步了解:
1、direct :直接模式,最简单的使用,一个消息只会有一个消费者。不需要 routingKey 规则匹配。
2、fanout:分列模式,将消息一次发给多个队列。不需要 routingKey 规则匹配。
3、topic:订阅模式,交换机中的消息根据匹配规则路由到对应的队列中。
集成RabbitMQ:
1、docker安装RabbitMQ:
# 1、获取镜像
docker pull rabbitmq:management
# 2、创建容器
docker run ‐di ‐‐name=tensquare_rabbitmq ‐p 5671:5617 ‐p 5672:5672 ‐p 4369:4369 ‐p 15671:15671 ‐p 15672:15672 ‐p 25672:25672 rabbitmq:management
监视画面:http://192.168.25.133:15672默认用户名、密码:guest
2、pom依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
3、application配置:
# 工程的端口
server:
port: 8099
spring:
rabbitmq:
host: 192.168.25.133
# mq 的端口
port: 5672
username: guest
password: guest
直接模式(Direct):
1、队列配置:
@Configuration
public class DirectConfig {
@Bean
public Queue helloQueue(){
return new Queue("hello");
}
}
亦可在15672监视画面中配置队列。
2、Producer:
@Autowired
private RabbitTemplate rabbitTemplate;
/**
* 直接模式 最简单的使用
* 一个消息只会有一个消费者,
*/
@Test
public void testSend(){
// 多个消费者只有一个消费者接收到消息
rabbitTemplate.convertAndSend("hello","直接模式测试");
// 多个接收者 均匀的分布到消费者中
/*for (int i = 0; i < 10; i++) {
rabbitTemplate.convertAndSend("hello","直接模式测试 :" + i);
}*/
}
3、consumer:
@Component
@RabbitListener(queues = "hello")
public class DirectCcustomer01 {
@RabbitHandler
public void showMessage(String message){
System.out.println("DirectCcustomer01 >>>hello>>>接收到消息:"+message);
}
}
@Component
@RabbitListener(queues = "hello")
public class DirectCcustomer02 {
@RabbitHandler
public void showMessage(String message){
System.out.println("DirectCcustomer02 >>>hello>>>接收到消息:"+message);
}
}
4、测试:
-
发送一次:多个消费者只有一个消费者可以接收。
-
发送多次:多个消费者均匀消费。
分列模式(Fanout):
1、队列配置:此处定义了三个队列测试,生产中根据实际需求定义。
@Configuration
public class FanoutConfig {
/*********************** 定义队列 start ************************/
@Bean
public Queue fanoutA() {
return new Queue("fanout.a");
}
@Bean
public Queue fanoutB() {
return new Queue("fanout.b");
}
@Bean
public Queue fanoutC() {
return new Queue("fanout.c");
}
/*********************** 定义队列 end ************************/
/**
* 分列式 交换器
* 名称:fanoutExchange
*/
@Bean
FanoutExchange fanoutExchange() {
return new FanoutExchange("fanoutExchange");
}
/****************** 队列绑定交换机 start ********************/
// fanoutA 与 fanoutExchange 绑定
@Bean
public Binding bindingExchangeWithA() {
return BindingBuilder.bind(fanoutA()).to(fanoutExchange());
}
// fanoutB 与 fanoutExchange 绑定
@Bean
public Binding bindingExchangeWithB() {
return BindingBuilder.bind(fanoutB()).to(fanoutExchange());
}
// fanoutC 与f anoutExchange 绑定
@Bean
public Binding bindingExchangeWithC() {
return BindingBuilder.bind(fanoutC()).to(fanoutExchange());
}
/****************** 队列绑定交换机 end ********************/
}
2、Producer:
/**
* 分列模式 -- 将消息一次发给多个队列
* 不需要 routingKey 规则匹配 直接模式的加强版
*/
@Test
public void testSendFanout(){
rabbitTemplate.convertAndSend("fanoutExchange","", "分列模式发送的消息");
}
3、consumer:对应三个消费者
@Component
@RabbitListener(queues = "fanout.a")
public class FanoutConsumerA {
@RabbitHandler
public void showMessage(String message){
System.out.println(">>>FanoutConsumerA >>>fanout.a>>>接收到消息:"+message);
}
}
@Component
@RabbitListener(queues = "fanout.b")
public class FanoutConsumerB {
@RabbitHandler
public void showMessage(String message){
System.out.println(">>>FanoutConsumerB >>>fanout.b>>>接收到消息:"+message);
}
}
@Component
@RabbitListener(queues = "fanout.c")
public class FanoutConsumerC {
@RabbitHandler
public void showMessage(String message){
System.out.println(">>>FanoutConsumerC >>>fanout.c>>>接收到消息:"+message);
}
}
4、测试:
发送一次消息,FanoutConsumerA 、B、C都可以接收到消息。
订阅模式(Topic):
1、队列配置:
/**
* @Auther: xf
* @Date: 2019/1/3 18:46
* @Description: 主题模式
*
* 任何发送到Topic Exchange的消息都会被转发到所有关心RouteKey中指定话题的Queue上
* RouteKey:匹配规则,使每个消息队列只关心自己匹配的消息
*/
@Configuration
public class TopicConfig {
/*********************** 定义队列 start ************************/
@Bean
public Queue topiocA() {
return new Queue("topic.a");
}
@Bean
public Queue topicB() {
return new Queue("topic.b");
}
@Bean
public Queue topicC() {
return new Queue("topic.c");
}
/*********************** 定义队列 end ************************/
/**
* 主题模式 交换器
* 名称:topicExchange
*/
@Bean
TopicExchange topicExchange() {
return new TopicExchange("topicExchange");
}
/****************** 队列绑定交换机 start ********************/
/**
* topicA 与 topicExchange 绑定
* routingKey : topic.coolron
* 只会接收包含topic.msg的消息
*/
@Bean
public Binding bindingTopicExchangeWithA() {
return BindingBuilder.bind(topiocA()).to(topicExchange()).with("topic.coolron");
}
/**
* topicB 与 topicExchange 绑定
* routingKey : topic.#
* 只会接收 topic 开头的信息 topic、topic.AA、topic.AA.BB、topic.AA.BB.CC
* # 匹配0个、一个或多个词
*/
@Bean
public Binding bindingTopicExchangeWithB() {
return BindingBuilder.bind(topicB()).to(topicExchange()).with("topic.#");
}
/**
* topicC 与 topicExchange 绑定
* routingKey : topic.*
* 只能接收类似 topic.AA、topic.BB、topic.CC 的消息
* * 匹配一个词
*/
@Bean
public Binding bindingTopicExchangeWithC() {
return BindingBuilder.bind(topicC()).to(topicExchange()).with("topic.*");
}
/****************** 队列绑定交换机 end ********************/
}
注意:"#"代表0个、一个或多个词、"*"代表一个词,"."分隔。
2、Producer:
/**
* 主题模式
*/
@Test
public void testSendTopic1(){
// 根据TopicConfig 中 RoutingKey 的配置 ,队列 topic.a、b、c 都可以匹配到
rabbitTemplate.convertAndSend("topicExchange","topic.coolron","主题模式");
}
@Test
public void testSendTopic2(){
// 根据TopicConfig 中 RoutingKey 的配置 ,队列 topic.b 可以匹配到 topic.#
rabbitTemplate.convertAndSend("topicExchange","topic.cool.ron","主题模式");
}
@Test
public void testSendTopic3(){
// 根据TopicConfig 中 RoutingKey 的配置 ,队列 topic.b、c 可以匹配到 topic.# topic.*
rabbitTemplate.convertAndSend("topicExchange","topic.ron","主题模式");
}
@Test
public void testSendTopic4(){
// 根据TopicConfig 中 RoutingKey 的配置 ,队列 topic.b、c 可以匹配到 topic.# topic.*,“” 代表一个词
rabbitTemplate.convertAndSend("topicExchange","topic.","主题模式");
}
@Test
public void testSendTopic5(){
// 根据TopicConfig 中 RoutingKey 的配置 ,队列 topic.b 可以匹配到 topic.#
rabbitTemplate.convertAndSend("topicExchange","topic","主题模式");
}
3、consumer:
/**
* @Auther: xf
* @Date: 2019/1/3 22:46
* @Description: 主题模式 消费者 A
*/
@Component
@RabbitListener(queues = "topic.a")
public class TopicCustomerA {
@RabbitHandler
public void showMessage(String message){
System.out.println("TopicCustomerA>>>topic.a>>>接收到消息:"+message);
}
}
@Component
@RabbitListener(queues = "topic.b")
public class TopicCustomerB {
@RabbitHandler
public void showMessage(String message){
System.out.println("TopicCustomerB>>>topic.b>>>接收到消息:"+message);
}
}
@Component
@RabbitListener(queues = "topic.c")
public class TopicCustomerC {
@RabbitHandler
public void showMessage(String message){
System.out.println("TopicCustomerC>>>topic.c>>>接收到消息:" + message);
}
}
4、测试结果:
-
testSendTopic1(topic.coolron):三个consumer都可以订阅到消息。规则:topic.coolron、topic.#、topic.*。
-
testSendTopic2(topic.cool.ron):只有TopicCustomerB订阅到消息。规则:topic.#。
-
testSendTopic3(topic.ron):TopicCustomerB、C订阅到消息。规则:topic.#、topic.*。
-
testSendTopic4(topic.):队列 B、C可以订阅到消息,“” 代表一个词 。规则topic.# topic.*。
-
testSendTopic5(topic):队列 B 可以订阅到消息。规则:topic.#。
总结:
1、一条消息,直接模式只能一个消费者订阅、分列模式可多个消费者订阅、topic模式根据路由规则匹配订阅。
2、Exchange位于producer和queue之间。
3、Exchange根据匹配规则将消息路由到对应的Queue中存储。
4、routingKey,"#"代表0个、一个或多个、"*"代表一个词。上文中"topic."会匹配到B、C,因为“”代表一个词。“topic”只会匹配到B。
代码地址:
springboot-account2: 公众号 - Gitee.com
个人微信公众号,谢谢支持!