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
- Install Erlang, download address: http://erlang.org/download/otp_win64_21.3.exe
- Install RabbitMQ, download address: https://dl.bintray.com/rabbitmq/all/rabbitmq-server/3.7.14/rabbitmq-server-3.7.14.exe
- After installation, enter the sbin directory under the RabbitMQ installation directory
- 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
- Access the address to see if the installation was successful: http://localhost:15672/
- Enter your account password and log in: guest guest
- Create an account number and set its role as administrator: mall mall
- Create a new virtual host as: /mall
- Click on the mall user to enter the user profile page
- Permissions to configure the virtual host for mall users
- 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.