This is the 14th day of my participation in the August More Text Challenge

Double 11 snatching orders is the most common scenario, snatching orders without payment will occupy a large amount of resources, such as commodity inventory. How to cancel overdue orders is a problem the architect must face. There are mainly the following schemes:

Sweep the table implementation

The principle of

  • Scan the order table by polling scheduled tasks to modify the batch status of timeout

advantages

  • The implementation is very simple

disadvantages

  • A large number of data sets, large server memory consumption.
  • In the case of frequent database queries and large order volumes, I/O is the bottleneck.
  • There is delay, short interval consumes resources, long interval is poor timeliness, the two are a pair of contradictions.
  • It is difficult to control. With the increase and refinement of scheduled services, each service needs to scan orders repeatedly, resulting in query waste

Java delay queue implementation

The principle of

  • With DelayQueue, place an order element in each order and implement the getDelay() method, which returns the distance from the element’s expiration

The remaining time, when <=0 the element is invalidated, can be retrieved from the queue. Enable the thread pool to listen to the data, once the invalid order is captured, after it is taken out, the cancellation logic is called for processing

advantages

  • Based on JVM memory, high efficiency and low task trigger time delay

disadvantages

  • After the server restarts, all data is lost
  • Rely on code hard coding, cluster expansion trouble
  • Depending on THE JVM memory, if the order volume is too large, unbounded queue content expansion, OOM is easy to appear
  • Need code implementation, multi-thread processing business, high complexity
  • In multithreaded processing, data frequently triggers wait and wake up, creating unnecessary competition

Message queue implementation

The principle of

  • Set two queues, each order to put one into the delay queue, set the expiration time. Once the message expires, it is retrieved and put into the work queue by

Consumer gets, evoking the timeout processing logic

  • RabbitMQ, which does not support delay queuing, can be set to x-message-TTL for queues and messages, using the lifetime of messages, and dead letter queues

    • A: Through the queue attribute setting, all messages in the queue have the same expiration time, coarse granularity, and simple encoding
    • B: Set the message individually. The TTL of each message can be different, fine grained, but the encoding is slightly more complex
  • RocketMQ does not support custom delay Settings

        DefaultMQProducer producer = new DefaultMQProducer("please_rename_unique_group_name");
        producer.setNamesrvAddr("192.168.10.11:9876");

        producer.start();
        Message msg1 = new Message(
                JmsConfig.TOPIC,
                "Order 001".getBytes());

        msg1.setDelayTimeLevel(2);// Delay 5 seconds
        Message msg2 = new Message(
                JmsConfig.TOPIC,
                "Order 001".getBytes());

        msg2.setDelayTimeLevel(4);// Delay 30 seconds

        SendResult sendResult1 = producer.send(msg1);
        SendResult sendResult2 = producer.send(msg2
        producer.shutdown();
Copy the code

RocketMQ does not support custom delay messages at any time, only for 18 latency intervals with built-in default values

private String messageDelayLevel = "1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h";
Copy the code

advantages

  • The queue can be removed at any time to realize real-time order cancellation and timely restore the resources (such as goods) occupied by the order.
  • Messages are stored in MQ and do not occupy application server resources
  • Asynchronous processing, once the processing capacity is insufficient, the consumer cluster can be easily expanded

disadvantages

  • This can result in a massive accumulation of messages
  • If the MQ server fails and restarts, the persistent queue expiration time is recalculated, resulting in inaccuracy
  • Dead letter messages may cause the monitoring system to anticipate them frequently

Redis implementation

Principle:

  • Use notify-keyspace-events of Redis. This option is left blank by default. Use Ex to enable expiration events and configure message listening. Place a key (such as order ID) in Redis for each order and set expiration time

advantages

  • Messages are stored in Redis and do not occupy application memory
  • External REDis storage, the application down machine will not lose data
  • Doing cluster scaling is quite convenient
  • Depending on Redis timeout, time accuracy is high

disadvantages

  • When a large number of orders are placed, each order needs to store REDis memory, which requires a large amount of Redis server resources

implementation

1. Redis enables key expiration notification

notify-keyspace-events Ex
Copy the code

2. The key has expired

@Component
public class RedisKeyExpiredListener extends JedisPubSub {

    private final static Logger logger = LoggerFactory.getLogger(RedisKeyExpiredListener.class);

    @Autowired
    private PushMsgService pushMsgService;

    private static final String PREFIX_EVENT_KEY = "xxx:event:sms:";

    @Override
    public void onMessage(String channel, String message) {
        if (message.startsWith(PREFIX_EVENT_KEY)) {
            pushMsgService.pushSmsForSms(Long.parseLong(message.substring(message.lastIndexOf(":") + 1))); }}}Copy the code

Register the redis key expired Listener

@PostConstruct
private void init(a) {
	// This method blocks the thread
	new Thread(new Runnable() {
	    @Override
	    public void run(a) {
		// To subscribe to the redis key expiration time, you need to configure notify-keyspace-events Ex on the REIDS server
		jedisPool.getResource().subscribe(redisKeyExpiredListener, "__keyevent@2__:expired");
	    }
	}).start();
}


Copy the code

keyevent@2:expired

  • __keyevent must start with this;
  • @2 means to listen on the second database;
  • :expired Indicates an expiration event

4. Set the expiration time

Jedis jedis = jedisPool.getResource();
jedis.select(2);
jedis.set(PREFIX_EVENT_KEY + teaching.getId(), String.valueOf(teaching.getId()));
jedis.expire(PREFIX_EVENT_KEY + teaching.getId(), expireTime.intValue());
Copy the code

Passive cancellation

The principle of

  • Each time the user queries an order, the time of the order is determined. If the time exceeds, the order cancellation service will be completed at the same time

advantages

  • The implementation is extremely simple
  • There is no additional performance penalty
  • Does not rely on any external middleware, just the processing of application logic

disadvantages

  • The latency is out of control, and if the user never triggers a query, the order hangs, neither paid nor canceled, and inventory is tied up