[英]thread pooling for http requests
关于并发的体系结构和性能,我有几个问题。
有一个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获取有用的数字。
我很欣赏任何关于如何改进我的应用程序的提示。
当然,您可以使用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();
}
}
});
}
Q1
首先,不清楚您需要回复客户端。 您是否必须与数据库通话以发送回复?
如果不这样,它就是Pub / Sub模式(即它的火与忘记),那么消息队列或任何发布/订阅系统都是理想的,并且比使用普通的ExecutorService
更好。 一些例子是AMQP ,JMS, Redis Pub / Sub等等。
您可以通过回复客户端来执行pub / sub,但这通常需要非阻塞客户端连接,如WebSockets,Comet,并且设置起来相当复杂。
Q2和Q3
如果你的问题是你需要回复客户端,那么它遵循请求/回复模式,这是一个难以扩展的问题。
在JVM上执行此操作的一些库是Hystrix ,它遵循命令和隔板模式,它提供可配置和容错的请求/ 响应以及请求折叠 ,我相信解决了您:“ 如果列表中有10k +对象,请执行批量插入。 “
确定适当的池大小实际上对于阻塞操作来说相当复杂。 对于非阻塞(即cpu绑定或内存处理),它只是可用的处理器,但由于您连接到数据库并且可能使用阻塞IO servlet容器,因此不是这种情况。
要确定阻塞操作的正确池大小,您将使用指标并监控Hystrix提供的开箱即用的功能。 您还应该了解您的下游依赖项。 例如,如果您的数据库只能处理200个并发连接,则不希望与数据库对话的线程池大于200。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.