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的更多相关文章

  1. Speed Limit 分类: POJ 2015-06-09 17:47 9人阅读 评论(0) 收藏

    Speed Limit Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 17967   Accepted: 12596 Des ...

  2. E - Speed Limit(2.1.1)

    E - Speed Limit(2.1.1) Time Limit:1000MS     Memory Limit:30000KB     64bit IO Format:%I64d & %I ...

  3. [ACM] poj 2017 Speed Limit

    Speed Limit Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 17030   Accepted: 11950 Des ...

  4. poj 2017 Speed Limit

    Speed Limit Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 17704   Accepted: 12435 Des ...

  5. zoj 2176 Speed Limit

    Speed Limit Time Limit: 2 Seconds      Memory Limit: 65536 KB Bill and Ted are taking a road trip. B ...

  6. POJ 2017 Speed Limit (直叙式的简单模拟 编程题目 动态属性很少,难度小)

                                                                                                      Sp ...

  7. Kattis - Speed Limit

    Speed Limit Bill and Ted are taking a road trip. But the odometer in their car is broken, so they do ...

  8. Poj 2017 Speed Limit(水题)

    一.Description Bill and Ted are taking a road trip. But the odometer in their car is broken, so they ...

  9. Speed Limit

    http://poj.org/problem?id=2017 #include<stdio.h> int main() { int n,mile,hour; ) { ,h = ; whil ...

  10. PyTorch DataLoader NumberWorkers Deep Learning Speed Limit Increase

    这意味着训练过程将按顺序在主流程中工作. 即:run.num_workers.   ,此外, ,因此,主进程不需要从磁盘读取数据:相反,这些数据已经在内存中准备好了. 这个例子中,我们看到了20%的加 ...

随机推荐

  1. 一 , FileChanle

    package nio; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer ...

  2. Go-稀疏数组

    package main import "fmt" // 稀疏数组 // 1. 二维数组 // 2. 存在大量相同相同数据和少量不同数据 // 思维: 将大量相同数据转化为: (数 ...

  3. [转帖]TiFlash 简介

    overview TiFlash 是 TiDB HTAP 形态的关键组件,它是 TiKV 的列存扩展,在提供了良好的隔离性的同时,也兼顾了强一致性.列存副本通过 Raft Learner 协议异步复制 ...

  4. [转帖]【k8s】1、基础概念和架构及组件

    文章目录 一.kubernetes概述 1.什么是kubernetes? 2.应用程序部署方式的演变 3.为什么要用kubernetes? 二.kubernetes 特性 三.Kubernetes集群 ...

  5. 在k8s中的控制器和部署服务-ReplicationController和ReplicaSet

    pod 代表了 k8s 中的基本部署单元,但是在实际应用场景中,服务不可能是单个pod运行的,否则会出现"单点".在 k8s 中对 pod 的托管部署,专门抽象成了单独的资源.其中 ...

  6. 【VMware vSAN】使用命令行从vSAN集群中移除ESXi主机并加入到新的vSAN集群。

    说明 本文只是陈述了一种方法,不必评判谁对谁错谁好谁坏,选择适合自己的即可. 环境 站点名称 vCenter版本 vSAN集群 集群主机 主机版本 磁盘组 vcsa67.lab.com vCenter ...

  7. 【解决了一个小问题】在某个linux基础镜像中安装python特定的版本

    作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢! cnblogs博客 zhihu Github 公众号:一本正经的瞎扯 在某个基础镜像中,安装了python3.6.但是一个测试需 ...

  8. ABP vNext系列文章01---模块化

    一.模块化应用  1.继承AbpModule 每个模块都应该定义一个模块类.定义模块类的最简单方法是创建一个派生自AbpModule的类,如下所示:  2.配置依赖注入和其他模块---ConfigSe ...

  9. 错误的daemon.json配置,导致docker容器启动失败 Failed to start Docker Application Container Engine

    docker学习-配置错误的源 问题点剖析 参考 docker学习-配置错误的源 问题点剖析 使用docker安装了nginx,编写Dockerfile,映射端口,终于跑起来了.但是,当我重启服务器, ...

  10. 【Mysql】复合主键的索引

    复合主键在where中使用查询的时候到底走不走索引呢?例如下表: create table index_test ( a int not null, b int not null, c int not ...