SpringBoot: On the RabbitMQ message queue

SpringBoot To simplify the  Spring creation of applications, run, debug, deploy a range of issues such as the birth of the product, characteristics of the automatic assembly so that we can better focus on the business itself, rather than external XML configuration, we simply follow the norms related to the introduction of dependence can easily build a WEB project

Full MQ (Message Queue), also known as a message queue, an asynchronous communication middleware. It can be understood as the post office, the sender passes the message to the post office, and then by the post office to help us send a message to a specific recipient (consumer), the specific process of sending and the time we do not care, it does not bother me that other things . Common MQ has kafka, activemq, zeromq, rabbitmq and so on, the major MQ comparison, advantages and disadvantages can be self Google

rabbitmq

RabbitMQ AMQP protocol is followed , a highly concurrent oriented erlanngdevelopment language is made in real time with the high reliability requirements messaging, client support for multiple languages. Support 延迟队列(这是一个非常有用的功能)....

Basic concepts

Broker: is simply a message queue server entity
Exchange: Message switch that specifies what the message according to the rules, the route to which queue
: Queue message queue carriers, each message will be put into one or more queues
Binding: binding, it the role is to exchangeand queuebind them in accordance with the routing rules
routing Key: routing keywords exchangefor message delivery based on keyword
vhost: Web Hosting, a brokerwhere you can set up multiple vhost, as a different user privilege separation
producer: news producer, program is to deliver a message of
consumer: the message consumer, is that the program accepts the message
channel: message channels, in each client's connection can be established more channel, each channelrepresenting a conversation task

Based Centos7.x installation please refer to: http://blog.battcn.com/2017/08/20/linux/linux-centos7-ribbitmq/

Common scenarios

  1. Send mail: message delivery to the user registration rabbitmq, the message sent by the asynchronous message consumer, improve the response speed of the system
  2. Traffic clipping: spike activity in a wide range of general application, spike because of the amount of traffic, cause the application to hang, in order to solve this problem, the general message queue to join the front-end application. Used to control the number of activities, orders will exceed a certain threshold of this discarded. Ease the short-term, high-traffic overwhelmed by applications.
  3. Orders timeout: use rabbitmqthe delay queue, can be very simple implementation of orders timeout features, such as the next one 30 minutes after the user does not pay to cancel the order
  4. There are more scenarios will not list them ... ..

Import dependence

In  pom.xml add  spring-boot-starter-amqp-dependent

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-amqp</artifactId>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.46</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

Configuration Properties

In  application.properties the configuration file rabbitmqcontent, the configuration is worth noting that this manual switching ACK

spring.rabbitmq.username=battcn
spring.rabbitmq.password=battcn
spring.rabbitmq.host=192.168.0.133
spring.rabbitmq.port=5672
spring.rabbitmq.virtual-host=/
# 手动ACK 不开启自动ACK模式,目的是防止报错后未正确处理消息丢失 默认 为 none
spring.rabbitmq.listener.simple.acknowledge-mode=manual

Specific coding

Defining the Queue

If too manually created or RabbitMQalready exists in the queue then the following codes may be omitted ...

package com.battcn.config;

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

/**
 * RabbitMQ配置
 *
 * @author Levin
 * @since 2018/4/11 0011
 */
@Configuration
public class RabbitConfig {

    public static final String DEFAULT_BOOK_QUEUE = "dev.book.register.default.queue";
    public static final String MANUAL_BOOK_QUEUE = "dev.book.register.manual.queue";

    @Bean
    public Queue defaultBookQueue() {
        // 第一个是 QUEUE 的名字,第二个是消息是否需要持久化处理
        return new Queue(DEFAULT_BOOK_QUEUE, true);
    }

    @Bean
    public Queue manualBookQueue() {
        // 第一个是 QUEUE 的名字,第二个是消息是否需要持久化处理
        return new Queue(MANUAL_BOOK_QUEUE, true);
    }
}

Entity class

Creating a Bookclass

public class Book implements java.io.Serializable {

    private static final long serialVersionUID = -2164058270260403154L;

    private String id;
    private String name;
    // 省略get set ...
}

Controller

Preparation of a Controllerclass, a message transmission job

package com.battcn.controller;

import com.battcn.config.RabbitConfig;
import com.battcn.entity.Book;
import com.battcn.handler.BookHandler;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author Levin
 * @since 2018/4/2 0002
 */
@RestController
@RequestMapping(value = "/books")
public class BookController {

    private final RabbitTemplate rabbitTemplate;

    @Autowired
    public BookController(RabbitTemplate rabbitTemplate) {
        this.rabbitTemplate = rabbitTemplate;
    }

