我们有一个基于servlet的应用程序,它同步服务请求,每个请求将花费近4000ms
,因为它必须对远程数据库执行大量sql查询并进行大量计算工作机会。
我们使用ab
来测试应用程序,并发度和吞吐量都比较小。
在我看来,在传统的 servlet 模型中,请求是同步处理的:为每个请求创建一个线程,并且该线程将等待,直到该过程完成,这意味着在我的情况下,该 servlet 线程将等待4000ms
,在挂起期间,它不能做任何事情。一种资源浪费。
有时,我对vertx
感兴趣,所以我基于vertx
编写了应用程序。我知道vertx
中的事件循环
模型无法被阻止。因此, block 作业(需要4000ms
)在工作线程中执行,如下所示:
router.route().blockingHandler(context -> {
List result=new ArrayList();
String[] layers = getLayers(context);
final int[] len=new int[]{layers.length};
layers.forEach(l_>{
context.vertx().executeBlocking(f->{
List d = doDataseJob(l);
d = doCalculationJob(d)
f.complete(d);
},false,r->{
len[0]--;
result.addAll(r.result())
if(len[0]==0){
//all blocking jobs have done
//return
context.response().end(.......);
}
});
});
});
但是再次通过ab
测试并发性后,我们发现与基于servlet的应用程序相比,只有一点点的改进。
据我所知,异步模式和同步模式下单个请求的响应时间不会有太大变化,但是异步模式下的吞吐量
和并发
应该有所提高(基于 vertx 的应用程序),因为仅转发请求的事件循环线程可以比以前处理更多的请求。
我错过了什么吗?或者我使用 vertx
错误?
更新1:让繁重的工作回归 future
:
@Override
public Future doHeavyJob(String layer) {
Future future = Future.future();
new Thread(()->{
List d = doDataseJob(tile, layer, future);
d = doCalculationJob(d);
future.complete(d)
}).start();
return future;
}
最佳答案
正如我在另一个主题中已经提到的,您以错误的方式使用 Vert.x。
没有并发增益,因为您所做的正是 servlet 之前所做的事情:在线程池上放置很长的工作。
事实上,使用 EventLoop 来完成这个任务并不会改变任何事情。它甚至可能会变得更糟,因为 Vert.x 工作线程池默认非常小,只有 20 个线程。如果您的 servlet 容器配置了更多线程,实际上,它在该设置中的性能会优于 Vert.x。
你应该做什么:
- 如果存在独立查询,请并行执行它们,并使用 Future 组合它们的结果
- 将 DAO 封装在 verticle 中,并使用 EventBus 进行通信
请注意,如果您的查询长达 4 秒,一旦您解决这些问题,您的数据库将成为瓶颈。
关于java - 异步和同步模式的并发,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50195673/