searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

异步http工具类介绍:AsyncHttpUtil

2023-06-21 02:15:07
191
0

目标

      在一个线程中大量调用http 同步请求,进行异步调用,不希望一个http 请求消耗一个线程的做法。在常用的的OK http,Apache http 的异步方案是采用线程池异步,一个请求对应一个线程, 并发请求量大时候,线程耗尽,只能要等待,性能不好,需要找一个可以固定线程池的真正异步非阻塞的client。

解决

    AsyncHttpClient 基于netty 框架开发,做到了类似go 的协程的异步http 能力,固定CPU 核数的线程池,可以支持大量http 异步请求,在大量并发http 请求下,相比ok http,Apache http 性能要好很多

介绍

    基于netty框架,使用操作系统native的高性能异步库 epoll,Kqueue进行网络IO异步管理,可以做到用少量线程,同时处理大量网络请求。该client实现了Http和 websocket协议(后续可以基于该库底层实现应用消息协议的的client)

pom.xml引入依赖

<dependency> 
      <groupId>org.asynchttpclient</groupId>
      <artifactId>async-http-client</artifactId> 
      <version>2.12.3</version>
</dependency>

 

测试

使用时,需要引入依赖

import org.asynchttpclient.*;
import static org.asynchttpclient.Dsl.*;

1:批量请求方法

public static List<ListenableFuture<Response>> AsyncBatchHttpClient(List<RequestBuilder> requests) {
      List<ListenableFuture<Response>> respons = new ArrayList();
      for(RequestBuilder request:requests)
     {
          ListenableFuture<Response> whenRes = asyncHttpClient
                                                  .executeRequest(request.addHeader("Content-Type","application/json")
                                                  .setReadTimeout(10000));
          respons.add(whenRes); 
     }
     return respons; 
}

请求返回的事一个ListenableFuture<Response>,可以进行很方便的future 异步处理,如果要批量等待全部完成,可以参考以下处理:

public static List<String> waitAllDone(List<ListenableFuture<Response>> respons) {
      List<String> results = new ArrayList<>(); 
      Iterator<ListenableFuture<Response>> it = respons.iterator();
       try {
            while(it.hasNext()) {
                  ListenableFuture<Response> respon = it.next(); 
                  try {
                        Response result = respon.get();
                        results.add(result.getResponseBody()); 
                  } catch (ExecutionException e) {
                        LOGGER.info("result get error:"+e.getMessage() ); 
                  }
            }
      } catch (InterruptedException e) {
            e.printStackTrace(); 
      }
      return results; 
}

准备2种接口:一个是快速返回的空接口,一个是允许随机sleep 模拟实际请求的接口,在一个单独的spring boot工程,单独服务启动

/**
* 随机休眠,模拟实际请求 * @return
*/
@RequestMapping(method = RequestMethod.GET, value = "/app/testGet")
public String appRandomTestGet(@RequestParam(value = "max", required = false) Integer max) {
      try {
             Thread.sleep(RandomUtils.nextLong(0,max));
      } catch (InterruptedException e) { 
            e.printStackTrace();
      }
      return "appRandomTestGet max:"+max+" ok"; 
}
/**
* 快速返回接口 * @return
*/
@RequestMapping(method = RequestMethod.GET, value = "/app/fastGet") 
public String appFastGet() {
      return "appFastGet ok"; 
}

在写请求服务,这里用上面封装的批量请求方法,先测试fastGet

      @Scheduled(cron = "0/4 * * * * ?") 
      public void testhttp(){
      List<RequestBuilder> requests = new ArrayList<>(); 
      int size =5000;
      //同时请求快速http 请求
      for(int i=0;i<size;i++) {
            requests.add(get("http://10.215.40.137:7071/app/fastGet")); 
      }
      httpdemo(requests,"fastGet-"+size); requests = new ArrayList<>();
      size =1000;
      //同时请求快速http 请求
      for(int i=0;i<size;i++) { 
            requests.add(get("http://10.215.40.137:7071/app/fastGet"));
      }
      httpdemo(requests,"fastGet-"+size); }

      public void httpdemo(List<RequestBuilder> requests,String type ) {
            long start = System.currentTimeMillis();
            List<ListenableFuture<Response>> respons = AsyncBatchHttpClient(requests); 
            waitAllDone(respons);
            LOGGER.info(type +" all done use:"+(System.currentTimeMillis()-start));
      }

单线程同时请求1000以上

如日志所示,5000个请求在180~350ms,大部分在220ms左右。请求1000个在25~50ms,大部分在30ms左右,取决于被压服务最大返回时间,这里被压服务只有一个进 程,所以会有点慢,符合预期,如果是okHttp 或者Apache http 会慢很多倍

查看线程数量是否随着请求量增大而增大: asyncHttp相关的线程 保持16个不变,测试机器CPU8核,猜测是处理请求和结果的线程池为2倍CPU核数

如下线程监控截图

使用多线程模拟真实场景,依旧如此,性能不错

 

0条评论
0 / 1000
张****钏
15文章数
1粉丝数
张****钏
15 文章 | 1 粉丝
原创