    /**
     * this.rabbitTemplate.convertAndSend(RabbitConfig.DEFAULT_BOOK_QUEUE, book); 对应 {@link BookHandler#listenerAutoAck}
     * this.rabbitTemplate.convertAndSend(RabbitConfig.MANUAL_BOOK_QUEUE, book); 对应 {@link BookHandler#listenerManualAck}
     */
    @GetMapping
    public void defaultMessage() {
        Book book = new Book();
        book.setId("1");
        book.setName("一起来学Spring Boot");
        this.rabbitTemplate.convertAndSend(RabbitConfig.DEFAULT_BOOK_QUEUE, book);
        this.rabbitTemplate.convertAndSend(RabbitConfig.MANUAL_BOOK_QUEUE, book);
    }
}

Consumer news

By default,  spring-boot-data-amqp automatic ACKmechanism, it means that MQ will automatically help us to complete after the message consumer ACK, so dependent on the existence of such a question: If the error message will not be lost, it will consume an infinite loop, it is very easy to Disk space depletion, although you can configure the number of consumption but this approach also beneath elegant. Currently more it recommended that we consume and then manually ACK error messages transferred to other message queue, do the compensation process

package com.battcn.handler;

import com.battcn.config.RabbitConfig;
import com.battcn.entity.Book;
import com.rabbitmq.client.Channel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

import java.io.IOException;

/**
 * BOOK_QUEUE 消费者
 *
 * @author Levin
 * @since 2018/4/11 0011
 */
@Component
public class BookHandler {

    private static final Logger log = LoggerFactory.getLogger(BookHandler.class);

    /**
     * <p>TODO 该方案是 spring-boot-data-amqp 默认的方式,不太推荐。具体推荐使用  listenerManualAck()</p>
     * 默认情况下,如果没有配置手动ACK, 那么Spring Data AMQP 会在消息消费完毕后自动帮我们去ACK
     * 存在问题:如果报错了,消息不会丢失,但是会无限循环消费,一直报错,如果开启了错误日志很容易就吧磁盘空间耗完
     * 解决方案:手动ACK,或者try-catch 然后在 catch 里面讲错误的消息转移到其它的系列中去
     * spring.rabbitmq.listener.simple.acknowledge-mode=manual
     * <p>
     *
     * @param book 监听的内容
     */
    @RabbitListener(queues = {RabbitConfig.DEFAULT_BOOK_QUEUE})
    public void listenerAutoAck(Book book, Message message, Channel channel) {
        // TODO 如果手动ACK,消息会被监听消费,但是消息在队列中依旧存在,如果 未配置 acknowledge-mode 默认是会在消费完毕后自动ACK掉
        final long deliveryTag = message.getMessageProperties().getDeliveryTag();
        try {
            log.info("[listenerAutoAck 监听的消息] - [{}]", book.toString());
            // TODO 通知 MQ 消息已被成功消费,可以ACK了
            channel.basicAck(deliveryTag, false);
        } catch (IOException e) {
            try {
                // TODO 处理失败,重新压入MQ
                channel.basicRecover();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }
    }

    @RabbitListener(queues = {RabbitConfig.MANUAL_BOOK_QUEUE})
    public void listenerManualAck(Book book, Message message, Channel channel) {
        log.info("[listenerManualAck 监听的消息] - [{}]", book.toString());
        try {
            // TODO 通知 MQ 消息已被成功消费,可以ACK了
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        } catch (IOException e) {
            // TODO 如果报错了,那么我们可以进行容错处理,比如转移当前消息进入其它队列
        }
    }
}

The main function

package com.battcn;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * @author Levin
 */
@SpringBootApplication
public class Chapter11Application {

    public static void main(String[] args) {
        SpringApplication.run(Chapter11Application.class, args);
    }
}

test

Upon completion Preparations start Chapter11Application access http: // localhost: 8080 / books  will see something like this, it means that everything is normal ....

2018-05-22 19:04:26.708  INFO 23752 --- [cTaskExecutor-1] com.battcn.handler.BookHandler           : [listenerAutoAck 监听的消息] - [com.battcn.entity.Book@77d8be18]
2018-05-22 19:04:26.709  INFO 23752 --- [cTaskExecutor-1] com.battcn.handler.BookHandler           : [listenerManualAck 监听的消息] - [com.battcn.entity.Book@8bb452]

to sum up

At present, many chiefs have written about  SpringBoot  tutorial, and any similarity, please bear with me, this tutorial is based on the latest  spring-boot-starter-parent:2.0.2.RELEASEwritten, including the new version features will be introduced together ...

Guess you like

Origin blog.51cto.com/14230003/2415210