[Java] SpringBoot integrate RabbitMQ

Introduction

  RabbitMQ i.e. a message queue, and is mainly used to achieve asynchronous decoupling applications, but also can play a message buffer, a message distribution effect.

  RabbitMQ achieve the AMQP (Advanced Message Queuing Protocol) message of a middleware, the AMQP, i.e., Advanced Message Queuing Protocol, Advanced Message Queuing Protocol, is an open standard application-layer protocol for message-oriented middleware design.

  In the project, without some return immediately extracted and time-consuming operation, we made asynchronous processing, and this processing asynchronous requests the server to save a large response time and improve system throughput.

  This article deals RabbitMQ and details how to use SpringBoot in.

Simple concept

  • Broker - is simply an entity message queue server.
  • Exchange - message router forwards the message to the queue bound by any rules specified message, which is routed to the queue.
  • Queue - a message queue for storing messages, each message will be put into one or more queues.
  • Binding - binding, its role is to bind the Queue Exchange and in accordance with the routing rules.
  • RoutingKey - routing keywords, Exchange for message delivery according to the keyword.
  • Producter - message producer, the program that generated the message.
  • Consumer - news consumers, receive messages program.
  • Channel - message channels, each client's connection can establish multiple Channel, each channel representing a session.

  Message queues are generally producer will send messages to the queue, the queue monitor consumers to consume. rabbitmq in a virtual host (default /) to hold one or more switches (Exchange). Users can only access control granularity in virtual hosts, switches, according to a certain strategy (RoutingKey) bindings (Binding) to the queue (Queue), so that producers and the queue is no direct link, but the message sent by the switch, switch then forwards the message on the queue to the corresponding binding.

  He says the switch (Exchange) as an important concept of a unique rabbitmq, separate out speaking about. We use the most common are 4 types:

  1. Direct: first match, and then delivery. I.e. a routing_key set at bind time routing_key message matches will be delivered to the queue bound to the exchanger. Queues with the switch must be precise correspondence relationship, which is the simplest.
  2. Topic: forwarded message is mainly based on wildcards. In this switch, the switch will be bound to the queue and define a routing mode, the wildcard is necessary after routing between this model and the routing switch to forward messages matching keys that can be considered Direct flexible version
  3. Headers: The rule is matched, and compared to the direct use of topic fixedly routingkey, headers matching rule type is a custom, when the queue of the exchanger will bind the key-value be set to a rule, the message comprising a set of key-value pair (attribute headers), when these have to match all the key-pair or a message is delivered to the queue corresponding to
  4. Fanout: message broadcast mode, regardless of routing or routing mode key, the message sent will bind to it all the queues, if configured routingkey will be ignored

  How to understand the principle of exchange of posted messages, the latter is relatively simple.

Ack mechanism

  RabbitMQ message queue of the program, there are a very important message acknowledgment mechanism called the probability, that is Ack mechanism, here I need to talk about alone.

  Each Consumer may take some time to process the data has been received. If in the process, Consumer wrong or 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.

  Here and did not use the timeout mechanism. RabbitMQ interrupted only to confirm that the Message has not been properly treated by the Consumer connection. In other words, RabbitMQ to the Consumer long enough time to do data processing. So even if you interrupt the Recieve.cs by Ctr-C, then Message will not be lost, it will be distributed to the next Consumer.

  If you forget ack, then the consequences will be severe. When Consumer exit, Message redistribute. And RabbitMQ will take up more and more memory because RabbitMQ will run for a long time, so this "memory leak" is fatal.

  The following example I will use the manual mode Ack.

SpringBoot integrated

maven dependence

        <!--Rabbit MQ-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>

Profiles

Add rabbitmq configuration in application.yml in:

