我刚刚阅读了jersey文档并试图通过使用异步服务器端api来比较我可以节省多少时间,但我得到的结果令人困惑,请帮助发现我的代码中是否有错误。
这是资源类:
@Path("/async-sync")
public class CompareAsyncAndSyncResource {
@GET
@Path("sync-call")
public String syncCall() throws InterruptedException {
expensiveComputation();
return "sync call finished";
}
@GET
@Path("async-call")
public void asyncCall(@Suspended final AsyncResponse asyncResponse) throws InterruptedException {
new Thread(new Runnable() {
@Override
public void run() {
expensiveComputation();
asyncResponse.resume("async call finished");
}
}).start();
}
private void expensiveComputation() {
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
一次同步来电和一次异步来电。
然后我做了这些测试:
public class CompareAsyncAndSyncResourceTest extends JerseyTest {
@Override
protected Application configure() {
return new ResourceConfig().register(CompareAsyncAndSyncResource.class);
}
@Test
public void sync_call_should_take_longer() throws Exception {
final Stopwatch stopwatch = new Stopwatch();
stopwatch.start();
callFiveTimes(new Runnable() {
@Override
public void run() {
String resp = target("async-sync/sync-call").request().get().readEntity(String.class);
System.out.println("sync returned " + stopwatch.elapsed(TimeUnit.MILLISECONDS));
assertThat(resp, is("sync call finished"));
}
});
stopwatch.stop();
System.out.println("five clients calling sync get at same time " + stopwatch.elapsed(TimeUnit.MILLISECONDS));
}
@Test
public void async_call_should_take_shorter() throws Exception {
final Stopwatch stopwatch = new Stopwatch();
stopwatch.start();
callFiveTimes(new Runnable() {
@Override
public void run() {
String resp = target("async-sync/async-call").request().get().readEntity(String.class);
System.out.println("async returned " + stopwatch.elapsed(TimeUnit.MILLISECONDS));
assertThat(resp, is("async call finished"));
}
});
stopwatch.stop();
System.out.println("five clients calling async get at same time " + stopwatch.elapsed(TimeUnit.MILLISECONDS));
}
private void callFiveTimes(final Runnable runnable) throws InterruptedException {
Iterable<Integer> rangeOfFive = newArrayList(1, 2, 3, 4, 5);
FluentIterable<Thread> threads = from(rangeOfFive).transform(new Function<Integer, Thread>() {
@Override
public Thread apply(Integer number) {
Thread thread = new Thread(runnable);
thread.start();
return thread;
}
});
for (Thread thread : threads) {
thread.join();
}
}
}
在测试中,我模拟了五个同时调用两个get调用的客户端,我想到完成五个异步调用会更快,但这是我得到的结果:
sync returned 1052
sync returned 2063
sync returned 3073
sync returned 4082
sync returned 5095
five clients calling sync get at same time 5096
同步部分是预期的,需要5秒才能完成,包括5个电话和一点开销。
但异步结果让我失望:
async returned 1847
async returned 2893
async returned 3904
async returned 4913
async returned 5923
five clients calling async get at same time 5923
看起来这五个异步调用并不是真正异步处理的。
我做错了什么?
编辑:我知道我现在做错了什么,它的番石榴,我应该调用toList。懒惰的评价再次让我感到高兴。但是看起来奇怪的是同步调用也变得更快,为什么呢?根据泽西的文档,同步获取调用应该阻止IO线程并减慢所有减少,但似乎没有发生。
答案 0 :(得分:5)
这根本不是什么异步。
从根本上说,Java Servlet规范中的所有异步工具(Jersey利用)都允许您将传入请求与初始线程分开。
通常请求和处理线程绑定在一起。
促进这种分离本身并不会使任何事情变得更快。你用它做什么可以使它更快。
您的迷你基准测试连续5次调用服务器。同步版本放置原始请求线程上的处理,阻塞一秒钟,然后恢复。但是如果你考虑到这一点,你的服务器在任何时候都不会消耗多个活动的处理线程,因为你的所有调用都被序列化到服务器(即1,然后是2,然后是3 ......)。
您的异步调用接受请求,然后,它立即创建一个新线程,然后该线程完成处理,而不是直接服务它。在这种情况下,每个请求都会占用初始线程的一部分,然后继续使用第二个线程。但是,它必须分配和分配给新线程,而原始同步代码则不会。该调度需要时间,您的同步线程没有采取的时间,因此更慢&#34;。它变慢了,因为它只是在做更多的工作。 (服务器还在异步上下文中包含您的请求以及需要的内部簿记)。
async为您提供的值不在卸载的服务器上。它在加载的服务器上。
线程处理过程本身需要花费一些时间在服务器上,特别是当您有100或1000个请求时。在该级别,线程开销本身是有影响的,尤其是在加载时。 Async可以让你更好地将请求的工作分配给其他设施,以解决繁重的问题。请求线程然后简单地成为接待台,&#34;我如何指导您的呼叫&#34;运营商。一旦他们完成了工作路线,他们就可以继续接受其他请求。
理想情况下,您可能会注意到很多公司没有很多公司,我可以如何指导您的电话&#34;操作员,因为工作不是特别耗时。与实际执行请求的人相比,单个运营商可以指导大量呼叫。因此,与进行实际处理相比,仅需要更少的线程来接受请求和路由它们。
通过异步,您可以将应用程序内部转移到其中一个客户支持位置,这些位置可以让您的人与人之间保持一致。这实际上可以让你的系统在负载下表现得更有效率,因为人们并没有全部卡在走廊里,而是安静地坐在个人等候室(队列)等待轮到他们真正的工作。
这是Java Servlets中Async工具的价值。通过一个简单的微基准测试来做你正在做的事情,他们无缘无故地做更多的工作。