Application scenarios

1. Asynchronous processing

Scenario: After the user is registered, the user needs to send an email or SMS message. The traditional methods are as follows:

  • Serial mode: After the registration information is written into the database, a successful email is sent. After the email is sent, a successful SMS message is sent. The registration information is returned to the client only after the preceding three tasks are complete.

    Here’s the problem: the email and SMS are not required, it’s just a notification, and the serial approach makes the client wait longer than necessary.

  • In parallel mode: After the registration information is written to the database, an email or SMS message is sent at the same time. The registration information is returned to the client only after the preceding three tasks are complete. The parallel approach can increase the processing time correspondingly.

  • Message queue (Publish/Subscribe) : after the registration information is written to the database, the successful registration information is directly returned to the client. Then it sends the message of successful registration email and message of successful registration SMS to Exchange, and Exchange distributes the message to two queues, which asynchronously inform corresponding consumers to consume.

2. Apply decoupling

Scenario: Double 11 is the shopping frenzy festival. When users place an order, the order system needs to notify the inventory system. The traditional way is that the order system calls the interface of the inventory system.

There is a downside to this approach: when the inventory system fails, the order system call fails, resulting in the entire request failing. High coupling between the order system and the inventory system.

Importing message queues (“Hello World! After the) :

  • Order system: after the user places an order, the order system completes the persistent processing, and then writes the message to the message queue, and returns the user to place the order successfully.
  • Inventory system: Message queues send messages to the inventory system, which acts accordingly. Even if the inventory system fails, the message queue can ensure the reliable delivery of messages and will not lead to message loss.

3. Flow peak cutting

Scenario: If a user accesses an application directly, the application will be suspended due to heavy traffic. To solve this problem, messages are usually queued at the front of the application.

Joining the message queue (“Hello World! ) function is:

  • The number of active people can be controlled, and if the number of order messages exceeds the threshold specified by MQ, new order messages are discarded.
  • The system only connects to MQ. Therefore, the system only consumes messages sent by MQ. Multiple requests do not access the system at the same time, resulting in system breakdown.

The specific implementation process is as follows:

  1. When the server receives the user request, it first writes to MQ. If the number of messages written to MQ exceeds the preset maximum MQ value, the user request is discarded or the error page is redirected.
  2. The system processes the messages in sequence according to the message queue.

Spring Boot integrates RabbitMQ

Spring Boot is easy to integrate with RabbitMQ because it further encapsulates RabbitMQ and the Spring framework naturally supports RabbitMQ’s AMQP protocol. So Spring Boot provides very friendly support for RabbitMQ.

1. Basic environment construction

  1. Spring Boot version: 2.2.5.release

  2. Introduce dependencies in POM.xml

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-amqp</artifactId>
    </dependency>
    Copy the code
  3. Creating a Virtual Host

    You can create multiple Virtual hosts for a RabbitMQ Server. It is recommended that you create a Virtual host for each system or service. The name of the Virtual host corresponds to the system. Similar to the concept of a Database in a Database (a system creates a Database). Producers and consumers who want to communicate with RabbitMQ Server must bind to the same Virtual host and then connect to that Virtual host to produce and consume messages.

    The newly created Virtual host can only be accessed by guest, because guest is a guest and has the highest permission to access all Virtual hosts, including the root Virtual host (/).

  4. Create a user

    You are advised to create a User for each system and name the User according to the system.

    The reason why a system corresponds to a Virtual host and a User is to make the system independent from each other and not interfere with each other.

    Note: Exchanges and Message queues do not need to be created manually in the RabbitMQ console, they can be created programmatically.

  5. The user is bound to a virtual host

    The newly created User has permission No Access and cannot access any Virtual hosts.

    Click the user name you want to bind in the table

  6. The configuration application. Yml

    spring:
      .
      # the RabbitMQ configuration
      rabbitmq:
        # IP
        host: localhost
        # port
        port: 5672
        # username
        username: postilhub
        # your password
        password: 123456
        # Virtual host
        virtual-host: /postilhub
    Copy the code
  7. Injection RabbitTemplate

    After the RabbitMQ Server connection is established, Spring automatically instantiates a RabbitTemplate object, which can be injected as needed.

    @Autowired
    private RabbitTemplate rabbitTemplate;
    Copy the code

2. “Hello World!” model

Producer development

@Test
public void produce(a) {
    // Send a single message. The first parameter is the queue name and the second parameter is the message content
    rabbitTemplate.convertAndSend("queue"."Hello World!");
}
Copy the code

