I once naively thought that I was invincible in the performance test scenario, but the reality patted me again.
The R & D put forward a pressure test requirement of canceling orders after a delay of 10s, which really confused me for a while. Finally, I referred to Java's delay queue java.util.concurrent.DelayQueue to realize this requirement.
demand
In one scenario, my previous design was to place an order and cancel the order if it was successful, because the test user's assets were not exhausted.
Recently, R & D has optimized the service function logic. In the regression test, it is found that the test results of this use case are quite different from those before. The R & D judgment may be caused by the short interval between placing and canceling orders. Therefore, it is necessary to conduct pressure test for delayed cancellation.
thinking
The thread safe queue class java.util.concurrent.DelayQueue is used here. You can understand its purpose by looking at its name. There is another scheme, java.util.Timer, but it will create additional threads and it is difficult to control the cancellation rate, so I gave it up this time.
First, when placing an order, if successful, the order number will be recorded in the java.util.concurrent.DelayQueue queue through a java.util.concurrent.DelayQueue implementation class, and then the expired order number will be obtained from the delay queue in the com.oktest.okex.algo.trade.OrderAndCancelGrid3.FunTester#doing method, and then the order will be cancelled.
The following is a demonstration video of the delayQueue function
realization
The order ID is of java.lang.String type, and the implementation class of java.util.concurrent.Delayed is as follows:
private static class FunDelay implements Delayed { int time String id FunDelay(String id) { this.time = getMark() + delay this.id = id } @Override public long getDelay(TimeUnit unit) { return time - getMark(); } @Override public int compareTo(Delayed o) { FunDelay item = (FunDelay) o; if (this.time < item.time) { return -1; } else { return 1; } } }
test case
Most of the content reuses the previous design, rewrites the after method, and delays the unprocessed order ID in the queue after the user ends the test.
private static class FunTester extends FixedThread<Trade> { DelayQueue<FunDelay> ids = new DelayQueue<>() FunTester(Trade o, int limit) { super(o, limit, true) } @Override protected void doing() throws Exception { def res = f.order(********) if (0 == res.getInteger("code")) { def array = res.getJSONArray("data").get(0) def id = array.getString("Id") ids.add(new FunDelay(id)) } else { output(res.toString()) } int i = 0 while (true) { if (i++ > 10) break def poll = ids.poll() if (poll == null) break f.stop("***", poll.id) } } @Override protected void after() { super.after() sleep(1.0) f.clearGrid() } @Override ThreadBase clone() { return new FunTester(f, limit) } }
implement
The execution method reuses the previous content and adds the fourth parameter to parameterize the delay time.
static int delay; public static void main(String[] args) { ClientManage.init(10, 5, 0, "", 0) def util = new ArgsUtil(args) def thread = util.getIntOrdefault(0, 1) def times = util.getIntOrdefault(1, 1000) def runuptime = util.getIntOrdefault(2, 0) delay = util.getIntOrdefault(3, 3) Constant.RUNUP_TIME = runuptime Common.PRINT_RESPONSE = false Common.VERIFY = false FunLibrary.LOG_KEY = false Common.MOCO = true Common.setHost() def line = DataUtils.getMocoTokens() def tasks = [] thread.times { def base = getBase(line.get(getRandomInt(500))) def drive = new Trade(base) tasks << new FunTester(drive, times) } new Concurrent(tasks, "Orders and delays 10 s cancel the order").start() over() }
test result
I used the default delay of 3s. The following is a screenshot of the log during execution:
Test results of order cancellation with 10s delay
As can be seen in the figure, orders are placed in 17s and cancelled in 20s. Perfect realization of demand!