我有一些关于架构和问题的问题。并发性能。
有一个JavaFX GUI,用户可以在其中启动各种任务,这些任务本身就是线程化任务(new Thread(new CustomTask<?>).start();
)。如果数据库的准备插入语句中有大约10k项,则这些任务为~700k HTTP请求执行循环并插入已处理的返回值。它们的进度显示在GUI(ObservableList
项目)中。
这些任务需要很长时间,瓶颈似乎是等待HTTP响应时的延迟。 (数据库插入是在10k准备好的插入语句的批量中关闭自动提交完成的)
通过将请求放在单独的任务/线程中来提高整体性能。
在这里使用线程是否合理?如何以其他方式改善性能?
如果线程合理,我该如何实现?
我正在考虑使用全局线程池或ExecutorService
请求任务排队。当响应可用时,它将被写入同步列表。如果列表中有10k +对象,则执行批量插入。
如何确定良好的线程池大小?如何区分线程?
Thread.activeCount()
返回7 (当前线程组)
ManagementFactory.getThreadMXBean().getThreadCount()
返回13 (线程整体?)
Runtime.getRuntime().availableProcessors()
返回8
我已经阅读了一些关于多线程的评论,他们都表示拥有比核心更多的线程并不一定能提高性能(没有“真正的”并发性,时间限制)。我不知道,但如果我不得不猜我会说数字13包括一些GUI线程。我似乎无法解决如何为ThreadPoolSize获取有用的数字。
我很欣赏有关如何改进我的申请的任何提示。
答案 0 :(得分:2)
当然,您可以使用ExecutorService
。
我已经阅读了一些关于多线程的评论,他们都说拥有比核心更多的线程并不一定能提高性能(没有“真正的”并发性,时间限制)
对于不休眠或等待/阻止的进程,例如计算素数或处理图像,都是如此。在您的情况下,HTTP客户端阻塞直到响应返回,并且直到它发生,线程保持空闲。对于大小为50-100-200的HTTP请求执行程序池是可以的。
模式可能如下:
ExecutorService es = Executors.newFixedThreadPool(50);
// ...
// creating request and response future associated with it
Future<Response> responseFuture = es.submit(new Callable<Response>() {
@Override
public Response call() throws Exception {
// request data by HTTP
return response;
}
});
customTask.push(responseFuture);
在customTask
对象中,让我们创建一个单一的线程服务执行器,它将在Response
的列表上运行:
// create single pool executor in order to accept responses
// one by one and at the order they're requested
ExecutorService customTaskService = Executors.newSingleThreadExecutor();
List<Response> results = new ArrayList<>();
// push() method
public void push(final Future<Response> responseFuture) {
customTaskService.execute(new Runnable() {
public void run() {
try {
// response.get() will block inside this service thread
// though not affecting GUI thread
results.add(response.get(TIMEOUT, TimeUnit.SECONDS));
} catch (RuntimeException e) {
// processing of a request failed
}
if (results.size() > MAX_SIZE) {
// make inserts to DB
results.clear();
}
}
});
}
答案 1 :(得分:2)
Q1
首先不清楚您需要回复客户端的内容。你是否必须与数据库交谈以发送回复?
如果你不是Pub / Sub模式(即它的火灾和遗忘),那么消息队列或任何发布/订阅系统都是理想的,并且比使用普通ExecutorService
更好。一些例子是AMQP,JMS,Redis Pub/Sub以及更多。
您可以使用回复客户端来执行pub / sub,但这通常需要非阻塞客户端连接,如WebSockets,Comet,并且设置相当复杂。
Q2和Q3
如果你的问题是你需要回复客户端,那么它遵循请求/回复模式,这是一个难以扩展的问题。
在JVM上执行此操作的一些库是Hystrix,它遵循命令和隔板模式,它提供可配置和容错的请求/回复以及request collapsing我相信解决了您:&# 34; 如果列表中有10k +对象,则执行批量插入。&#34;
确定正确的池大小对于阻塞操作实际上相当复杂。对于非阻塞(即cpu绑定或内存处理),它只是可用的处理器,但由于您连接到数据库并且可能使用阻塞IO servlet容器,因此不是这种情况。
要确定阻塞操作的正确池大小,您将使用指标并监控Hystrix提供的开箱即用。您还应该了解您的下游依赖项。例如,如果您的数据库只能处理200个并发连接,那么您不希望与数据库对话的线程池大于200.