Note: In the above code, the producer specified the queue, but if the queue does not exist, the queue will not be created just because the producer specified the queue. Producers cannot create queues, because if a queue has no consumers consuming messages, there is no point in its existence. So it will only be created if the consumer exists and listens on the queue, if the queue does not exist.

Consumer development

@Component
@RabbitListener(queuesToDeclare = @Queue(value = "queue", durable = "false", autoDelete = "true"))
public class Consumer {

    / * * *@paramThe message argument is the content of the message */
    @RabbitHandler
    public void consume(String message) { System.out.println(message); }}Copy the code
  • RabbitListener: indicates that the class/method is a consumer and specifies the queue on which the consumer listens, or if it does not exist, create a queue. You can also set the features of the durable, autoDelete, and EXCLUSIVE queues. True indicates that the queue is enabled, and false indicates that the queue is disabled. By default, queues that are durable are true, and both autoDelete and EXCLUSIVE are false.

  • RabbitHandler: represents the callback method of the consumer when MQ is sent to the consumer.

3. Work Queues model

Producer development

@Test
public void produce(a) {
    // Circulates multiple messages
    for (int i = 0; i < 10; i++) {
        rabbitTemplate.convertAndSend("queue"."Work Model"+ i); }}Copy the code

Consumer development

@Component
public class Consumer {

    // Consumer 1
    @RabbitListener(queuesToDeclare = @Queue("queue"))
    public void consume1(String message) {
        System.out.println("consumer1:" + message);
    }

    // Consumer 2
    @RabbitListener(queuesToDeclare = @Queue("queue"))
    public void consume2(String message) {
        System.out.println("consumer2:"+ message); }}Copy the code

Note: @rabbithandler is no longer required when @rabbitListener is used in a method.

4. The Publish/Subscribe model

Producer development

@Test
public void produce(a) {
    // The first parameter is the switch name, the second parameter is the queue name ("" represents the random name of the queue used to create temporary queues), and the third parameter is the message content
    rabbitTemplate.convertAndSend("fanout".""."Publish/Subscribe Model");
}
Copy the code

Note: In the above code, although the producer specified the switch, the switch cannot be created, just like the queue. The creation of the switch depends on the consumer.

Consumer development

@Component
public class Consumer {

    @rabbitListener (Bindings = {@queuebinding (// create a temporary Queue and listen for value = @queue, Exchange = @exchange (value = "fanout", type = "fanout"))}
    public void consume1(String message) {
        System.out.println("consumer1:" + message);
    }

    @RabbitListener(bindings = { @QueueBinding( value = @Queue, exchange = @Exchange(value = "fanout", type = "fanout") ) })
    public void consume2(String message) {
        System.out.println("consumer2:"+ message); }}Copy the code

The temporary queue names are randomly generated by RabbitMQ and deleted automatically after all messages are sent to consumers.

5. Routing model

Producer development

@Test
public void produce(a) {
    // The first parameter is the switch name the second parameter is the switch RoutingKey and the third parameter is the message content
    rabbitTemplate.convertAndSend("direct"."rabbit.info"."Routing Model");
}
Copy the code

Consumer development

@Component
public class Consumer {

    @rabbitListener (bindings = {@queuebinding (// create temporary Queue and listen value = @queue, // temporary Queue bind switch (switch default type is direct, Exchange = @exchange (value = "direct", type = "direct"), RoutingKey = {"rabbit.info", "rabbit.error"} ) })
    public void consume1(String message) {
        System.out.println("consumer1:" + message);
    }

    @RabbitListener(bindings = { @QueueBinding( value = @Queue, exchange = @Exchange(value = "direct"), key = {"rabbit.info"} ) })
    public void consume2(String message) {
        System.out.println("consumer2:"+ message); }}Copy the code

6. Switchable viewer model

1. Producer development

@Test
public void produce(a) {
    // The first parameter is the switch name the second parameter is the switch RoutingKey and the third parameter is the message content
    rabbitTemplate.convertAndSend("topic"."rabbit.info"."Topic Model");
}
Copy the code

2. Consumer development

@Component
public class Consumer {

    @rabbitListener (Bindings = {@queuebinding (// create a temporary Queue and listen for value = @queue, Exchange = @exchange (name = "topic", type = "topic"), Key = {"rabbit.#", "rabbit.*"})})
    public void consume1(String message) {
        System.out.println("consumer1:" + message);
    }

    @RabbitListener(bindings = { @QueueBinding( value = @Queue, exchange = @Exchange(value = "topic", type = "topic"), key = {"rabbit.*"} ) })
    public void consume2(String message) {
        System.out.println("consumer2:"+ message); }}Copy the code