spring:
  rabbitmq:
    host: 127.0.0.1
    port: 5672
    username: admin
    password: 123456
    publisher-confirms: true # support the release confirmed
    publisher-returns: true # supports publishing return
    listener:
      simple:
        acknowledge-mode: manual # manual answer
        concurrency: # 1 specifies the minimum number of consumers
        max-concurrency: 1 # specify the maximum number of consumers
        retry:
          enabled: true # support retry

Configuration class

  RabbitMQ is the core class configuration class, in which custom template class, switch statement, queues, switch to bind the queue.

  Here I declare a Direct type of switch, and bound to a queue to test the key routes through Direct mode, also declared Fanout types of switches, and bind to two queues to test broadcast mode.

@Configuration
@ slf4j
public class RabbitConfig {
    @Resource
    private RabbitTemplate rabbitTemplate;

    /**
     * Customized amqp templates can be customized more
     * <p>
     * <p>
     * Template class is defined here as the message converter Jackson
     * ConfirmCallback callback interfaces provide for receiving i.e. ack message to the message sent to switch RabbitMQ ack Exchange
     * ReturnCallback interfaces provide for RabbitMQ message to the switch, without the callback queue corresponding to the exchanger Binding i.e. not send any message in a queue ack
     *
     * @Return The AMQP Template
      * / 
    // @Primary 
    @Bean
     public AmqpTemplate amqpTemplate () {
         // use jackson message converter 
        rabbitTemplate.setMessageConverter ( new new Jackson2JsonMessageConverter ());
        rabbitTemplate.setEncoding ( "UTF-. 8" );
         // message returned to the queue fails, yml-configure Returns Publisher: to true 
        rabbitTemplate.setMandatory ( to true );
        rabbitTemplate.setReturnCallback((message, replyCode, replyText, exchange, routingKey) -> {
            String correlationId = message.getMessageProperties().getCorrelationId();
            log.debug ( "message: {} transmission fails, the response code: Reason {}: {} switch: routing keys {}: {}" , correlationId, replyCode, replyText, Exchange, routingKey);
        });
        // message acknowledgment, yml-Confirms configure Publisher: to true 
        rabbitTemplate.setConfirmCallback ((correlationData, ACK, the cause is) -> {
             IF (ACK) {
                log.debug ( "success message to the exchange, ID: {}" , correlationData.getId ());
            } else {
                log.debug ( "message sent to the exchange failed, because: {}" , the cause is);
            }
        });
        return rabbitTemplate;
    }

    /* ----------------------------------------------------------------------------Direct exchange test--------------------------------------------------------------------------- */

    /**
     * Declare Direct switch supports persistence.
     *
     * @return the exchange
     */
    @Bean("directExchange")
    public Exchange directExchange() {
        return ExchangeBuilder.directExchange("DIRECT_EXCHANGE").durable(true).build();
    }

    /**
     * Declare a persistent queue support.
     *
     * @return the queue
     */
    @Bean("directQueue")
    public Queue directQueue() {
        return QueueBuilder.durable("DIRECT_QUEUE").build();
    }

    /**
     * Key specified by binding to bind to a specific queue switch.
     *
     * @param queue    the queue
     * @param exchange the exchange
     * @return the binding
     */
    @Bean
    public Binding directBinding(@Qualifier("directQueue") Queue queue,
                                 @Qualifier("directExchange") Exchange exchange) {
        return BindingBuilder.bind(queue).to(exchange).with("DIRECT_ROUTING_KEY").noargs();
    }

    /* ----------------------------------------------------------------------------Fanout exchange test--------------------------------------------------------------------------- */

    /**
     * Statement fanout switch.
     *
     * @return the exchange
     */
    @Bean("fanoutExchange")
    public FanoutExchange fanoutExchange() {
        return (FanoutExchange) ExchangeBuilder.fanoutExchange("FANOUT_EXCHANGE").durable(true).build();
    }

    /**
     * Fanout queue A.
     *
     * @return the queue
     */
    @Bean("fanoutQueueA")
    public Queue fanoutQueueA() {
        return QueueBuilder.durable("FANOUT_QUEUE_A").build();
    }

