mall Integrates RabbitMQ to Implement Delay Messages

Keywords: Java RabbitMQ Lombok SpringBoot github

SpringBoot e-commerce project mall (20k+star) address: https://github.com/macrozheng/mall

abstract

This article mainly explains the process of mall integrating RabbitMQ to implement delay message, taking sending delay message to cancel timeout order as an example.

Project Usage Framework Introduction

RabbitMQ

RabbitMQ is a widely used open source message queue.It is lightweight and easy to deploy, and can support a variety of message protocols.RabbitMQ can be deployed in distributed and federated configurations to meet high-scale, high-availability needs.

Installation and use of RabbitMQ

  1. Install Erlang, download address: http://erlang.org/download/otp_win64_21.3.exe

  1. Install RabbitMQ, download address: https://dl.bintray.com/rabbitmq/all/rabbitmq-server/3.7.14/rabbitmq-server-3.7.14.exe

  1. After installation, enter the sbin directory under the RabbitMQ installation directory

  1. Enter cmd in the address bar and return to start the command line, then enter the following command to start the administration function:
rabbitmq-plugins enable rabbitmq_management

  1. Access the address to see if the installation was successful: http://localhost:15672/

  1. Enter your account password and log in: guest guest
  2. Create an account number and set its role as administrator: mall mall

  1. Create a new virtual host as: /mall

  1. Click on the mall user to enter the user profile page

  1. Permissions to configure the virtual host for mall users

  1. The installation and configuration of RabbitMQ is now complete.

Message Model for RabbitMQ

sign Chinese name English Name describe
P Producer Producer Sender of the message, which can be sent to the switch
C Consumer Consumer Receiver of a message, consuming it from a queue
X Switch Exchange Receives messages from producers and sends them to specified queues based on routing keys
Q queue Queue Store messages from switches
type Switch Type type direct means to send messages directly from the routing key (orange/black)

Lombok

Lombok adds an interesting add-on to the Java language that you can have without handwriting getters, setters, and other methods for entity classes through a single comment.

Note: You need to install the Lookplug-in for idea and add dependencies to the pom file in your project.

Business Scenario Description

Used to solve the problem of how to cancel an order when the order timed out after the user placed it.

  • Users place orders (there will be a series of operations such as locking stock of goods, using coupons, and scoring).
  • Generate an order to get the id of the order;
  • Get the set order timeout (assuming 60 minutes without payment for canceling the order);
  • Send a delay message to RabbitMQ according to the order timeout so that it triggers the cancel action after the order timeout.
  • If the user does not pay, cancel the order (release the locked stock, return the coupon, return the credit series of operations).

Integrating RabbitMQ to implement delay messages

Adding dependencies to pom.xml

<!--Message Queuing Related Dependencies-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<!--lombok rely on-->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

Modify SpringBoot configuration file

Modify the application.yml file to add the Mongodb-related configuration under the spring node.

  rabbitmq:
    host: localhost # Connected Address of rabbitmq
    port: 5672 # Connection port number of rabbitmq
    virtual-host: /mall # Virtual host for rabbitmq
    username: mall # User name of rabbitmq
    password: mall # Password for rabbitmq
    publisher-confirms: true #If callback is required for asynchronous messages, it must be set to true

Adds the message queue's enumeration configuration class QueueEnum

Constant definition used to delay message queues and process cancel order message queues, including switch name, queue name, routing key name.

package com.macro.mall.tiny.dto;

import lombok.Getter;

/**
 * Message Queue Enumeration Configuration
 * Created by macro on 2018/9/14.
 */
@Getter
public enum QueueEnum {
    /**
     * Message Notification Queue
     */
    QUEUE_ORDER_CANCEL("mall.order.direct", "mall.order.cancel", "mall.order.cancel"),
    /**
     * Message Notification ttl Queue
     */
    QUEUE_TTL_ORDER_CANCEL("mall.order.direct.ttl", "mall.order.cancel.ttl", "mall.order.cancel.ttl");

    /**
     * Exchange Name
     */
    private String exchange;
    /**
     * Queue name
     */
    private String name;
    /**
     * Routing Key
     */
    private String routeKey;

