http请求的线程池

时间:2015-04-16 15:52:55

标签: java multithreading performance http concurrency

我有一些关于架构和问题的问题。并发性能。

设定:

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

Visualisation

问题:

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

目标:

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


Q1:

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

Q2:

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

Q3:

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

Thread.activeCount() 返回7 (当前线程组) ManagementFactory.getThreadMXBean().getThreadCount() 返回13 (线程整体?) Runtime.getRuntime().availableProcessors() 返回8

我已经阅读了一些关于多线程的评论,他们都表示拥有比核心更多的线程并不一定能提高性能(没有“真正的”并发性,时间限制)。我不知道,但如果我不得不猜我会说数字13包括一些GUI线程。我似乎无法解决如何为ThreadPoolSize获取有用的数字。


我很欣赏有关如何改进我的申请的任何提示。

2 个答案:

答案 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.