    /**
     * Fanout queue B .
     *
     * @return the queue
     */
    @Bean("fanoutQueueB")
    public Queue fanoutQueueB() {
        return QueueBuilder.durable("FANOUT_QUEUE_B").build();
    }

    /**
     Fanout bind to A * queue switch.
     *
     * @param queue          the queue
     * @param fanoutExchange the fanout exchange
     * @return the binding
     */
    @Bean
    public Binding bindingA(@Qualifier("fanoutQueueA") Queue queue,
                            @Qualifier("fanoutExchange") FanoutExchange fanoutExchange) {
        return BindingBuilder.bind(queue).to(fanoutExchange);
    }

    /**
     * Binding to queue B Fanout switch.
     *
     * @param queue          the queue
     * @param fanoutExchange the fanout exchange
     * @return the binding
     */
    @Bean
    public Binding bindingB(@Qualifier("fanoutQueueB") Queue queue,
                            @Qualifier("fanoutExchange") FanoutExchange fanoutExchange) {
        return BindingBuilder.bind(queue).to(fanoutExchange);
    }
}

Monitor

  Next, write a message queue listener class, and the message queue listener corresponding processing, and the processing is completed confirmed by Ack mechanisms:

/**
 * Message Listener
 *
 * @Author XiongNeng
 * @version 1.0
 */
@Component
@ slf4j
public class Receiver {

    /**
     * FANOUT broadcast monitor a queue.
     *
     * @param message the message
     * @param channel the channel
     * @throws IOException the io exception  这里异常需要处理
     */
    @RabbitListener(queues = {"FANOUT_QUEUE_A"})
    public void on(Message message, Channel channel) throws IOException {
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), true);
        log.debug("FANOUT_QUEUE_A " + new String(message.getBody()));
    }

    /**
     * FANOUT broadcast queue monitor II.
     *
     * @param message the message
     * @param channel the channel
     * @Throws IOException The IO Exception handling exceptions requires here
      * / 
    @RabbitListener (Queues = { "FANOUT_QUEUE_B" })
     public  void T (the Message Message, Channel Channel) throws IOException {
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), true);
        log.debug("FANOUT_QUEUE_B " + new String(message.getBody()));
    }

    /**
     * DIRECT mode.
     *
     * @param message the message
     * @param channel the channel
     * @throws IOException the io exception  这里异常需要处理
     */
    @RabbitListener(queues = {"DIRECT_QUEUE"})
    public void message(Message message, Channel channel) throws IOException {
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), true);
        log.debug("DIRECT " + new String(message.getBody()));
    }
}

The message sender

  Write a messaging service to send messages to the switch:

/**
 * Messaging Service
 */
@Service
@ slf4j
public class SenderService {
    @Resource
    private RabbitTemplate rabbitTemplate;

    /**
     * Test broadcast mode.
     *
     * @param p the p
     * @return the response entity
     */
    public void broadcast(String p) {
        CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
        rabbitTemplate.convertAndSend("FANOUT_EXCHANGE", "", p, correlationData);
    }

    /**
     * Direct test mode.
     *
     * @param p the p
     * @return the response entity
     */
    public void direct(String p) {
        CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
        rabbitTemplate.convertAndSend("DIRECT_EXCHANGE", "DIRECT_ROUTING_KEY", p, correlationData);
    }

}

Run the test

[cTaskExecutor-1] com.xncoding.pos.mq.Receiver: FANOUT_QUEUE_A " a collection of students it!" 
[cTaskExecutor -1] com.xncoding.pos.mq.Receiver: DIRECT "fixed message" 
[cTaskExecutor -1] COM. xncoding.pos.mq.Receiver: FANOUT_QUEUE_B "a collection of students it!"

 

Guess you like

Origin www.cnblogs.com/jxd283465/p/11975136.html