    QueueEnum(String exchange, String name, String routeKey) {
        this.exchange = exchange;
        this.name = name;
        this.routeKey = routeKey;
    }
}

Add RabbitMQ Configuration

Used to configure the binding relationships of switches, queues, and queues to switches.

package com.macro.mall.tiny.config;

import com.macro.mall.tiny.dto.QueueEnum;
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Message Queue Configuration
 * Created by macro on 2018/9/14.
 */
@Configuration
public class RabbitMqConfig {

    /**
     * Switches bound to the order message actual consumption queue
     */
    @Bean
    DirectExchange orderDirect() {
        return (DirectExchange) ExchangeBuilder
                .directExchange(QueueEnum.QUEUE_ORDER_CANCEL.getExchange())
                .durable(true)
                .build();
    }

    /**
     * Switches bound by order delay queue
     */
    @Bean
    DirectExchange orderTtlDirect() {
        return (DirectExchange) ExchangeBuilder
                .directExchange(QueueEnum.QUEUE_TTL_ORDER_CANCEL.getExchange())
                .durable(true)
                .build();
    }

    /**
     * Order Actual Consumption Queue
     */
    @Bean
    public Queue orderQueue() {
        return new Queue(QueueEnum.QUEUE_ORDER_CANCEL.getName());
    }

    /**
     * Order Delay Queue (Dead Letter Queue)
     */
    @Bean
    public Queue orderTtlQueue() {
        return QueueBuilder
                .durable(QueueEnum.QUEUE_TTL_ORDER_CANCEL.getName())
                .withArgument("x-dead-letter-exchange", QueueEnum.QUEUE_ORDER_CANCEL.getExchange())//Switches forwarded after expiration
                .withArgument("x-dead-letter-routing-key", QueueEnum.QUEUE_ORDER_CANCEL.getRouteKey())//Routing keys forwarded after expiration
                .build();
    }

    /**
     * Bind order queue to switch
     */
    @Bean
    Binding orderBinding(DirectExchange orderDirect,Queue orderQueue){
        return BindingBuilder
                .bind(orderQueue)
                .to(orderDirect)
                .with(QueueEnum.QUEUE_ORDER_CANCEL.getRouteKey());
    }

    /**
     * Bind order delay queue to switch
     */
    @Bean
    Binding orderTtlBinding(DirectExchange orderTtlDirect,Queue orderTtlQueue){
        return BindingBuilder
                .bind(orderTtlQueue)
                .to(orderTtlDirect)
                .with(QueueEnum.QUEUE_TTL_ORDER_CANCEL.getRouteKey());
    }

}

The following switches and queues can be seen on the RabbitMQ administration page



Switch and Queue Description

  • mall.order.direct (switch bound to cancel order message queue): The bound queue is mall.order.cancel, which is sent whenever a message is sent with mall.order.cancel as the routing key.
  • mall.order.direct.ttl (switch bound by order delay message queue): The bound queue is mall.order.cancel.ttl, and once a message is sent with mall.order.cancel.ttl as the routing key, it is forwarded to this queue, which is saved for a certain time, and automatically sent to mall.order.cance after the time-out.L (Cancel Order Message Consumer Queue).

Add Sender of Delayed Message CancelOrderSender

Used to send messages to the order delay message queue (mall.order.cancel.ttl).

package com.macro.mall.tiny.component;

import com.macro.mall.tiny.dto.QueueEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * Sender of cancel order message
 * Created by macro on 2018/9/14.
 */
@Component
public class CancelOrderSender {
    private static Logger LOGGER =LoggerFactory.getLogger(CancelOrderSender.class);
    @Autowired
    private AmqpTemplate amqpTemplate;

    public void sendMessage(Long orderId,final long delayTimes){
        //Send a message to a delayed queue
        amqpTemplate.convertAndSend(QueueEnum.QUEUE_TTL_ORDER_CANCEL.getExchange(), QueueEnum.QUEUE_TTL_ORDER_CANCEL.getRouteKey(), orderId, new MessagePostProcessor() {
            @Override
            public Message postProcessMessage(Message message) throws AmqpException {
                //Set the delay milliseconds value for the message
                message.getMessageProperties().setExpiration(String.valueOf(delayTimes));
                return message;
            }
        });
        LOGGER.info("send delay message orderId:{}",orderId);
    }
}

