繁体   English   中英

http请求的线程池

[英]thread pooling for http requests

关于并发的体系结构和性能,我有几个问题。

建立:

有一个JavaFX GUI,用户可以在其中启动各种任务,这些任务本身就是线程化任务( new Thread(new CustomTask<?>).start(); )。 如果数据库的准备插入语句中有大约10k项,则这些任务为~700k HTTP请求执行循环并插入已处理的返回值。 它们的进度显示在GUI( ObservableList项目)中。

可视化

问题:

这些任务需要很长时间,瓶颈似乎是等待HTTP响应时的延迟。 (数据库插入是在10k准备好的插入语句的批量中关闭自动提交完成的)

目标:

通过将请求放在单独的任务/线程中来提高整体性能。


Q1:

在这里使用线程是否合理? 如何以其他方式改善性能?

Q2:

如果线程合理,我该如何实现? 我正在考虑使用全局线程池或ExecutorService ,其中请求任务排队。 当响应可用时,它将被写入同步列表。 如果列表中有10k +对象,则执行批量插入。

Q3:

如何确定良好的线程池大小? 如何区分线程?

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM