You must have been hungry, or in meituan takeout order experience, after placing the order, over a certain period of time, the order was automatically canceled. This is the delayed task. The application scenario of delayed task is quite extensive, not only the above mentioned ele. me, Meituan takeout, but also 12306, or Taobao, Ctrip and so on have such scenes. How does this delay work? Follow me and read on.

1. The SQL query, Serive layer assembly time manipulation

Make some judgments in the splicing SQL or Serive layer, for example, the order status is “ordered, but not paid”, and the current time is more than 15 minutes after the order time, the order status displayed in the client or background will be changed to “cancelled”.

This is more convenient, and there is no delay, but the state in the database is not the real state. Don’t forget to do something special with the order state if you need to provide interfaces to other departments to call.

2.Job

This is one of the most common ways. Create a Job that repeats the order every once in a while and changes the order status when the condition is met.

This method is also more convenient, but there will be a certain delay. If the order data is relatively small, it is acceptable to scan once every minute, and the delay is only about one minute. But once the order data is so large that it can take more than an hour to scan it, the delay can be terrifying. And keep scanning the database, for the database is also a kind of pressure. There are also some improvements that can be made, such as adding a time range to the scan, so that orders that have been scanned before a certain time are not scanned because they have already been processed by the previous Job.

The first method can be used in combination with the second method.

The first two are more general practice, if the amount of data is not large, use, also good.

3.DelayQueue

DelayQueue is a Java native queue, as its name implies.As you can see from the figure above, DelayQueue is a generic queue that accepts the type inherited Delayed. So we need to write a class to inherit Delayed. To implement Delayed, two methods need to be overridden:

 public long getDelay(TimeUnit unit)
 public int compareTo(Delayed o)
Copy the code

The first method: the basis for determining whether a message is due (can be read out). When a negative number is returned, the message expires and can be read.

The second method: putting data into the DelayQueue executes this method, which determines where the data should be placed.

In this class, we need to define some properties, such as orderId,orderTime,expireTime.

Now let’s do a test to test the compareTo method:

public class OrderDelay implements Delayed { private int orderId; private Date orderTime; public Date getOrderTime() { return orderTime; } public void setOrderTime(Date orderTime) { this.orderTime = orderTime; } private static final int expireTime = 15000; public int getOrderId() { return orderId; } public void setOrderId(int orderId) { this.orderId = orderId; } @Override public long getDelay(TimeUnit unit) { return orderTime.getTime() + expireTime - new Date().getTime(); } @Override public int compareTo(Delayed o) { return this.orderTime.getTime() - ((OrderDelay) o).orderTime.getTime() > 0 ? 1:1; }}Copy the code

The getDelay method can be ignored for now because it is not needed for testing compareTo. Then we write some code in the main method:

DelayQueue<OrderDelay> queue = new DelayQueue<>(); Calendar c = Calendar.getInstance(); c.add(Calendar.DATE, 1); Date time1 = c.getTime(); OrderDelay orderDelay1=new OrderDelay(); orderDelay1.setOrderId(1); orderDelay1.setOrderTime(time1); queue.put(orderDelay1); Println ("1: "+ new SimpleDateFormat(" YYYY /MM/ DD HH: MM :ss").format(time1)); c.add(Calendar.DATE, -15); Date time2 = c.getTime(); OrderDelay orderDelay2=new OrderDelay(); orderDelay2.setOrderId(2); orderDelay2.setOrderTime(time2); queue.put(orderDelay2); Println ("2: "+ new SimpleDateFormat(" YYYY /MM/ DD HH: MM :ss").format(time2)); int a=0;Copy the code

Set the breakpoint on the last line, and then debug. You can see that although order1 is pushed to DelayQueue first, the first data in DelayQueue is order2’s. This is the use of the compareTo method: determine where the data should be ranked based on the return value of this methodGenerally speaking, the smaller orderTime is, the sooner it will expire and be consumed, so there is no problem with this method.

Now that the compareTo test is complete, let’s test the getDelay method again.

private static void produce(int orderId) { OrderDelay delay = new OrderDelay(); delay.setOrderId(orderId); Date currentTime = new Date(); SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String dateString = formatter.format(currentTime); delay.setOrderTime(currentTime); System.out.printf(" now time is %s; Order %d joins queue %n", dateString, orderId); queue.put(delay); }Copy the code

Define a consumer method again:

private static void consum() { while (true) { try { OrderDelay orderDelay = queue.take(); // Date currentTime = new Date(); SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String dateString = formatter.format(currentTime); System.out.printf(" now time is %s; Order %d expired %n", dateString, OrderDelay.getorderId ()); } catch (InterruptedException e) { e.printStackTrace(); }}}Copy the code

Run both methods inside the main method:

produce(1);
consum();
Copy the code

And set the breakpoint at

 OrderDelay orderDelay = queue.take();
Copy the code

Debug, run it here, F8, and you’ll see that the code is not going to execute, it’s blocked, and that actually tells you that DelayQueue is a blocking queue. After 15 seconds, I finally get to the next line of code and get the data, which is where the getDelay and take methods come in. GetDelay: Determines whether the data can be taken out based on the return value of the method. Take: Fetches data, but is subject to the getDelay method and blocks if the condition is not met.

All right. Both the getDelay method and compareTo have been tested. The next thing is simple. I’ll just release the code:

static DelayQueue<OrderDelay> queue = new DelayQueue<>(); public static void main(String[] args) throws InterruptedException { Thread productThread = new Thread(() -> { for (int i = 0; i < 20; i++) { try { Thread.sleep(1200); } catch (InterruptedException e) { e.printStackTrace(); } produce(i); }}); productThread.start(); Thread consumThread = new Thread(() -> { consum(); }); consumThread.start(); } private static void produce(int orderId) { OrderDelay delay = new OrderDelay(); delay.setOrderId(orderId); Date currentTime = new Date(); SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String dateString = formatter.format(currentTime); delay.setOrderTime(currentTime); System.out.printf(" now time is %s; Order %d joins queue %n", dateString, orderId); queue.put(delay); } private static void consum() { while (true) { try { OrderDelay orderDelay = queue.take(); // Date currentTime = new Date(); SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String dateString = formatter.format(currentTime); System.out.printf(" now time is %s; Order %d expired %n", dateString, OrderDelay.getorderId ()); } catch (InterruptedException e) { e.printStackTrace(); }}}Copy the code

Run:

Through the console output, you will see that the function implementation is OK.

It’s also more convenient, has almost no latency, and doesn’t require much memory, since you’re just storing an order number. The disadvantage is also obvious, because the order is stored in memory, once the server is down, it is in trouble. Consumers and producers can only be in the same set of code. In this era of microservices, consumers and producers are generally kept separate, even on different servers. Because of this, if the consumer pressure is too large, you can easily solve the problem by adding servers.

The first three can also be used in combination