Add Recipient CancelOrderReceiver for Cancel Order Message

Used to receive messages from the message queue for canceling an order (mall.order.cancel).

package com.macro.mall.tiny.component;

import com.macro.mall.tiny.service.OmsPortalOrderService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * Handler of cancel order message
 * Created by macro on 2018/9/14.
 */
@Component
@RabbitListener(queues = "mall.order.cancel")
public class CancelOrderReceiver {
    private static Logger LOGGER =LoggerFactory.getLogger(CancelOrderReceiver.class);
    @Autowired
    private OmsPortalOrderService portalOrderService;
    @RabbitHandler
    public void handle(Long orderId){
        LOGGER.info("receive delay message orderId:{}",orderId);
        portalOrderService.cancelOrder(orderId);
    }
}

Add OmsPortalOrderService interface

package com.macro.mall.tiny.service;

import com.macro.mall.tiny.common.api.CommonResult;
import com.macro.mall.tiny.dto.OrderParam;
import org.springframework.transaction.annotation.Transactional;

/**
 * Foreground Order Management Service
 * Created by macro on 2018/8/30.
 */
public interface OmsPortalOrderService {

    /**
     * Generate order based on submission information
     */
    @Transactional
    CommonResult generateOrder(OrderParam orderParam);

    /**
     * Cancel a single timeout order
     */
    @Transactional
    void cancelOrder(Long orderId);
}

Add implementation class OmsPortalOrderServiceImpl for OmsPortalOrderService

package com.macro.mall.tiny.service.impl;

import com.macro.mall.tiny.common.api.CommonResult;
import com.macro.mall.tiny.component.CancelOrderSender;
import com.macro.mall.tiny.dto.OrderParam;
import com.macro.mall.tiny.service.OmsPortalOrderService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * Foreground Order Management Service
 * Created by macro on 2018/8/30.
 */
@Service
public class OmsPortalOrderServiceImpl implements OmsPortalOrderService {
    private static Logger LOGGER = LoggerFactory.getLogger(OmsPortalOrderServiceImpl.class);
    @Autowired
    private CancelOrderSender cancelOrderSender;

    @Override
    public CommonResult generateOrder(OrderParam orderParam) {
        //todo performs a single action under a series of classes, referring specifically to the mall project
        LOGGER.info("process generateOrder");
        //Opens a delay message when the order is completed to cancel the order when the user has not paid (orderId should be generated after the order is placed)
        sendDelayMessageCancelOrder(11L);
        return CommonResult.success(null, "checkout success");
    }

    @Override
    public void cancelOrder(Long orderId) {
        //todo performs a series of class cancellations, referring specifically to mall projects
        LOGGER.info("process cancelOrder orderId:{}",orderId);
    }

    private void sendDelayMessageCancelOrder(Long orderId) {
        //Get order timeout, assuming 60 minutes
        long delayTimes = 30 * 1000;
        //Send Delay Message
        cancelOrderSender.sendMessage(orderId, delayTimes);
    }

}

Add OmsPortalOrderController Definition Interface

package com.macro.mall.tiny.controller;

import com.macro.mall.tiny.dto.OrderParam;
import com.macro.mall.tiny.service.OmsPortalOrderService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * Order Management Controller
 * Created by macro on 2018/8/30.
 */
@Controller
@Api(tags = "OmsPortalOrderController", description = "Order management")
@RequestMapping("/order")
public class OmsPortalOrderController {
    @Autowired
    private OmsPortalOrderService portalOrderService;

    @ApiOperation("Generate orders based on shopping cart information")
    @RequestMapping(value = "/generateOrder", method = RequestMethod.POST)
    @ResponseBody
    public Object generateOrder(@RequestBody OrderParam orderParam) {
        return portalOrderService.generateOrder(orderParam);
    }
}

Perform interface testing

Call next single interface

Note: Delayed message time has been set to 30 seconds

Project Source Address

https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-08

Public Number

mall project In the full series of learning tutorials, focus on the first time public number is available.

Posted by v4g on Mon, 02 Sep 2019 09:17:33 -0700