Apache HttpClient 4.x-多线程执行和资源泄漏

时间:2018-09-06 11:48:07

标签: java multithreading apache http apache-commons-httpclient

Apache HttpClient 4.2.1

我有一个Java进程,该进程从一个CouchDB A下载多个文件并将其上传到另一个CouchDBB。使用ExecutorService使用40个工作线程完成下载和上传。

当文件计数很高(〜25k)时,由于Linux打开文件描述符的限制,该进程将引发SocketException“打开的文件过多”。我认为这可能是由于资源泄漏引起的,然后继续分析以下代码:

启动工作线程

 private boolean copyDocumentsFromAtoB(Array ids) {
      ExecutorService downloader = Executors.newFixedThreadPool(50);
      ExecutorService uploader = Executors.newFixedThreadPool(50);

      for (String id: ids) {
       downloader.execute(new DownloaderThread(id, downloadedDocs));
      }

      for (JsonElement doc: downloadedDocs) {
       uploader.execute(new UploaderThread(doc));
      }
    }

可运行的下载程序类(UploaderThread类具有类似的实现)

private static final class DocumentDownloader implements Runnable {

        private final String documentId;
        private final JsonArray downloadedDocs;

        DocumentDownloader(String documentId, JsonArray downloadedDocs) {
            this.documentId = documentId;
            this.downloadedDocs = downloadedDocs;
        }

        @Override
        public void run() {
            InputStream docStream = null;
            String url = buildUrl(documentId);
            HttpGet doc = new HttpGet(url);
            try {
                //doc.setHeaders()

                HttpClient httpClient = new DefaultHttpClient();
                HttpResponse docResponse = httpClient.execute(doc);
                docStream = docResponse.getEntity().getContent();

                //document parsing and adding to downloadedDocs. Not important
            } catch (Exception e) {
                //handle exceptions
            } finally {
                if (docStream != null) {
                    try {
                        docStream.close();
                    } catch (IOException e) {
                        LOGGER.debug("Cannot close input stream", e);
                    }
                }
            }
        }
    }

发现:

  1. HttpClient对于每个线程都是本地的。根据我在网上找到的许多资源和帖子,这不是最佳选择。
  2. 如果我这样做了,httpClient.getConnectionManager().shutdown();在Runnable类的末尾,则打开的文件数减少了。
  3. 通过PoolingClientConnectionManager实例和本地上下文对象使用全局HttpClient(在copyDocumentsFromAtoB方法中仅实例化一次,并通过构造函数传递给Runnable类),进一步减少了打开文件的数量。

问题:

  1. 为什么原始实现在复制过程中打开的文件描述符的数量增加了?多个HttpClient实例对此有何贡献?

  2. docStream.close();块中的finally是否关闭在进程和数据库之间创建的Http连接?

  3. 当它所属的线程终止时,HttpClient实例是否被破坏(释放进程中的所有开放资源)? (这可以解释为什么复制过程过早终止后打开的文件数会减少)

  4. 除了使用单个全局HttpClient之外,我是否还可以进行其他优化(就资源泄漏而言)?

  5. 是否有我可以用来获取该方案针对不同实现的量化统计信息的工具?

  6. 我可以按照哪些步骤找到最佳工人人数 setMaxTotalsetDefaultMaxPerRoute线程(对于 PoolingClientConnectionManager)?

0 个答案:

没有答案