AsyncHttpClient And Download Speed Limit
AsyncHttpClient
Official repository and docs: https://github.com/AsyncHttpClient/async-http-client
Maven Dependency
Check the latest version of async-http-client at https://mvnrepository.com/artifact/org.asynchttpclient/async-http-client
<dependency>
<groupId>org.asynchttpclient</groupId>
<artifactId>async-http-client</artifactId>
<version>2.12.3</version>
</dependency>
Usage
Basic Usage
AsyncHttpClient provides 2 APIs for defining requests: bound and unbound. AsyncHttpClient and Dsl` provide methods for standard HTTP methods (POST, PUT, etc)
// bound, define request inline with execute()
Future<Response> whenResponse=asyncHttpClient.prepareGet("http://www.example.com/").execute();
// unbound, define request and execute separately
Request request=get("http://www.example.com/").build();
Future<Response> whenResponse=asyncHttpClient.executeRequest(request);
Client Configuration
Instead of default configuration, create a customized one with Dsl.config()
AsyncHttpClientConfig config = Dsl.config()
.setConnectTimeout(CONN_TIMEOUT)
.setRequestTimeout(REQ_TIMEOUT)
.setMaxRequestRetry(100)
.build();
AsyncHttpClient client = Dsl.asyncHttpClient(config);
Download To File
The default implementation accumulates the HTTP chunks received into an ArrayList. This could lead to high memory consumption, or an OutOfMemory exception when trying to download a large file. Use a FileChannel to write the bytes to our local file directly. We'll use the getBodyByteBuffer() method to access the body part content through a ByteBuffer.
// Open the file, enable append(not necessary in this example)
FileOutputStream os = new FileOutputStream(LOCAL_FILE, true);
// Create a default client
AsyncHttpClient client = Dsl.asyncHttpClient();
// Use Future to block till download complete, just for demostration
ListenableFuture<Response> whenResponse = client.prepareGet(FILE_URL).execute(new AsyncCompletionHandler<Response>() {
@Override
public State onBodyPartReceived(HttpResponseBodyPart bodyPart) throws Exception {
os.getChannel().write(bodyPart.getBodyByteBuffer());
return State.CONTINUE;
}
@Override
public Response onCompleted(Response response) throws Exception {
return response;
}
});
Response response=whenResponse.get();
// Thread will never exit if client is not closed
client.close();
You can get the bodyPart length and calculate the totoally received length
private int totalLength;
@Override
public State onBodyPartReceived(HttpResponseBodyPart bodyPart) throws Exception {
os.getChannel().write(bodyPart.getBodyByteBuffer());
receivedLength += bodyPart.length();
System.out.println(receivedLength);
if (bodyPart.isLast())
{
System.out.println("last");
return State.ABORT;
}
return State.CONTINUE;
}
Range Requests, Partial Download
You can modify the request header to download part of the file, as long as the server supports range request.
Response DefaultHttpResponse(decodeResult: success, version: HTTP/1.1)
HTTP/1.1 200 OK
Server: openresty
Date: Sun, 04 Dec 2022 13:35:21 GMT
Content-Type: application/octet-stream
Last-Modified: Thu, 21 Apr 2022 17:16:39 GMT
Connection: keep-alive
ETag: "62619177-1b58d5"
Accept-Ranges: bytes <--- this means the server supports range requests
content-length: 1792213
In Java code, create the request like
FileOutputStream os = new FileOutputStream(localFilePath, true);
Request request = Dsl.get(remoteUrl).setHeader(HttpHeaderNames.RANGE, "bytes="+offset+"-"+(offset + length - 1)).build();
ListenableFuture<FragmentResponse> whenResponse = client.executeRequest(request, new AsyncCompletionHandler<>() {
//...
});
You will get the response header as
Response DefaultHttpResponse(decodeResult: success, version: HTTP/1.1)
HTTP/1.1 206 Partial Content
Server: openresty
Date: Sun, 04 Dec 2022 13:27:14 GMT
Content-Type: application/octet-stream
Last-Modified: Fri, 15 Oct 2021 12:25:23 GMT
Connection: keep-alive
ETag: "61697333-268f48e"
X-RateLimit-Byte-Rate: 67108864
Content-Range: bytes 38797312-39845887/40432782
content-length: 1048576
Range Boundary
According to Hypertext Transfer Protocol (HTTP/1.1): Range Requests, the range boundaries are inclusive - inclusive. Examples of byte-ranges-specifier values:
- The first 500 bytes (byte offsets 0-499, inclusive): bytes=0-499
- The second 500 bytes (byte offsets 500-999, inclusive): bytes=500-999
Example Code Of Download Speed Limit
This is an example of controlling download speed by splitting file into fragments -- and download them piece by piece.
public class FragmentableDownloader {
Logger log = LoggerFactory.getLogger(FragmentableDownloader.class);
public static final int CONN_TIMEOUT = 60000;
public static final int REQ_TIMEOUT = 60000;
private AsyncHttpClient client;
/**
* limit bytes per second
*/
private long rateLimit;
/**
* size of each fragment
*/
private long fragmentSize;
private String remoteUrl;
private String localFilePath;
private long fileLength;
private long timestamp;
private long interval;
public FragmentableDownloader(long rateLimit, long fragmentSize, String remoteUrl, String localFilePath) {
this.rateLimit = rateLimit;
this.fragmentSize = fragmentSize;
this.remoteUrl = remoteUrl;
this.localFilePath = localFilePath;
AsyncHttpClientConfig config = Dsl.config()
.setConnectTimeout(CONN_TIMEOUT)
.setRequestTimeout(REQ_TIMEOUT)
.setMaxRequestRetry(100)
.build();
this.client = Dsl.asyncHttpClient(config);
interval = fragmentSize / rateLimit;
}
public FragmentResponse downloadFragment(long offset, long length) throws Exception {
FileOutputStream os = new FileOutputStream(localFilePath, true);
Request request = Dsl.get(remoteUrl).setHeader(HttpHeaderNames.RANGE, "bytes="+offset+"-"+(offset + length - 1)).build();
ListenableFuture<FragmentResponse> whenResponse = client.executeRequest(request, new AsyncCompletionHandler<>() {
private long totalLength;
private int byteTransferred = 0;
@Override
public State onHeadersReceived(HttpHeaders headers) throws Exception {
String length = headers.get(HttpHeaderNames.CONTENT_RANGE);
int pos = length.lastIndexOf('/');
length = length.substring(pos + 1);
this.totalLength = Long.parseLong(length);
return State.CONTINUE;
}
@Override
public State onBodyPartReceived(HttpResponseBodyPart bodyPart) throws Exception {
os.getChannel().write(bodyPart.getBodyByteBuffer());
byteTransferred += bodyPart.length();
//log.info("byteTransferred: {}", byteTransferred);
return State.CONTINUE;
}
@Override
public FragmentResponse onCompleted(Response response) throws Exception {
log.info("complete");
return new FragmentResponse(response.getStatusCode(), totalLength);
}
});
return whenResponse.get();
}
public void download() throws Exception {
Files.deleteIfExists(Path.of(localFilePath));
long offset = 0;
FragmentResponse response = downloadFragment(0, fragmentSize);
offset += fragmentSize;
this.fileLength = response.getTotalLength();
if (this.fileLength <= fragmentSize) {
return;
}
while (offset < fileLength - 1) {
log.info("Offset: {}", offset);
timestamp = System.currentTimeMillis();
response = downloadFragment(offset, fragmentSize);
offset += fragmentSize;
long duration = System.currentTimeMillis() - timestamp;
log.info("file:{}, offset:{}, response: {}, speed:{}", fileLength, offset, response.getStatus(), fragmentSize / duration);
long wait = interval * 1000L - duration;
if (wait > 0) {
log.info("Sleep {} milliseconds for rate limit", wait);
Thread.sleep(wait);
}
}
log.info("Download finished");
client.close();
}
public static void main(String[] args) throws Exception {
String url = "https://mirrors.ustc.edu.cn/ubuntu/dists/jammy/main/signed/linux-amd64/5.13.0-19.19/signed.tar.gz";
String path = "/home/milton/Downloads/signed.tar.gz";
long rateLimit = 500 * 1024L;
long fragmentSize = 10 * 1024 * 1024L;
FragmentableDownloader downloader = new FragmentableDownloader(rateLimit, fragmentSize, url, path);
downloader.download();
}
public static class FragmentResponse {
private int status;
private long totalLength;
public FragmentResponse(int status, long totalLength) {
this.status = status;
this.totalLength = totalLength;
}
public int getStatus() {
return status;
}
public long getTotalLength() {
return totalLength;
}
}
}
Ref
AsyncHttpClient And Download Speed Limit的更多相关文章
- Speed Limit 分类: POJ 2015-06-09 17:47 9人阅读 评论(0) 收藏
Speed Limit Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 17967 Accepted: 12596 Des ...
- E - Speed Limit(2.1.1)
E - Speed Limit(2.1.1) Time Limit:1000MS Memory Limit:30000KB 64bit IO Format:%I64d & %I ...
- [ACM] poj 2017 Speed Limit
Speed Limit Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 17030 Accepted: 11950 Des ...
- poj 2017 Speed Limit
Speed Limit Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 17704 Accepted: 12435 Des ...
- zoj 2176 Speed Limit
Speed Limit Time Limit: 2 Seconds Memory Limit: 65536 KB Bill and Ted are taking a road trip. B ...
- POJ 2017 Speed Limit (直叙式的简单模拟 编程题目 动态属性很少,难度小)
Sp ...
- Kattis - Speed Limit
Speed Limit Bill and Ted are taking a road trip. But the odometer in their car is broken, so they do ...
- Poj 2017 Speed Limit(水题)
一.Description Bill and Ted are taking a road trip. But the odometer in their car is broken, so they ...
- Speed Limit
http://poj.org/problem?id=2017 #include<stdio.h> int main() { int n,mile,hour; ) { ,h = ; whil ...
- PyTorch DataLoader NumberWorkers Deep Learning Speed Limit Increase
这意味着训练过程将按顺序在主流程中工作. 即:run.num_workers. ,此外, ,因此,主进程不需要从磁盘读取数据:相反,这些数据已经在内存中准备好了. 这个例子中,我们看到了20%的加 ...
随机推荐
- Keep English Level-04
firm -- 坚定的,坚固的;公司 share -- n 股份,份额 executive -- 执行官 There is no chance,no density,no fate,that can ...
- 08-避免Latch的产生
1.Latch简介 Latch就是锁存器,是一种在异步电路系统中,对输入信号电平敏感的单元,用来存储信息 锁存器在数据未锁存时,输出端的信号随输入信号变化,就像信号通过一个缓冲器,一旦锁存信号有效,数 ...
- 如何取消VSCODE文件夹折叠
1.问题 如图所示,文件夹折叠在一起,导致我无法在父文件夹中新建一个文件夹,而是只能在子文件夹中新建文件夹 2.解决 原因:文件夹以紧凑方式呈现,取消即可 1. 打开设置,在里面搜索Explorer: ...
- 【TouchGFX】代码结构
生成代码与用户代码 代码结构图示如下 据上图显示代码结构分为三层 引擎 这是TouchGFX提供的标准类,作为生成类的基类 生成 这是touchgfx designer生成的类,作为用户类的基类,这部 ...
- AspNetCore在docker里访问Oracle数据库的坑:ORA-01882: timezone region not found
哦吼 之前刚说了尝试了使用docker来部署AspNetCore应用,结果这才刚上班就遇到问题了= =- 我这项目用的数据库是Oracle,之前直接运行没啥问题,但放在docker里运行就报了这个错误 ...
- [转帖]Linux内存之Cache
一. Linux内存之Cache 1.1.Cache 1.1.1.什么是Cache? Cache存储器,是位于CPU和主存储器DRAM之间的一块高速缓冲存储器,规模较小,但是速度很快,通常由SRAM( ...
- [转帖]生产环境 TiDB 集群混合部署实践
https://tidb.net/book/tidb-monthly/2022/2022-04/usercase/tidb-cluster 一.背景 由于各种场外因素导致我们不能自由选择的理想硬件环 ...
- [转帖]一、Kafka Tool使用
一.Kafka Tool使用 1.添加cluster 2.开启SASL_PLAINTEXT 如果kafka 开启SASL_PLAINTEXT认证(用户名和密码认证) 3.高级设置 如果设置的是SASL ...
- [转帖]解决jmeter请求响应结果乱码的问题
如下图所示,请求百度接口的时候,发现返回的信息里面中文是乱码 这个时候我们只需要改一下jmeter里的配置文件,设置响应结果的字符编码为UTF-8就行了. 进入jmeter安装目录/bin中,找到jm ...
- Jmeter学习之一_连接与测试Redis
Jmeter学习之一_连接与测试Redis 简介 下载: https://dlcdn.apache.org//jmeter/binaries/apache-jmeter-5.6.zip 注意事项: D ...