Springboot integration RabbitMp

RabbitMQ

That is a message queue, and is mainly used to implement asynchronous decoupling the application process, but also can play the message buffer, the role of news distribution.

Messaging middleware using the Internet more and more companies, also saw the news just RocketMQ Ali will be donated to the apache, of course, the protagonist of today still speak RabbitMQ. The main role is decoupled messaging middleware, middleware standard usage is most producers to produce messages to the queue, consumers pick up the message from the queue and processed, the producers do not care who the consumer is, consumers do not care about who in the production of news, so as to achieve the purpose of decoupling. In distributed systems, message queues will be used in many other aspects, such as: support, RPC calls of distributed transactions, and so on.

Had previously been using ActiveMQ, in actual production use, there have been some minor problems in the network access to a lot of information, decided to try to replace ActiveMQ use RabbitMQ, RabbitMQ high availability, high performance, flexibility, and some features It attracted us access to some information sorted out this article.

RabbitMQ Introduction

RabbitMQ is a messaging middleware implement AMQP (Advanced Message Queuing Protocol), and originated in the financial system to store and forward messages in a distributed system, in terms of ease of use, scalability, high availability, and so doing well . RabbitMQ is mainly to achieve two-way decoupling between the system and implementation. When a producer to produce large amounts of data, consumers can not consume fast, we need an intermediate layer. Save this data.

AMQP, namely Advanced Message Queuing Protocol, Advanced Message Queuing Protocol, is an open standard application layer protocol for message-oriented middleware design. Message middleware mainly for decoupling between the components, sender of the message without knowing the user's presence information, and vice versa. AMQP main feature is a message for the queue, the routing (including point and publish / subscribe), reliability and safety.

RabbitMQ AMQP is an open source implementation, server-side using Erlang language, supports a variety of clients, such as: Python, Ruby, .NET, Java, JMS, C, PHP, ActionScript, XMPP, STOMP, and support for AJAX. In a distributed system for store and forward messages, in terms of ease of use, scalability, high availability, and so doing well.

Related concepts

Usually we talk about queue service, there will be three concepts: message's queue, who received the message, RabbitMQ on this basic concept, do a layer of abstraction between the person and message queues, adding switches (the exchange). such message queue and are not directly linked, in turn, becomes the message to the message by the switch, the switch then give the message queue according to the scheduling policy.

image

P stands for the left side of the producers, that is to RabbitMQ messaging process.
That intermediate is RabbitMQ, which includes switches and queue.
C represents the right of the consumer, that is to RabbitMQ messaging process to take.
So, one of the more important concepts have four, namely: virtual hosts, switches, queues, and bindings.

Web Hosting: Web Hosting holds a set of switches, queues and bindings. Why do we need multiple virtual hosts it? Very simple, RabbitMQ them, users can only access control granularity in virtual host. So, if you need to access the switch prohibit Group A Group B / queues / bindings, you must create a virtual host for the A and B respectively. Each server has a default RabbitMQ virtual host "/."
Switches: Exchange for forwarding messages, but it does not do storage, if not Queue bind to the Exchange, then it drops off directly over the message sent by the Producer. There is a more important concept here: routing keys. Message to the switch when the opportunity to interact forwarded to the corresponding queue, then what are forwarded to which queue, it is necessary according to the routing keys.
Binding: the switch is required and bound to queue, which as shown above, many to many relationship.
Switch (Exchange)
function of the switch primarily to receive messages and forwarded to bind the queue, the switch does not store the message after ack mode is enabled, the switch can not find the queue returns an error. Switches There are four types: Direct, topic, Headers and Fanout

Direct: direct type of behavior is "first match, then delivery" that is set at a routing_key binding when routing_key message matches will be delivered to the queue bound to the exchanger..
Topic: Press rule to forward messages (most flexible)
Headers: set header attribute parameter type switch
Fanout: forward the message to all queues binding
Direct the exchange
Direct the exchange RabbitMQ is the default mode switches, is the simplest mode, according to the key match to find the full text of the queue.

image

The first X - Q1 there is a binding key, the name is orange; X - Q2 have two binding key, the name for the black and green. When routing keys and the message corresponding to the binding key, then the message to know to which queue.

Ps: Why X Q2 to have a black, green, 2 Ge binding key it, a not on line yet? - This is mainly because there may Q3, and Q3 only information accepting black, and Q2 receive information not only black, but also to receive information of green.

Topic Exchange

Topic Exchange forwards the message is mainly based on wildcards. In this switch, the switch will be bound to the queue, and routing define a pattern, then, it is necessary between such wildcard routing mode and route matching key switch can forward the message.

In this mode switch:

Routing key must be a string of characters (.) Separated by periods, such as http://agreements.us, or agreements.eu.stockholm and so on.
Routing mode must contain an asterisk ( ), the main route for a key word matching the specified location, for example, a route pattern like this:. Agreements..b , then it can only match routing key is like this: The first word is the agreements, the fourth word is b. Pound sign (#) indicates the equivalent of one or more words, for example, a pattern matching is agreements.eu.berlin. #, Then to the beginning of the routing keys agreements.eu.berlin are possible.
When the specific code is sent as the first parameter represents a switch, the second parameter represents a routing key, i.e., the third parameter message. As follows:
rabbitTemplate.convertAndSend ( "testTopicExchange", "key1.ackey2", "! RabbitMQ the this IS");
Topic and direct the like, supports only "pattern" on the matching, the "dotted" in routing_key form may be used two wildcards:

  • It represents a word
  • Represents zero or more words
    Headers the Exchange
    headers are matched according to the rules, and compared to the direct use of topic fixedly routing_key, headers matching rule type is a custom in the queue switch binding, it will set a key-value pair rules, the message also includes a key-value pair (headers attributes), such as a pair of key-value pairs, or all of the match, the message is delivered to the corresponding queue.

Fanout Exchange

Fanout Exchange message broadcasting mode, regardless of routing or routing mode key, the message sent will bind to it all the queues, if configured routing_key will be ignored.

springboot integrated RabbitMQ
springboot integrated RabbitMQ is very simple, if you simply use very little configuration, springboot provides springboot-starter-amqp project support for a variety of messages.

Simple to use

1, pom configuration package, mainly to add support spring-boot-starter-amqp of

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

2, the configuration file

Configuring rabbitmq installation address, port and account information

1
2
3
4
5
6
spring.application.name=spirng-boot-rabbitmq

spring.rabbitmq.host= 127.0.0.1
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
Queue configuration
1
2
3
4
5
6
7
8
9
@Configuration
public class RabbitConfig {

@Bean
public Queue Queue() {
return new Queue("hello");
}

}

sender

1
2
3
4
5
6
7
8
9
10
11
12
13
14
rabbitTemplate is the default implementation provided springboot

public class HelloSender {

@Autowired
private AmqpTemplate rabbitTemplate;

public void send() {
String context = "hello " + new Date();
System.out.println("Sender : " + context);
this.rabbitTemplate.convertAndSend("hello", context);
}

}

Recipient

1
2
3
4
5
6
7
8
9
10
@Component
@RabbitListener(queues = "hello")
public class HelloReceiver {

@RabbitHandler
public void process(String hello) {
System.out.println("Receiver : " + hello);
}

}

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
@RunWith(SpringRunner.class)
@SpringBootTest
public class RabbitMqHelloTest {

@Autowired
private HelloSender helloSender;

@Test
public void hello() throws Exception {
helloSender.send();
}

}

注意,发送者和接收者的queue name必须一致,不然不能接收
多对多使用

一个发送者,N个接收者或者N个发送者和N个接收者会出现什么情况呢?

一对多发送

对上面的代码进行了小改造,接收端注册了两个Receiver,Receiver1和Receiver2,发送端加入参数计数,接收端打印接收到的参数,下面是测试代码,发送一百条消息,来观察两个接收端的执行效果。

1
2
3
4
5
6
@Test
public void oneToMany() throws Exception {
for (int i=0;i<100;i++){
neoSender.send(i);
}
}

结果如下:

1
2
3
4
5
6
7
8
9
10
Receiver 1: spirng boot neo queue ****** 11
Receiver 2: spirng boot neo queue ****** 12
Receiver 2: spirng boot neo queue ****** 14
Receiver 1: spirng boot neo queue ****** 13
Receiver 2: spirng boot neo queue ****** 15
Receiver 1: spirng boot neo queue ****** 16
Receiver 1: spirng boot neo queue ****** 18
Receiver 2: spirng boot neo queue ****** 17
Receiver 2: spirng boot neo queue ****** 19
Receiver 1: spirng boot neo queue ****** 20

根据返回结果得到以下结论 > 一个发送者,N个接受者,经过测试会均匀的将消息发送到N个接收者中

多对多发送

复制了一份发送者,加入标记,在一百个循环中相互交替发送

1
2
3
4
5
6
7
@Test
public void manyToMany() throws Exception {
for (int i=0;i<100;i++){
neoSender.send(i);
neoSender2.send(i);
}
}

结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
Receiver 1: spirng boot neo queue ****** 20
Receiver 2: spirng boot neo queue ****** 20
Receiver 1: spirng boot neo queue ****** 21
Receiver 2: spirng boot neo queue ****** 21
Receiver 1: spirng boot neo queue ****** 22
Receiver 2: spirng boot neo queue ****** 22
Receiver 1: spirng boot neo queue ****** 23
Receiver 2: spirng boot neo queue ****** 23
Receiver 1: spirng boot neo queue ****** 24
Receiver 2: spirng boot neo queue ****** 24
Receiver 1: spirng boot neo queue ****** 25
Receiver 2: spirng boot neo queue ****** 25

结论:和一对多一样,接收端仍然会均匀接收到消息
高级使用

对象的支持

springboot以及完美的支持对象的发送和接收,不需要格外的配置。

//发送者

1
2
3
4
public void send(User user) {
System.out.println("Sender object: " + user.toString());
this.rabbitTemplate.convertAndSend("object", user);
}

//接受者

1
2
3
4
@RabbitHandler
public void process(User user) {
System.out.println("Receiver object : " + user);
}

结果如下:

1
2
Sender object: User{name='neo', pass='123456'}
Receiver object : User{name='neo', pass='123456'}

Topic Exchange

topic 是RabbitMQ中最灵活的一种方式,可以根据routing_key自由的绑定不同的队列

首先对topic规则配置,这里使用两个队列来测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Configuration
public class TopicRabbitConfig {

final static String message = "topic.message";
final static String messages = "topic.messages";

@Bean
public Queue queueMessage() {
return new Queue(TopicRabbitConfig.message);
}

@Bean
public Queue queueMessages() {
return new Queue(TopicRabbitConfig.messages);
}

@Bean
TopicExchange exchange() {
return new TopicExchange("exchange");
}

@Bean
Binding bindingExchangeMessage(Queue queueMessage, TopicExchange exchange) {
return BindingBuilder.bind(queueMessage).to(exchange).with("topic.message");
}

@Bean
Binding bindingExchangeMessages(Queue queueMessages, TopicExchange exchange) {
return BindingBuilder.bind(queueMessages).to(exchange).with("topic.#");
}
}

使用queueMessages同时匹配两个队列,queueMessage只匹配”topic.message”队列

1
2
3
4
5
6
7
8
9
10
11
public void send1() {
String context = "hi, i am message 1";
System.out.println("Sender : " + context);
this.rabbitTemplate.convertAndSend("exchange", "topic.message", context);
}

public void send2() {
String context = "hi, i am messages 2";
System.out.println("Sender : " + context);
this.rabbitTemplate.convertAndSend("exchange", "topic.messages", context);
}

发送send1会匹配到topic.#和topic.message 两个Receiver都可以收到消息,发送send2只有topic.#可以匹配所有只有Receiver2监听到消息

Fanout Exchange

Fanout 就是我们熟悉的广播模式或者订阅模式,给Fanout交换机发送消息,绑定了这个交换机的所有队列都收到这个消息。

Fanout 相关配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
@Configuration
public class FanoutRabbitConfig {

@Bean
public Queue AMessage() {
return new Queue("fanout.A");
}

@Bean
public Queue BMessage() {
return new Queue("fanout.B");
}

@Bean
public Queue CMessage() {
return new Queue("fanout.C");
}

@Bean
FanoutExchange fanoutExchange() {
return new FanoutExchange("fanoutExchange");
}

@Bean
Binding bindingExchangeA(Queue AMessage,FanoutExchange fanoutExchange) {
return BindingBuilder.bind(AMessage).to(fanoutExchange);
}

@Bean
Binding bindingExchangeB(Queue BMessage, FanoutExchange fanoutExchange) {
return BindingBuilder.bind(BMessage).to(fanoutExchange);
}

@Bean
Binding bindingExchangeC(Queue CMessage, FanoutExchange fanoutExchange) {
return BindingBuilder.bind(CMessage).to(fanoutExchange);
}

}

这里使用了A、B、C三个队列绑定到Fanout交换机上面,发送端的routing_key写任何字符都会被忽略:

1
2
3
4
5
public void send() {
String context = "hi, fanout msg ";
System.out.println("Sender : " + context);
this.rabbitTemplate.convertAndSend("fanoutExchange","", context);
}

结果如下:

结果说明,绑定到fanout交换机上面的队列都收到了消息

消息持久化

在生产环境中,我们需要考虑万一生产者挂了,消费者挂了,或者 rabbitmq 挂了怎么样。一般来说,如果生产者挂了或者消费者挂了,其实是没有影响,因为消息就在队列里面。那么万一 rabbitmq 挂了,之前在队列里面的消息怎么办,其实可以做消息持久化,RabbitMQ 会把信息保存在磁盘上。

做法是可以先从 Connection 对象中拿到一个 Channel 信道对象,然后再可以通过该对象设置 消息持久化。

The producer or consumer reconnection
here Spring automatic reconnection mechanism.

ACK acknowledgment mechanism

Each Consumer may take some time to process the data has been received. If in the process, Consumer wrong, abnormal exit, while the data processing has not been completed, so unfortunately, this data is lost. Because we use no-ack way to confirm that is to say, each time after receiving the Consumer data, regardless of whether the process is completed, RabbitMQ Server will immediately put this Message marked as complete, and then removed from the queue.

If a Consumer quit unexpectedly, and its data can be processed further Consumer process, so that the data in this case would not have lost (Note that this is the case).
In order to ensure that data is not lost, RabbitMQ supports message confirmation mechanism that acknowledgments. In order to ensure that data can be processed correctly rather than just being received Consumer, then we can not use no-ack. Ack but should be transmitted after the data is processed.

Ack sent after processing the data, is to tell RabbitMQ data has been received, processed, RabbitMQ can go safely delete it.
If the Consumer withdrew but did not send ack, then RabbitMQ will send this Message to the next Consumer. This ensures that data will not be lost in case of Consumer abnormal exit.

If you have questions, click here to ACK mechanism: https://www.zhihu.com/question/41976893

Original: Big Box  Springboot integration RabbitMp


Guess you like

Origin www.cnblogs.com/petewell/p/11422306.html