Spring AMQP uses

Description: SpringAMQP (official website: https://spring.io/projects/spring-amqp) is a set of templates based on RabbitMQ packaging, and uses SpringBoot to realize automatic assembly, which is very convenient to use. Installation and original use reference: http://t.csdn.cn/51qyD

basic operation

Create two modules, one for sending messages (sender) and one for receiving messages (receiver), both modules have a common parent module

Step 1: Add dependencies

In the pom.xml file of the parent module, add dependencies as follows:

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.9.RELEASE</version>
        <relativePath/>
    </parent>

    <dependencies>
        <!--lombok依赖,用于生成set、get、toString方法-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <!--AMQP依赖,包含RabbitMQ-->
        <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>

Step 2: Create a configuration file

The content of the configuration file (application.yml) is as follows, and the content of the two modules is the same

spring:
  rabbitmq:
    # MQ ip地址
    host: XXX.XXX.XXX.XXX
    # MQ的端口号
    port: 5672
    # 虚拟主机 每个用户单独对应一个 不同用户之间无法访问彼此的虚拟主机
    virtual-host: /
    # 用户名
    username: root
    # 密码
    password: 123456

Step 3: Create the Listener class

On the receiving side, create a listener class to receive messages, as follows:

import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
public class RabbitListenerDemo {
    
    

    @RabbitListener(queues = "demo.queue")
    public void listenDemoQueueMessage(String msg){
    
    
        System.out.println("msg = " + msg);
    }
}

Step 4: Write the sender code

In the test class of the sender, write the test code and send a message to the receiver, where the RunWith() annotation is used to build the context in which the program runs;

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.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class SenderTest {
    
    

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Test
    public void testSender() {
    
    
        rabbitTemplate.convertAndSend("demo.queue","hello rabbit mq!");
    }
}

Step Five: Start

Start the receiver first (this is because, if the queue does not exist on the RabbitMQ management platform, starting the sender first will cause message loss, and starting the receiver first, RabbitMQ will first create a queue according to the queue name), and then start the sender;

It can be seen that the test is completed and the receiver can receive the message

insert image description here

work queue

The actual business situation is that one sender may have multiple receivers to receive, and the processing efficiency of the receivers may vary. In this way, the code of the receiver can be written as follows, using thread sleep to simulate the execution efficiency of the receiver, and then setting variables to count the number of executions of each receiver:

(RabbitListenerDemo.java)

import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
public class RabbitListenerDemo {
    
    
    
    private static int count1 = 0;
    private static int count2 = 0;
    private static int count3 = 0;

    @RabbitListener(queues = "demo.queue")
    public void listenDemoQueueMessage1(String msg) throws InterruptedException {
    
    
        System.out.println("msg1 = " + msg + "======= count1 =" + (++count1));
        Thread.sleep(10);
    }

    @RabbitListener(queues = "demo.queue")
    public void listenDemoQueueMessage2(String msg) throws InterruptedException {
    
    
        System.out.println("msg2 = " + msg + "======= count2 =" + (++count2));
        Thread.sleep(20);
    }

    @RabbitListener(queues = "demo.queue")
    public void listenDemoQueueMessage3(String msg) throws InterruptedException {
    
    
        System.out.println("msg3 = " + msg + "======= count3 =" + (++count3));
        Thread.sleep(50);
    }
}

(SenderTest: send 200 times in a loop, sleep for 10 milliseconds)

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.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class SenderTest {
    
    

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Test
    public void testSender() throws InterruptedException {
    
    
        for (int i = 0; i < 200; i++) {
    
    
            rabbitTemplate.convertAndSend("demo.queue","hello rabbit mq!======>" + i);
            Thread.sleep(10);
        }
    }
}

Start, you can see that No. 3, which has the lowest execution efficiency, has received the same amount of messages as No. 1 and No. 2.

insert image description here

This is because RabbitMQ has a default allocation strategy, so that each receiver can receive the same amount of messages, rather than the faster the processing, the more processing. You can add this configuration in the configuration file of the receiver, indicating that each receiver can only process one message and one message (it can be speculated that the default is to distribute the requests evenly according to the number of receivers, and then let them handle them separately);

spring:
  rabbitmq:
    listener:
      simple:
        prefetch: 1

Restart the test, you can see that the effect of "the able one works more" has been achieved

insert image description here

publish/subscribe

Publish/subscribe means that before the message is sent to the queue, the queue information bound to the message is judged, and then the message is distributed according to the bound queue;

insert image description here

According to the distribution situation, it can be divided into the following three types:

  • Broadcast (Fanout): the message is distributed to all queues;

  • Routing (Direct): The message is only distributed to the queue with the keyword (RoutingKey);

  • Topic: Messages are only distributed to eligible queues;

Fanout (broadcast)

Create a broadcast configuration class for binding queues and broadcast switches (FanoutExchange);

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


/**
 * 广播配置类
 */
@Configuration
public class FanoutConfig {
    
    

    /**
     * 声明交换机
     * @return
     */
    @Bean
    public FanoutExchange fanoutExchange(){
    
    
        return new FanoutExchange("essay.fanout");
    }

    /**
     * 生成第一个队列
     * @return
     */
    @Bean
    public Queue fanoutQueue1(){
    
    
        return new Queue("fanout.queue1");
    }

    /**
     * 绑定队列和交换机
     *
     * @return
     */
    @Bean
    public Binding bindingQueue1(){
    
    
        return BindingBuilder.bind(fanoutQueue1()).to(fanoutExchange());
    }

    /**
     * 生成第二个队列
     * @return
     */
    @Bean
    public Queue fanoutQueue2(){
    
    
        return new Queue("fanout.queue2");
    }

    /**
     * 绑定队列和交换机
     *
     * @return
     */
    @Bean
    public Binding bindingQueue2(){
    
    
        return BindingBuilder.bind(fanoutQueue2()).to(fanoutExchange());
    }
}

recipient code

    @RabbitListener(queues = "fanout.queue1")
    public void listenFanoutQueue1(String msg){
    
    
        System.out.println("接收者1接收到了消息:" + msg);
    }

    @RabbitListener(queues = "fanout.queue2")
    public void listenFanoutQueue2(String msg){
    
    
        System.out.println("接收者2接收到了消息:" + msg);
    }

Sender code: the message is not sent directly to the queue, but to an exchange;

    @Test
    public void fanoutExchangeTest(){
    
    
    	// 第二个参数是routeKey(路由转发关键字)不能不加,可以为空字符串
        rabbitTemplate.convertAndSend("essay.fanout","", "hello everyone!");
    }

As a result of the test, each queue has received messages and sent them to their respective receivers

insert image description here

Direct (routing)

On the receiving method of the receiver, create the corresponding queue and routing switch, and set the routeKey (routing key), receiver 1 (group1, group2), receiver 2 (group1, group3)

    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(name = "direct.queue1"),
            exchange = @Exchange(name = "essay.direct",type = ExchangeTypes.DIRECT),
            key = {
    
    "group1", "group2"}
    ))
    public void listenDirectQueue1(String msg){
    
    
        System.out.println("接收者1号接收到了消息:" + msg);
    }

    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(name = "direct.queue2"),
            exchange = @Exchange(name = "essay.direct",type = ExchangeTypes.DIRECT),
            key = {
    
    "group1", "group3"}
    ))
    public void listenDirectQueue2(String msg){
    
    
        System.out.println("接收者2号接收到了消息:" + msg);
    }

