【发布时间】: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 客户端设置中的某些内容吗?
更新
- 我使用多个 HttpClient,因为单个对象导致与通过 URL 进行 1 个连接的性能相同
- 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