异步http工具类介绍:AsyncHttpUtil

2023-06-21 02:15:07
191
0

目标

      在一个线程中大量调用http 同步请求,进行异步调用,不希望一个http 请求消耗一个线程的做法。在常用的的OK http,Apache http 的异步方案是采用线程池异步,一个请求对应一个线程, 并发请求量大时候,线程耗尽,只能要等待,性能不好,需要找一个可以固定线程池的真正异步非阻塞的client。

解决

    AsyncHttpClient 基于netty 框架开发,做到了类似go 的协程的异步http 能力,固定CPU 核数的线程池,可以支持大量http 异步请求,在大量并发http 请求下,相比ok http,Apache http 性能要好很多

介绍

    基于netty框架,使用操作系统native的高性能异步库 epoll,Kqueue进行网络IO异步管理,可以做到用少量线程,同时处理大量网络请求。该client实现了Http和 websocket协议(后续可以基于该库底层实现应用消息协议的的client)

pom.xml引入依赖

<dependency> 
      <groupId>org.asynchttpclient</groupId>
      <artifactId>async-http-client</artifactId> 
      <version>2.12.3</version>
</dependency>

 

测试

使用时,需要引入依赖

import org.asynchttpclient.*;
import static org.asynchttpclient.Dsl.*;

1:批量请求方法

public static List<ListenableFuture<Response>> AsyncBatchHttpClient(List<RequestBuilder> requests) {
      List<ListenableFuture<Response>> respons = new ArrayList();
      for(RequestBuilder request:requests)
     {
          ListenableFuture<Response> whenRes = asyncHttpClient
                                                  .executeRequest(request.addHeader("Content-Type","application/json")
                                                  .setReadTimeout(10000));
          respons.add(whenRes); 
     }
     return respons; 
}

请求返回的事一个ListenableFuture<Response>,可以进行很方便的future 异步处理,如果要批量等待全部完成,可以参考以下处理:

public static List<String> waitAllDone(List<ListenableFuture<Response>> respons) {
      List<String> results = new ArrayList<>(); 
      Iterator<ListenableFuture<Response>> it = respons.iterator();
       try {
            while(it.hasNext()) {
                  ListenableFuture<Response> respon = it.next(); 
                  try {
                        Response result = respon.get();
                        results.add(result.getResponseBody()); 
                  } catch (ExecutionException e) {
                        LOGGER.info("result get error:"+e.getMessage() ); 
                  }
            }
      } catch (InterruptedException e) {
            e.printStackTrace(); 
      }
      return results; 
}

准备2种接口:一个是快速返回的空接口,一个是允许随机sleep 模拟实际请求的接口,在一个单独的spring boot工程,单独服务启动

/**
* 随机休眠,模拟实际请求 * @return
*/
@RequestMapping(method = RequestMethod.GET, value = "/app/testGet")
public String appRandomTestGet(@RequestParam(value = "max", required = false) Integer max) {
      try {
             Thread.sleep(RandomUtils.nextLong(0,max));
      } catch (InterruptedException e) { 
            e.printStackTrace();
      }
      return "appRandomTestGet max:"+max+" ok"; 
}
/**
* 快速返回接口 * @return
*/
@RequestMapping(method = RequestMethod.GET, value = "/app/fastGet") 
public String appFastGet() {
      return "appFastGet ok"; 
}

在写请求服务,这里用上面封装的批量请求方法,先测试fastGet

      @Scheduled(cron = "0/4 * * * * ?") 
      public void testhttp(){
      List<RequestBuilder> requests = new ArrayList<>(); 
      int size =5000;
      //同时请求快速http 请求
      for(int i=0;i<size;i++) {
            requests.add(get("http://10.215.40.137:7071/app/fastGet")); 
      }
      httpdemo(requests,"fastGet-"+size); requests = new ArrayList<>();
      size =1000;
      //同时请求快速http 请求
      for(int i=0;i<size;i++) { 
            requests.add(get("http://10.215.40.137:7071/app/fastGet"));
      }
      httpdemo(requests,"fastGet-"+size); }

      public void httpdemo(List<RequestBuilder> requests,String type ) {
            long start = System.currentTimeMillis();
            List<ListenableFuture<Response>> respons = AsyncBatchHttpClient(requests); 
            waitAllDone(respons);
            LOGGER.info(type +" all done use:"+(System.currentTimeMillis()-start));
      }

单线程同时请求1000以上

如日志所示,5000个请求在180~350ms,大部分在220ms左右。请求1000个在25~50ms,大部分在30ms左右,取决于被压服务最大返回时间,这里被压服务只有一个进 程,所以会有点慢,符合预期,如果是okHttp 或者Apache http 会慢很多倍

查看线程数量是否随着请求量增大而增大: asyncHttp相关的线程 保持16个不变,测试机器CPU8核,猜测是处理请求和结果的线程池为2倍CPU核数

如下线程监控截图

使用多线程模拟真实场景,依旧如此,性能不错

 

文章来自个人专栏
java开发实践
6 文章 | 1 订阅
0条评论
0 / 1000
请输入你的评论
0
0