The sender sends a message, routeKey = group1

	rabbitTemplate.convertAndSend("essay.direct","group1", "hello group1!");

insert image description here


The sender sends a message, routeKey = group2

	rabbitTemplate.convertAndSend("essay.direct","group2", "hello group2!");

Only receiver 1 owns group2, so only receiver 1 receives the message

insert image description here

Topic

Similar to routing, the difference is the type of ExchangeTypes and the composition of the key , the key is composed of wildcards and keywords

  • #: Indicates one or more characters;

  • *: represents a character;

The following three keys represent respectively:

  • group.#: Indicates that all messages starting with "group" are sent;

  • #.class: Indicates that all messages ending with "class" are sent;

  • *.person: Indicates two characters and all messages ending with "person" are sent;

    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(name = "topic.queue1"),
            exchange = @Exchange(name = "essay.topic",type = ExchangeTypes.TOPIC),
            key = "group.#"
    ))
    public void listenTopicQueue1(String msg){
    
    
        System.out.println("接收者1号接收到了消息:" + msg);
    }

    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(name = "topic.queue2"),
            exchange = @Exchange(name = "essay.topic",type = ExchangeTypes.TOPIC),
            key = "#.class"
    ))
    public void listenTopicQueue2(String msg){
    
    
        System.out.println("接收者2号接收到了消息:" + msg);
    }

    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(name = "topic.queue3"),
            exchange = @Exchange(name = "essay.topic",type = ExchangeTypes.TOPIC),
            key = "*.person"
    ))
    public void listenTopicQueue3(String msg){
    
    
        System.out.println("接收者3号接收到了消息:" + msg);
    }

sender test

	// 1号接收
	rabbitTemplate.convertAndSend("essay.topic","group.b.c.d", "hello NO.1!");
	
	// 2号接收
	rabbitTemplate.convertAndSend("essay.topic","b.c.d.class", "hello NO.2!");
	
	// 3号接收
	rabbitTemplate.convertAndSend("essay.topic","b.person", "hello NO.3!");

Start, the test results are as follows, you can see that the expected results have been achieved

insert image description here

Summarize

RabbitMQ is an asynchronous communication technology. SpringAMQP is a template based on RabbitMQ, which can save the tedious operation of RabbitMQ (establish connection, set connection parameters, create channel, create queue, send message/receive message).

In addition, SpringAMQP can be used to establish work queues, publish/subscribe and other modes, in which the work queue can be set spring.rabbitmq.listener.simple.prefetch=1to achieve the effect of "capable people do more work";

The publish/subscribe mode is divided into broadcast, route, and topic. The broadcast mode needs to manually establish the association between the queue and the routing switch. The difference between routing and topic lies in the type of routing switch and the format of the routing keyword.

Guess you like

Origin blog.csdn.net/qq_42108331/article/details/131815322