Article Directory
One, know RabbitMQ
RabbitMQ is introduced with AMQP protocol:
(1) RabbitMQ is an open source message broker and queue server, used to share data between completely different applications through common protocols. The bottom layer of RabbitMQ is written in Erlang language, and RabbitMQ is based on the AMQP protocol.
(2) RabbitMQ can not only be written using the java client, but also other languages (python, php, etc...), it provides a rich API
Advantages of RabbitMQ:
(1) Open source, excellent performance, stability guarantee
(2) Perfect integration with SpringAMQP, rich API (Spring provides a framework based on RabbitMQ, called AMQP framework) This framework not only presents the native RabbitMQ, but also provides Rich and expandable APIs help developers to better apply
(3) Rich cluster mode, expression configuration, HA mode, mirror queue model
description: (to ensure high reliability and availability in advance without data loss) universal use The mirror queue mode
(4) AMQP full name: Advanced Message Queuing Protocl AMQP translated: Advanced Message Queuing Protocol
Two, install RabbitMQ
Here to facilitate the installation of RabbitMQ on docker
(1) First search for the installation command of RabbitMQ: https://hub.docker.com/_/rabbitmq
docker run -d --hostname my-rabbit --name some-rabbit rabbitmq:3-management
Successful installation:
For the mapped ports, you need to pay attention to two: 15672 and 5672, where 15672 is the management port and 5672 is the communication port
You can see that the host mapping port corresponding to docker container 15672 is 32771,
The default username and password are both guest
Three, SpringBoot integrates RabbitMQ
1. Add dependencies
2. Configure application.properties
Note that the port selection here is 32781, the port corresponding to the rabbitmq container is 5672, select the communication port, and do not choose the management port
spring.rabbitmq.host=192.168.245.133
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.port=32781
3. Direct exchange mode
For direct exchange mode, please refer to: https://blog.csdn.net/fakerswe/article/details/81508963
The so-called "directly connected to the exchange" means: the message delivered by the Producer (producer) is forwarded by DirectExchange (switch) to a specific Queue (queue) bound by routingkey, the message is put into the queue, and the Consumer subscribes to the message from the Queue
Here the specific parameters of a queue is routingKey
controlled, this parameter message is delivered to the queue
The core idea of the RabbitMQ message model: The producer will send the message to the RabbitMQ exchange center (Exchange). One side of the Exchange is the producer, and the other side is one or more queues. The Exchange determines a message Life cycle-sent to some queues, or discarded directly.
(1) Configure RabbitDirectConfig
@Configuration
public class RabbitDirectConfig {
public final static String DIRECTNAME = "yolo-direct";
// 消息队列
@Bean
Queue queue() {
return new Queue("hello.yolo");
}
@Bean
DirectExchange directExchange() {
return new DirectExchange(DIRECTNAME, true, false);
}
// 将 queue 和 directExchange 绑定到一起
@Bean
Binding binding() {
return BindingBuilder.bind(queue()).to(directExchange()).with("direct");
}
}
(2) Consumer
@Component
public class DirectReceiver {
// 监听队列
@RabbitListener(queues = "hello.yolo")
public void handler1(String msg) {
System.out.println("handler1>>>" + msg);
}
}
(3) Test: Producer
@Autowired
RabbitTemplate rabbitTemplate;
@Test
public void contextLoads() {
//将消息转发到 routingKey 为 hello.yolo 的队列,对应 DirectReceiver 的监听队列名
rabbitTemplate.convertAndSend("hello.yolo", "hello yolo! ni hao!");
}
4. Broadcast mode
You can refer to: https://blog.csdn.net/fakerswe/article/details/81455340
To put it simply, the message in the exchange (Exchange) is sent to all queues bound to the exchange and ignored routingKey
.
As can be seen from the figure, after the producer sends the message to the exchange, the exchange sends the message to the consumer queue. If the consumer queue wants to receive the messages in the exchange, it needs to ensure that the name of the exchange bound to the queue must be the same as that of the exchange. This is the key to the broadcast mode and the roughest premise for all subsequent MQ modes.
Here the message is sent to the switch through the producer, and then the switch is sent to the bound queue
(1) Configure the broadcast mode
@Configuration
public class RabbitFanoutConfig {
public static final String FANOUTNAME = "yolo-fanout";
/**
* 队列1
* @return
*/
@Bean
Queue queueOne() {
return new Queue("queue-one");
}
/**
* 队列2
* @return
*/
@Bean
Queue queueTwo() {
return new Queue("queue-two");
}
/**
* 交换机
* @return
*/
@Bean
FanoutExchange fanoutExchange() {
return new FanoutExchange(FANOUTNAME, true, false);
}
/**
* 绑定队列1
* @return
*/
@Bean
org.springframework.amqp.core.Binding bindingOne() {
return BindingBuilder.bind(queueOne()).to(fanoutExchange());
}
/**
* 绑定队列2
* @return
*/
@Bean
Binding bindingTwo() {
return BindingBuilder.bind(queueTwo()).to(fanoutExchange());
}
}
(2) Consumer
/**
* 定义接收器:消费者
*/
@Component
public class FanoutReceiver {
/**
* 接收消息队列1
* @param msg
*/
@RabbitListener(queues = "queue-one")
public void handler1(String msg) {
System.out.println("FanoutReceiver:handler1:" + msg);
}
/**
* 接收消息队列2
* @param msg
*/
@RabbitListener(queues = "queue-two")
public void handler2(String msg) {
System.out.println("FanoutReceiver:handler2:" + msg);
}
}
(3) Test: Producer
/**
* 往交换机上发送信息
*/
@Test
public void test1() {
rabbitTemplate.convertAndSend(RabbitFanoutConfig.FANOUTNAME,null,"hello fanout!");
}
It should be noted here that you need to start the consumer first, and then start the producer. Otherwise, start the producer first. After the exchange receives the message and finds that there is no queue interested in it, the message will be discarded., Has nothing to do with routingKey at this time
Both queue 1 and queue 2 received the message
5. Topic route matching mode
Can refer to: https://blog.csdn.net/weixin_43770545/article/details/90902788
If you want to buy a pair of sports shoes on Taobao, would you search for "XXX sports shoes" in the search box. At this time, the system will fuzzy match all the sports shoes that meet the requirements and show it to you.
The so-called "topic routing matching switch" is also the same reason, but there are certain rules when using it.
String routingkey = “testTopic.#”;
String routingkey = “testTopic.*”;
*
Means that only one word
#
is matched means that multiple words are matched
(1) Configure topic mode
@Configuration
public class RabbitTopicConfig {
public static final String TOPICNAME = "yolo-topic";
@Bean
TopicExchange topicExchange() {
return new TopicExchange(TOPICNAME, true, false);
}
@Bean
Queue xiaomi() {
return new Queue("xiaomi");
}
@Bean
Queue huawei() {
return new Queue("huawei");
}
@Bean
Queue phone() {
return new Queue("phone");
}
@Bean
Binding xiaomiBinding() {
//xiaomi.# 表示如果路由的 routingKey 是以xiaomi 开头就会发送到 xiaomi 这个队列上
return BindingBuilder.bind(xiaomi()).to(topicExchange()).with("xiaomi.#");
}
@Bean
Binding huaweiBinding() {
//huawei.#
return BindingBuilder.bind(huawei()).to(topicExchange()).with("huawei.#");
}
@Bean
Binding phoneBinding() {
// #.phone.# 表示routingKey 中包含 phone 就会被发送到 phone 这个队列上
return BindingBuilder.bind(phone()).to(topicExchange()).with("#.phone.#");
}
}
(2) Consumer
@Component
public class TopicReceiver {
@RabbitListener(queues = "xiaomi")
public void handler1(String msg) {
System.out.println("TopicReceiver:handler1:" + msg);
}
@RabbitListener(queues = "huawei")
public void handler2(String msg) {
System.out.println("TopicReceiver:handler2:" + msg);
}
@RabbitListener(queues = "phone")
public void handler3(String msg) {
System.out.println("TopicReceiver:handler3:" + msg);
}
}
(3) Test: Producer
@Test
public void test2() {
//可以被小米的队列收到
rabbitTemplate.convertAndSend(RabbitTopicConfig.TOPICNAME, "xiaomi.news", "小米新闻");
//可以被手机的队列收到
rabbitTemplate.convertAndSend(RabbitTopicConfig.TOPICNAME, "vivo.phone", "vivo 手机");
//可以被华为和手机的队列收到
rabbitTemplate.convertAndSend(RabbitTopicConfig.TOPICNAME, "huawei.phone", "华为手机");
}
6. Header mode
This mode uses the key/value (key-value pair) matching queue in the header, and has nothing to do with routingKey
(1) Configure config
@Configuration
public class RabbitHeaderConfig {
public static final String HEADERNAME = "yolo-header";
@Bean
HeadersExchange headersExchange() {
return new HeadersExchange(HEADERNAME, true, false);
}
@Bean
Queue queueName() {
return new Queue("name-queue");
}
@Bean
Queue queueAge() {
return new Queue("age-queue");
}
@Bean
Binding bindingName() {
Map<String, Object> map = new HashMap<>();
//
map.put("name", "yolo");
//whereAny 表示消息的header中只要有一个header匹配上map中的key,value,就把消息发送到对应的队列上
return BindingBuilder.bind(queueName()).to(headersExchange()).whereAny(map).match();
}
@Bean
Binding bindingAge() {
//只要有,age 这个字段,就发送到相应的队列上去
return BindingBuilder.bind(queueAge()).to(headersExchange()).where("age").exists();
}
}
(2) Consumer
@Component
public class HeaderReceiver {
@RabbitListener(queues = "name-queue")
public void handler1(byte[] msg) {
System.out.println("HeaderReceiver:handler1:" + new String(msg, 0, msg.length));
}
@RabbitListener(queues = "age-queue")
public void handler2(byte[] msg) {
System.out.println("HeaderReceiver:handler2:" + new String(msg, 0, msg.length));
}
}
(3) Test
@Test
public void test3() {
//对应 RabbitHeaderConfig 中的map 的 key / value
Message nameMsg = MessageBuilder.withBody("hello yolo !".getBytes()).setHeader("name","yolo").build();
Message ageMsg = MessageBuilder.withBody("hello 99 !".getBytes()).setHeader("age","99").build();
//此时发送的消息接收,跟 routingKey无关,跟消息的 header 内容有关
rabbitTemplate.send(RabbitHeaderConfig.HEADERNAME, null, ageMsg);
rabbitTemplate.send(RabbitHeaderConfig.HEADERNAME, null, nameMsg);
}
If it is changed, the key-value pair in the header: the match cannot be successful and the queue cannot receive the information