【问题标题】:Why does apache http client about 2 time slower then URL.openConnection in java?为什么 apache http 客户端比 java 中的 URL.openConnection 慢 2 倍?
【发布时间】:2020-02-10 15:39:00
【问题描述】:

考虑这段代码:

package com.zip;

import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpHead;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.util.Date;

import static com.diffplug.common.base.Errors.rethrow;

/**
 * @author nsheremet
 */
public class ParallelDownload2 {
  public static int THREADCOUNT = 20;
  private static final String URL = "https://server.com/myfile.zip";
  public static String OUTPUT = "C:\\!deleteme\\myfile.zip";
  public static void main(String[] args) throws Exception {
    System.setProperty("https.protocols", "TLSv1,TLSv1.1,TLSv1.2");
    System.out.println(new Date());

    CloseableHttpClient httpClient = HttpClients.createDefault();

    HttpGet request = new HttpGet(URL);
    request.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36");
    CloseableHttpResponse response = rethrow().wrap(() -> httpClient.execute(request)).get();
    Long contentLength = Long.parseLong(response.getFirstHeader("Content-Length").getValue());
    long blocksize = contentLength / THREADCOUNT;

    RandomAccessFile randomAccessFile = new RandomAccessFile(new File(OUTPUT), "rwd");
    randomAccessFile.setLength(contentLength);
    randomAccessFile.close();
    response.close();

    for (long i = 0; i <THREADCOUNT; i++) {
      long startpos = i * blocksize;
      long endpos = (i + 1) * blocksize - 1;
      if (i == THREADCOUNT - 1) {
        endpos = contentLength;
      }
      new Thread(new DownloadTask(i, startpos, endpos)).start();
    }
    System.out.println(new Date());
  }

  public static class DownloadTask implements Runnable {

    public DownloadTask(
        long id,
        long startpos,
        long endpos
    ) {
      this.id = id;
      this.startpos = startpos;
      this.endpos = endpos;
    }

    long id;
    long startpos;
    long endpos;

    @Override
    public void run() {
      try {
        CloseableHttpClient httpClient = HttpClients.createDefault();

        HttpGet request = new HttpGet(URL);
        request.addHeader("Range", "bytes=" + startpos + "-" + endpos + "");
        request.addHeader("Connection", "keep-alive");
        request.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36");
        CloseableHttpResponse response = rethrow().wrap(() -> httpClient.execute(request)).get();

        if (response.getStatusLine().getStatusCode() == 206) {

          InputStream is = response.getEntity().getContent();
          RandomAccessFile randomAccessFile = new RandomAccessFile(new File(OUTPUT), "rwd");
          randomAccessFile.seek(startpos);
          int len = 0;
          byte[] buffer = new byte[1024*10];
          while ((len = is.read(buffer)) != -1) {
            randomAccessFile.write(buffer, 0, len);
          }
          is.close();
          randomAccessFile.close();
          System.out.println("Thread "+ Thread.currentThread().getId() +": Download");
        }
      } catch (IOException e) {
        e.printStackTrace();
      }
      System.out.println(new Date());
    }

  }

}

这是来自this one 的修改副本,它是通过简单的URL.openConnection 编写的。为什么URL.openConnection 使用多线程以 10 Mb/sec 的速度下载文件,而 apach http 客户端版本的速度大多在 1-5 Mb/sec 之间?我错过了 http apache 客户端设置中的某些内容吗?

更新

  1. 我使用多个 HttpClient,因为单个对象导致与通过 URL 进行 1 个连接的性能相同
  2. Http apache客户端用于mane高性能服务器,所以我相信肯定有配置问题。但究竟是什么?

关于代码

这当然不是生产就绪代码,应该被视为我想让多线程下载快速工作的原型。

关于多线程

我无法解释为什么,因为我不是下载资源的所有者,但多线程下载速度比单线程快得多(10 倍)。

【问题讨论】:

  • 我没有答案,但确实意识到 Apache HttpClient 对 http 客户端的作用就像 Oracle 对 DBMS 的作用一样。你选择它是因为它令人难以置信的可配置性,而不是它的易用性。只是坚持 HttpClient 的默认实现可能是您问题的根源,这将其设置为非常基本的。如果分块下载是内置的,并且您想让 HttpClient 为您完成工作而不是自己动手,我不会感到惊讶。您拥有的代码是针对 URLConnection 的,它对您没有任何作用。
  • 所有版本的 Apache HttpClient 在 JRE 1.8 及更早版本中始终比 HUC 快得多。前段时间我停止比较 HttpClient 与 JRE HUC 的性能,因为它变得毫无意义。
  • 你为什么使用多个 HttpClient 实例而不是一个?
  • 1) 欢迎任何用于 apache 客户端的 non default 配置
  • 2) 关于多个 HttpClient - 单个 http 客户端(具有多个请求)与单个连接具有相同的速度。

标签: java apache-httpclient-4.x java-io


【解决方案1】:

可能性很小。

(1) 区别可能不是传输速度,而是连接时的初始延迟。过去我也遇到过类似的问题,而罪魁祸首竟然是 IPv6。初始请求是在 IPv6 上完成的,它会静默地回退到 IPv4,但只是在超时之后。

尝试使用-Djava.net.preferIPv4Stack=true 运行,或者将主机指定为数字 IPv4 四元组,看看是否有区别。

(2) 差异可能是由于 https 实现可能会检查证书路径、在线撤销列表等。如果有差异,请检查 http URL。如果是这样,请查看 Apache 的文档如何根据自己的喜好配置 https 行为。

(3) 无论如何,运行 tcpdump 或 wireshark 可能会给你更多有用的信息。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-20
    • 1970-01-01
    • 1970-01-01
    • 2016-02-13
    • 2021-04-12
    相关资源
    最近更新 更多