├── README.md
└── spring_boot_rabbitMQ
├── .gitignore
├── pom.xml
└── src
├── main
├── java
│ └── com
│ │ └── rabbitmq
│ │ ├── Application.java
│ │ ├── commons
│ │ └── R.java
│ │ ├── config
│ │ ├── RabbitMQDirectConfig.java
│ │ ├── RabbitMQFanoutConfig.java
│ │ └── RabbitMQTopicConfig.java
│ │ ├── controller
│ │ └── MessageController.java
│ │ ├── receive
│ │ ├── DirectReceiver.java
│ │ ├── FanoutReceiver.java
│ │ └── TopicReceiver.java
│ │ └── send
│ │ ├── DirectSender.java
│ │ ├── FanoutSender.java
│ │ └── TopicSender.java
└── resources
│ └── application.properties
└── test
└── java
└── com
└── rabbitmq
└── ApplicationTests.java
/README.md:
--------------------------------------------------------------------------------
1 | ### spring_boot_rabbitMQ
2 | - Spring Boot集成rabbitMQ实现消息推送,rabbitMQ为异步消息处理提出了一个很好的解决方案,它是一个非常好用的消息中间件。`主要解决当生产者大量产生数据时,消费者无法快速消费的问题`。这个时候需要一个中间层,保存这个数据,rabbitMQ是一个很好的解决方案。
3 | - Spring Boot为rabbitMQ提供了支持, Spring Boot为rabbitMQ准备了spring-boot-starter-amqp,并且为RabbitTemplate和rabbitMQ提供了自动配置选项。
4 | - AMQP协议,即Advanced Message Queuing Protocol,高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。消息中间件主要用于组件之间的解耦,消息的发送者无需知道消息使用者的存在,反之亦然。AMQP的主要特征是面向消息、队列、路由(包括点对点和发布/订阅)、可靠性、安全。
5 | ### 安装rabbitMQ
6 | - 安装brew
7 |
8 | `ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"`
9 |
10 | - 安装rabbitmq
11 |
12 | `brew install rabbitmq`
13 |
14 | - 启动rabbitmq server
15 |
16 | `cd /usr/local/sbin/`
17 |
18 | `sudo ./rabbitmq-server`
19 |
20 | - 成功示例,端口是5672
21 |
22 |
23 |
24 | ### 相关概念
25 | - 通常我们谈到队列服务, 会有三个概念:发消息者、队列、收消息者,RabbitMQ在这个基本概念之上, 多做了一层抽象, 在发消息者和队列之间, 加入了交换器 (Exchange)。这样发消息者和队列就没有直接联系, 转而变成发消息者把消息给交换器, 交换器根据调度策略再把消息再给队列。
26 |
27 | 
28 |
29 | - Broker:简单来说就是消息队列服务器实体。
30 | - Exchange:消息交换机,它指定消息按什么规则,路由到哪个队列。
31 | - Queue:消息队列载体,每个消息都会被投入到一个或多个队列。
32 | - Binding:绑定,它的作用就是把exchange和queue按照路由规则绑定起来。
33 | - Routing Key:路由关键字,exchange根据这个关键字进行消息投递。
34 | - Producer:消息生产者,就是投递消息的程序。
35 | - Consumer:消息消费者,就是接受消息的程序。
36 | - Channel:消息通道,在客户端的每个连接里,可建立多个channel,每个channel代表一个会话任务。
37 |
38 | ### 基本使用过程
39 |
40 | - 客户端连接到消息队列服务器,打开一个channel。
41 | - 客户端声明一个exchange,并设置相关属性。
42 | - 客户端声明一个queue,并设置相关属性。
43 | - 客户端使用routing key,在exchange和queue之间建立好绑定关系。
44 | - 客户端投递消息到exchange。
45 | - exchange接收到消息后,就根据消息的key和已经设置的binding,进行消息路由,将消息投递到一个或多个队列里。
46 |
47 | 
48 |
49 | ### Exchange四种类型
50 |
51 | - Direct模式(常用)
52 | - Direct exchange完全根据key进行投递,只有key与绑定时的routing-key完全一致的消息才会收到消息。
53 | - Topic模式(常用)
54 | - Topic exchange会根据key进行模式匹配然后进行投递,与设置的routing-key匹配上的队列才能收到消息。
55 | - Fanout模式
56 | - 每个发到fanout类型交换器的消息都会分到所有绑定的队列上去。fanout交换器不处理路由键,只是简单的将队列绑定到交换器上,每个发送到交换器的消息都会被转发到与该交换器绑定的所有队列上。
57 | - Header模式(不常用)
58 | - 不太常用,可以自定义匹配规则。
59 |
60 | ### 消息持久化
61 | - RabbitMQ支持消息的持久化,即将消息数据持久化到磁盘上,如果消息服务器中途断开,下次开启会将持久化的消息重新发送。
62 | - RabbitMQ的持久化有交换机、队列、消息的持久化。
63 | - 声明交换机Exchange的时候设置 durable=true
64 |
65 | ```
66 | public TopicExchange exchange() {
67 | return new TopicExchange(EXCHANGE_NAME,true,false);
68 | }
69 | ```
70 | - 声明队列Queue的时候设置 durable=true
71 |
72 | ```
73 | @Bean
74 | public Queue queue() {
75 | //durable:是否将队列持久化 true表示需要持久化 false表示不需要持久化
76 | return new Queue(QUEUE_NAME, false);
77 | }
78 | ```
79 | - 发送消息的时候设置消息的 deliveryMode = 2
80 |
81 | ```
82 | new MessageProperties() --> DEFAULT_DELIVERY_MODE = MessageDeliveryMode.PERSISTENT --> deliveryMode = 2;
83 | ```
84 |
85 | - 出于数据安全考虑,一般消息都会进行持久化。
86 |
87 | ### 保证MQ消息的幂等性
88 | - mq内部会为每条消息生成一个全局唯一、与业务无关的消息id,当mq接收到消息时,会先根据该id判断消息是否重复发送,mq再决定是否接收该消息。
89 | - Java自带的UUID
90 | - Twitter Snowflake
91 | - 利用MySQL的唯一索引UNIQUE KEY,如果重复了,数据就会插入失败。
92 |
93 | ### 实例代码可以直接拉下来使用。
94 |
--------------------------------------------------------------------------------
/spring_boot_rabbitMQ/.gitignore:
--------------------------------------------------------------------------------
1 | /target/
2 | !.mvn/wrapper/maven-wrapper.jar
3 |
4 | ### STS ###
5 | .apt_generated
6 | .classpath
7 | .factorypath
8 | .project
9 | .settings
10 | .springBeans
11 | .sts4-cache
12 |
13 | ### IntelliJ IDEA ###
14 | .idea
15 | *.iws
16 | *.iml
17 | *.ipr
18 |
19 | ### NetBeans ###
20 | /nbproject/private/
21 | /build/
22 | /nbbuild/
23 | /dist/
24 | /nbdist/
25 | /.nb-gradle/
--------------------------------------------------------------------------------
/spring_boot_rabbitMQ/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | com.rabbitmq.borker
8 | spring-boot-rabbitMQ
9 | 0.0.1-SNAPSHOT
10 | jar
11 |
12 | spring-boot-rabbitMQ
13 | push message with rabbitMQ for Spring Boot
14 |
15 |
16 | org.springframework.boot
17 | spring-boot-starter-parent
18 | 2.1.0.RELEASE
19 |
20 |
21 |
22 |
23 | UTF-8
24 | UTF-8
25 | 1.8
26 |
27 |
28 |
29 |
30 | org.springframework.boot
31 | spring-boot-starter-web
32 |
33 |
34 |
35 | org.springframework.boot
36 | spring-boot-starter-amqp
37 |
38 |
39 | org.springframework.boot
40 | spring-boot-starter-websocket
41 |
42 |
43 |
44 | org.springframework.boot
45 | spring-boot-devtools
46 | runtime
47 |
48 |
49 |
50 | org.springframework.boot
51 | spring-boot-starter-test
52 | test
53 |
54 |
55 |
56 | org.projectlombok
57 | lombok
58 |
59 |
60 |
61 | com.alibaba
62 | fastjson
63 | 1.2.83
64 |
65 |
66 |
67 |
68 |
69 |
70 | org.springframework.boot
71 | spring-boot-maven-plugin
72 |
73 |
74 |
75 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/spring_boot_rabbitMQ/src/main/java/com/rabbitmq/Application.java:
--------------------------------------------------------------------------------
1 | package com.rabbitmq;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class Application {
8 |
9 | public static void main(String[] args) {
10 | SpringApplication.run(Application.class, args);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/spring_boot_rabbitMQ/src/main/java/com/rabbitmq/commons/R.java:
--------------------------------------------------------------------------------
1 | package com.rabbitmq.commons;
2 |
3 | import lombok.Data;
4 |
5 | @Data
6 | public class R {
7 |
8 | private String msg;
9 |
10 | private Integer code;
11 |
12 | private Object data;
13 |
14 | public static R ok() {
15 | R r = new R();
16 | r.setCode(200);
17 | r.setMsg("OK");
18 | return r;
19 | }
20 |
21 | public static R ok(Object object) {
22 | R r = new R();
23 | r.setCode(200);
24 | r.setMsg("OK");
25 | r.setData(object);
26 | return r;
27 | }
28 |
29 | public static R error(Object object) {
30 | R r = new R();
31 | r.setCode(309);
32 | r.setMsg("error");
33 | r.setData(object);
34 | return r;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/spring_boot_rabbitMQ/src/main/java/com/rabbitmq/config/RabbitMQDirectConfig.java:
--------------------------------------------------------------------------------
1 | package com.rabbitmq.config;
2 |
3 | import org.springframework.amqp.core.Queue;
4 | import org.springframework.context.annotation.Bean;
5 | import org.springframework.context.annotation.Configuration;
6 |
7 | /**
8 | * Direct模式
9 | *
10 | * @author cm_wang
11 | */
12 | @Configuration
13 | public class RabbitMQDirectConfig {
14 |
15 | public static final String DIRECT_QUEUE = "direct.queue";
16 |
17 | /**
18 | * Direct模式
19 | *
20 | * @return
21 | */
22 | @Bean
23 | public Queue directQueue() {
24 | // 第一个参数是队列名字, 第二个参数是指是否持久化
25 | return new Queue(DIRECT_QUEUE, true);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/spring_boot_rabbitMQ/src/main/java/com/rabbitmq/config/RabbitMQFanoutConfig.java:
--------------------------------------------------------------------------------
1 | package com.rabbitmq.config;
2 |
3 | import org.springframework.amqp.core.Binding;
4 | import org.springframework.amqp.core.BindingBuilder;
5 | import org.springframework.amqp.core.FanoutExchange;
6 | import org.springframework.amqp.core.Queue;
7 | import org.springframework.context.annotation.Bean;
8 | import org.springframework.context.annotation.Configuration;
9 |
10 | /**
11 | * Fanout模式
12 | *
13 | * @author cm_wang
14 | */
15 | @Configuration
16 | public class RabbitMQFanoutConfig {
17 |
18 | public static final String FANOUT_QUEUE = "fanout.queue";
19 |
20 | public static final String FANOUT_EXCHANGE = "fanout.exchange";
21 |
22 | @Bean
23 | public Queue fanoutQueue() {
24 | return new Queue(FANOUT_QUEUE);
25 | }
26 |
27 | /**
28 | * Fanout模式 Fanout 就是我们熟悉的广播模式或者订阅模式,
29 | * 给Fanout交换机发送消息,绑定了这个交换机的所有队列都收到这个消息。
30 | *
31 | * @return
32 | */
33 | @Bean
34 | public FanoutExchange fanoutExchange() {
35 | return new FanoutExchange(FANOUT_EXCHANGE);
36 | }
37 |
38 | @Bean
39 | public Binding fanoutBinding() {
40 | return BindingBuilder.bind(fanoutQueue()).to(fanoutExchange());
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/spring_boot_rabbitMQ/src/main/java/com/rabbitmq/config/RabbitMQTopicConfig.java:
--------------------------------------------------------------------------------
1 | package com.rabbitmq.config;
2 |
3 | import org.springframework.amqp.core.Binding;
4 | import org.springframework.amqp.core.BindingBuilder;
5 | import org.springframework.amqp.core.Queue;
6 | import org.springframework.amqp.core.TopicExchange;
7 | import org.springframework.context.annotation.Bean;
8 | import org.springframework.context.annotation.Configuration;
9 |
10 | /**
11 | * Topic模式
12 | *
13 | * @author cm_wang
14 | */
15 | @Configuration
16 | public class RabbitMQTopicConfig {
17 |
18 | public static final String TOPIC_QUEUE = "topic.queue";
19 |
20 | public static final String TOPIC_EXCHANGE = "topic.exchange";
21 |
22 | @Bean
23 | public Queue topicQueue() {
24 | return new Queue(TOPIC_QUEUE, false);
25 | }
26 |
27 | @Bean
28 | public TopicExchange topicExchange() {
29 | return new TopicExchange(TOPIC_EXCHANGE, true, false);
30 | }
31 |
32 | @Bean
33 | public Binding topicBinding() {
34 | return BindingBuilder.bind(topicQueue()).to(topicExchange()).with("rabbitmq.message");
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/spring_boot_rabbitMQ/src/main/java/com/rabbitmq/controller/MessageController.java:
--------------------------------------------------------------------------------
1 | package com.rabbitmq.controller;
2 |
3 | import org.springframework.beans.factory.annotation.Autowired;
4 | import org.springframework.web.bind.annotation.GetMapping;
5 | import org.springframework.web.bind.annotation.RequestMapping;
6 | import org.springframework.web.bind.annotation.RequestParam;
7 | import org.springframework.web.bind.annotation.RestController;
8 |
9 | import com.alibaba.fastjson.JSONObject;
10 | import com.rabbitmq.commons.R;
11 | import com.rabbitmq.send.DirectSender;
12 | import com.rabbitmq.send.FanoutSender;
13 | import com.rabbitmq.send.TopicSender;
14 |
15 | /**
16 | * 消息路由规则:四种模式(topic、direct、fanout、header)
17 | * topic:根据绑定关键字通配符规则匹配、比较灵活
18 | * direct:默认,根据routingKey完全匹配,好处是先匹配再发送
19 | * fanout:不需要指定routingkey,相当于群发
20 | * header:不太常用,可以自定义匹配规则
21 | *
22 | * @author cm_wang
23 | */
24 | @RestController
25 | @RequestMapping("/api/v1")
26 | public class MessageController {
27 |
28 | @Autowired
29 | private TopicSender topicSender;
30 |
31 | @Autowired
32 | private DirectSender directSender;
33 |
34 | @Autowired
35 | private FanoutSender fanoutSender;
36 |
37 | /**
38 | * topic 模式
39 | *
40 | * @return
41 | */
42 | @GetMapping("/sendTopic")
43 | public R sendTopic(@RequestParam("obj") JSONObject obj) {
44 | topicSender.sendTopic(obj);
45 | return R.ok();
46 | }
47 |
48 | /**
49 | * direct 模式 发送者与接收者的Queue名字一定要相同,否则接收收不到消息
50 | *
51 | * @return
52 | */
53 | @GetMapping("/sendDirect")
54 | public R sendDirect(@RequestParam("obj") JSONObject obj) {
55 | for (int i = 0; i < 10; i++) {
56 | directSender.sendDirect(obj);
57 | }
58 | return R.ok();
59 | }
60 |
61 | /**
62 | * fanout模式
63 | *
64 | * @return
65 | */
66 | @GetMapping("/sendFanout")
67 | public R sendFanout(@RequestParam("obj") JSONObject obj) {
68 | for (int i = 0; i < 10; i++) {
69 | fanoutSender.sendFanout(obj);
70 | }
71 | return R.ok();
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/spring_boot_rabbitMQ/src/main/java/com/rabbitmq/receive/DirectReceiver.java:
--------------------------------------------------------------------------------
1 | package com.rabbitmq.receive;
2 |
3 | import org.springframework.amqp.rabbit.annotation.RabbitListener;
4 | import org.springframework.stereotype.Component;
5 |
6 | import com.alibaba.fastjson.JSONObject;
7 | import com.rabbitmq.config.RabbitMQDirectConfig;
8 |
9 | import lombok.extern.slf4j.Slf4j;
10 |
11 | /**
12 | * direct模式消费者
13 | *
14 | * @author cm_wang
15 | */
16 | @Component
17 | @Slf4j
18 | public class DirectReceiver {
19 |
20 | // queues是指要监听的队列的名字
21 | @RabbitListener(queues = RabbitMQDirectConfig.DIRECT_QUEUE)
22 | public void receiverDirect(JSONObject obj) {
23 | log.info("receiverDirectQueue收到消息" + obj.toString());
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/spring_boot_rabbitMQ/src/main/java/com/rabbitmq/receive/FanoutReceiver.java:
--------------------------------------------------------------------------------
1 | package com.rabbitmq.receive;
2 |
3 | import org.springframework.amqp.rabbit.annotation.RabbitListener;
4 | import org.springframework.stereotype.Component;
5 |
6 | import com.alibaba.fastjson.JSONObject;
7 | import com.rabbitmq.config.RabbitMQFanoutConfig;
8 |
9 | import lombok.extern.slf4j.Slf4j;
10 |
11 | /**
12 | * fanout模式消费者
13 | *
14 | * @author cm_wang
15 | */
16 | @Component
17 | @Slf4j
18 | public class FanoutReceiver {
19 |
20 | // queues是指要监听的队列的名字
21 | @RabbitListener(queues = RabbitMQFanoutConfig.FANOUT_QUEUE)
22 | public void receiveFanout(JSONObject obj) {
23 | log.info("receiveFanout收到消息" + obj.toString());
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/spring_boot_rabbitMQ/src/main/java/com/rabbitmq/receive/TopicReceiver.java:
--------------------------------------------------------------------------------
1 | package com.rabbitmq.receive;
2 |
3 | import org.springframework.amqp.rabbit.annotation.RabbitListener;
4 | import org.springframework.stereotype.Component;
5 |
6 | import com.alibaba.fastjson.JSONObject;
7 | import com.rabbitmq.config.RabbitMQTopicConfig;
8 |
9 | import lombok.extern.slf4j.Slf4j;
10 |
11 | /**
12 | * topic模式消费者
13 | *
14 | * @author cm_wang
15 | */
16 | @Component
17 | @Slf4j
18 | public class TopicReceiver {
19 |
20 | // queues是指要监听的队列的名字
21 | @RabbitListener(queues = RabbitMQTopicConfig.TOPIC_QUEUE)
22 | public void receiveTopic(JSONObject obj) {
23 | log.info("receiveTopic收到消息:" + obj.toString());
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/spring_boot_rabbitMQ/src/main/java/com/rabbitmq/send/DirectSender.java:
--------------------------------------------------------------------------------
1 | package com.rabbitmq.send;
2 |
3 | import org.springframework.amqp.core.AmqpTemplate;
4 | import org.springframework.beans.factory.annotation.Autowired;
5 | import org.springframework.stereotype.Component;
6 |
7 | import com.alibaba.fastjson.JSONObject;
8 | import com.rabbitmq.config.RabbitMQDirectConfig;
9 |
10 | import lombok.extern.slf4j.Slf4j;
11 |
12 | /**
13 | * direct模式生产者
14 | *
15 | * @author cm_wang
16 | */
17 | @Component
18 | @Slf4j
19 | public class DirectSender {
20 |
21 | @Autowired
22 | private AmqpTemplate amqpTemplate;
23 |
24 | public void sendDirect(JSONObject obj) {
25 |
26 | log.info("sendDirectQueue已发送消息");
27 | // 第一个参数是指要发送到哪个队列里面, 第二个参数是指要发送的内容
28 | this.amqpTemplate.convertAndSend(RabbitMQDirectConfig.DIRECT_QUEUE, obj);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/spring_boot_rabbitMQ/src/main/java/com/rabbitmq/send/FanoutSender.java:
--------------------------------------------------------------------------------
1 | package com.rabbitmq.send;
2 |
3 | import org.springframework.amqp.core.AmqpTemplate;
4 | import org.springframework.beans.factory.annotation.Autowired;
5 | import org.springframework.stereotype.Component;
6 |
7 | import com.alibaba.fastjson.JSONObject;
8 | import com.rabbitmq.config.RabbitMQFanoutConfig;
9 |
10 | import lombok.extern.slf4j.Slf4j;
11 |
12 | /**
13 | * fanout模式生产者
14 | *
15 | * @author cm_wang
16 | */
17 | @Component
18 | @Slf4j
19 | public class FanoutSender {
20 |
21 | @Autowired
22 | private AmqpTemplate amqpTemplate;
23 |
24 | public void sendFanout(JSONObject obj) {
25 |
26 | log.info("sendFanout已发送消息");
27 | // 这里的第2个参数为空。
28 | // 因为fanout 交换器不处理路由键,只是简单的将队列绑定到交换器上,
29 | // 每个发送到交换器的消息都会被转发到与该交换器绑定的所有队列上
30 | this.amqpTemplate.convertAndSend(RabbitMQFanoutConfig.FANOUT_EXCHANGE, "", obj);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/spring_boot_rabbitMQ/src/main/java/com/rabbitmq/send/TopicSender.java:
--------------------------------------------------------------------------------
1 | package com.rabbitmq.send;
2 |
3 | import org.springframework.amqp.core.AmqpTemplate;
4 | import org.springframework.beans.factory.annotation.Autowired;
5 | import org.springframework.stereotype.Component;
6 |
7 | import com.alibaba.fastjson.JSONObject;
8 | import com.rabbitmq.config.RabbitMQTopicConfig;
9 |
10 | import lombok.extern.slf4j.Slf4j;
11 |
12 | /**
13 | * topic模式生产者
14 | *
15 | * @author cm_wang
16 | */
17 | @Component
18 | @Slf4j
19 | public class TopicSender {
20 |
21 | @Autowired
22 | private AmqpTemplate amqpTemplate;
23 |
24 | /**
25 | * topic 模式
26 | */
27 | public void sendTopic(JSONObject obj) {
28 |
29 | log.info("sendTopic已发送消息");
30 | // 第一个参数:TopicExchange名字
31 | // 第二个参数:Route-Key
32 | // 第三个参数:要发送的内容
33 | this.amqpTemplate.convertAndSend(RabbitMQTopicConfig.TOPIC_EXCHANGE, "rabbitmq.message", obj);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/spring_boot_rabbitMQ/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | spring.rabbitmq.host=localhost
2 | spring.rabbitmq.port=5672
3 | spring.rabbitmq.virtual-host=/
4 | spring.rabbitmq.username=guest
5 | spring.rabbitmq.password=guest
6 | spring.rabbitmq.publisher-confirms=true
--------------------------------------------------------------------------------
/spring_boot_rabbitMQ/src/test/java/com/rabbitmq/ApplicationTests.java:
--------------------------------------------------------------------------------
1 | package com.rabbitmq;
2 |
3 | import org.junit.Test;
4 | import org.junit.runner.RunWith;
5 | import org.springframework.boot.test.context.SpringBootTest;
6 | import org.springframework.test.context.junit4.SpringRunner;
7 |
8 | @RunWith(SpringRunner.class)
9 | @SpringBootTest
10 | public class ApplicationTests {
11 |
12 | @Test
13 | public void contextLoads() {
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------