httpClient同步、异步性能对比

news2024/11/30 0:34:58

0、测试目的

同步阻塞模式下,如果服务端接口响应较慢,那会直接影响客户端接口请求的吞吐量,虽然可以通过在应用代码中通过异步线程的方式优化,但是会增加客户端的线程开销。所以考虑用异步模式来解决这个问题

因此测试时,主要是针对线程数设置比较小的情况下,客户端发起请求的吞吐量来进行对比

1、准备工作

用spring boot写一个最简单的接口:sleep 1s,然后返回ok
在这里插入图片描述

客户端程序引入httpClient依赖:

<dependency>
    <groupId>org.apache.httpcomponents.client5</groupId>
     <artifactId>httpclient5</artifactId>
     <version>5.1.3</version>
 </dependency>

2、同步模式

代码:

import lombok.SneakyThrows;
import org.apache.hc.client5.http.ClientProtocolException;
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
import org.apache.hc.core5.http.ClassicHttpResponse;
import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.HttpStatus;
import org.apache.hc.core5.http.ParseException;
import org.apache.hc.core5.http.io.HttpClientResponseHandler;
import org.apache.hc.core5.http.io.entity.EntityUtils;

import java.io.IOException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

public class SyncClientHttpTest {

    static final CloseableHttpClient httpclient;

    static {
        PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
        connectionManager.setMaxTotal(1000);
        connectionManager.setDefaultMaxPerRoute(100);

        httpclient = HttpClients.custom().setConnectionManager(connectionManager).build();
    }

    public static void main(final String[] args) throws Exception {
        AtomicInteger atomicInteger = new AtomicInteger(0);

        AtomicBoolean stop = new AtomicBoolean(false);

        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                while (!stop.get()) {
                    httpGet();
                    atomicInteger.incrementAndGet();
                }
            }).start();
        }

        Thread.sleep(30000);

        stop.set(true);
        Thread.sleep(1000);

        System.out.println(atomicInteger.get());


        System.exit(0);

    }

    @SneakyThrows
    private static void httpGet() {
        final HttpGet httpget = new HttpGet("http://localhost:8080/test");

        // Create a custom response handler
        final HttpClientResponseHandler<String> responseHandler = new HttpClientResponseHandler<String>() {

            @Override
            public String handleResponse(
                    final ClassicHttpResponse response) throws IOException {
                final int status = response.getCode();
                if (status >= HttpStatus.SC_SUCCESS && status < HttpStatus.SC_REDIRECTION) {
                    final HttpEntity entity = response.getEntity();
                    try {
                        return entity != null ? EntityUtils.toString(entity) : null;
                    } catch (final ParseException ex) {
                        throw new ClientProtocolException(ex);
                    }
                } else {
                    throw new ClientProtocolException("Unexpected response status: " + status);
                }
            }

        };
        final String responseBody = httpclient.execute(httpget, responseHandler);
//            System.out.println(responseBody);
        if(!responseBody.equals("ok")) {
            throw new RuntimeException("error");
        }
    }
}

}

开启5个线程,循环发起请求30s

打印结果:150
差不多每秒5个请求,符合预期

改为10个线程
打印结果:300

改为100个线程
打印结果:3000

请求吞吐和线程数呈线性增长关系(线程数应小于maxPerRoute)

3、异步模式

代码:

import lombok.SneakyThrows;
import org.apache.hc.client5.http.async.methods.*;
import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
import org.apache.hc.client5.http.impl.async.HttpAsyncClients;
import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManager;
import org.apache.hc.core5.concurrent.FutureCallback;
import org.apache.hc.core5.reactor.IOReactorConfig;
import org.apache.hc.core5.util.Timeout;

import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Example of asynchronous HTTP/1.1 request execution.
 */
public class AsyncClientHttpTest {

    static final CloseableHttpAsyncClient client;

    static final AtomicInteger atomicInteger = new AtomicInteger(0);
    static final AtomicBoolean stop = new AtomicBoolean(false);

    static {
        PoolingAsyncClientConnectionManager connectionManager = new PoolingAsyncClientConnectionManager();
        connectionManager.setMaxTotal(1000);
        connectionManager.setDefaultMaxPerRoute(100);

        IOReactorConfig ioReactorConfig = IOReactorConfig.custom()
            .setSoTimeout(Timeout.ofSeconds(5))
                .setIoThreadCount(5) //IO线程数
                .build();

        client = HttpAsyncClients.custom()
                .setIOReactorConfig(ioReactorConfig)
                .setConnectionManager(connectionManager)
                .build();

        client.start();
    }

    public static void main(final String[] args) throws Exception {

         new Thread(()->{
             while (!stop.get()) {
                 httpGet();
             }
         }).start();

        Thread.sleep(5000);
        stop.set(true);

        Thread.sleep(25000);

        System.out.println(atomicInteger.get());

//        client.close(CloseMode.GRACEFUL);

        System.exit(0);
    }

    @SneakyThrows
    private static void httpGet() {
        final SimpleHttpRequest request = SimpleRequestBuilder.get()
                .setUri("http://localhost:8080//test")
                .build();

        final Future<SimpleHttpResponse> future = client.execute(
                SimpleRequestProducer.create(request),
                SimpleResponseConsumer.create(),
                new FutureCallback<SimpleHttpResponse>() {

                    @Override
                    public void completed(final SimpleHttpResponse response) {
//                        System.out.println(request + "->" + new StatusLine(response));
//                        System.out.println(response.getBody().getBodyText());
                        if(!response.getBody().getBodyText().equals("ok")) {
                            throw new RuntimeException("error");
                        }
                        atomicInteger.incrementAndGet();
                    }

                    @Override
                    public void failed(final Exception ex) {
                        System.out.println(request + "->" + ex);
                    }

                    @Override
                    public void cancelled() {
                        System.out.println(request + " cancelled");
                    }

                });
    }

}

ps: 这里代码其实不够严谨,不过测试结果对比已经很悬殊了,不影响最终结论

开启5个IO线程(不设置默认为cpu核数)
客户端1个线程循环发起请求5s,之后再sleep 25s打印结果

打印结果:2700

修改代码:connectionManager.setDefaultMaxPerRoute(100);
->connectionManager.setDefaultMaxPerRoute(200);
调大maxPerRoute为200

打印结果:5400

可以看到异步模式下,每秒的吞吐受maxPerRoute的影响较大(基本持平)
注意如果不手动设置,这个默认值为5,所以如果不进行ConnectionManager设置,异步的测试结果会很差

3、结论

异步模式下因为使用了多路复用,一个IO线程管理多个连接,所以只需少量线程即可进行大量的远程接口调用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/3212.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【网络篇】如何给虚拟机添加网卡,设置固定ip

引言 基于Centos7&#xff0c;准备两台虚拟机作为rac服务器。 以Oracle rac集群的配置说明。 网络分配 根据子网地址&#xff0c;我们给虚拟机分配如下ip: 名称公网私网网关rac1192.168.189.10192.168.83.10192.168.189.2rac2192.168.189.11192.168.83.11192.168.189.2 说明…

从零到一手写迷你版Vue

Vue响应式设计思路 Vue响应式主要包含&#xff1a; 数据响应式监听数据变化&#xff0c;并在视图中更新Vue2使用Object.defineProperty实现数据劫持Vu3使用Proxy实现数据劫持模板引擎提供描述视图的模板语法插值表达式{{}}指令 v-bind, v-on, v-model, v-for,v-if渲染将模板转…

高性能服务器之Reactor设计

今天来针对上一节课讲的多路转接知识再进一步进行设计&#xff0c;Reactor是基于epoll的ET模式设计的&#xff0c;在现在的高校和企业中是广泛应用的&#xff0c;今天我们来实现一个简洁版&#xff0c;完整版博主可没那个实力~ 目录 基本原理 代码实现 epoll_server.cc A…

当面试官让我回答React和Vue框架的区别......

我们为什么需要错误边界 在React组件中可能会由于某些JavaScript错误&#xff0c;导致一些无法追踪的错误&#xff0c;导致应用崩溃。部分 UI 的 JavaScript 错误不应该导致整个应用崩溃。为此&#xff0c;React引入了错误边界(Error Boundary)的概念&#xff1a;可以捕获发生…

MySQL搭建主从复制流程及相关问题

目录一、关于主从复制1.1 关于主从复制1.2 应用场景1.3 优缺点1.4 原理二、配置主从复制2.1 同步各个服务器的时间2.2 修改主库&#xff08;M1&#xff09;配置2.3 主库&#xff08;M1&#xff09;为从库&#xff08;S1\S2&#xff09;增加账号2.3 查看主库&#xff08;M1&…

欢迎女神科学家颜宁回国,并祝她如愿以偿

目录1、女神科学家颜宁是谁2、颜宁在深圳人才论坛最新演讲&#xff0c;以及招聘邮箱3、颜宁微博回应4、结论与展望最近女神科学家颜宁回国了&#xff0c;整个科学界和中国都沸腾了&#xff0c;也上了热搜&#xff0c;成了热门话题&#xff0c;越来越多的海归精英选择回国 1、…

Python 和Java 哪个更适合做自动化测试?

很多小伙伴在功能测试行业工作了2、3年后&#xff0c;发现自己已经把功能测试做的非常好了&#xff0c;已经到职业发展和薪资发展的瓶颈期了&#xff0c;就想着学点东西&#xff0c;提升一下技能。 而对于功能测试升级来说&#xff0c;一般有这么3个主流的发展方向&#xff1a;…

事件/边沿检测--上升沿检测、下降沿检测

检测上升沿&#xff1a;&#xff08;从低到高的跳变 __| ) input sig_a; reg sig_a_d1; wire sig_a_risedge; alaways (posedge clk or negedge rstb) begin if(!rstb) sig_a_d1 < 1b0; else sig_a_d1 < sig_a; end assign sig_a_risedge sig_a & !sig_a_d1; …

【02】概率图模型在真实世界中的应用案例

概率图模型在真实世界中的应用案例 概率图模型有许多不同的实际应用。 为了激起大家对概率图模型的兴趣&#xff0c;也为了让大家能够对概率图模型有感性的认知&#xff0c;本章我会分享概率图模型的诸多实际应用案例。 文章目录图像中的概率模型图像生成图像修复图像降噪语言…

【Python百日进阶-WEB开发】Day171 - Django案例:03配置工程日志

文章目录八、配置工程日志8.1 目的和原因8.2 配置工程日志的步骤8.2.1 配置工程日志8.2.2 准备日志文件目录8.2.3 日志器记录器的使用8.2.4 Git管理工程日志九、配置前端静态文件9.1 准备静态文件9.2 指定静态文件的加载路径十、相关文档八、配置工程日志 8.1 目的和原因 目的…

什么是跨域?以及解决方案

现在的web项目&#xff0c;很多都是前后端分离&#xff0c;特别容易出现跨域问题 那么什么是跨域问题呢?本篇文章带你彻底从本质上弄明白什么是跨域问题以及如何解决 一、跨域有什么现象 首先我们看一下现象&#xff0c;如何出现的跨域问题。例&#xff1a; 前段&#xff1a…

【Linux】基础IO —— 上

&#x1f387;Linux&#xff1a;基础IO详解 博客主页&#xff1a;一起去看日落吗分享博主的在Linux中学习到的知识和遇到的问题博主的能力有限&#xff0c;出现错误希望大家不吝赐教分享给大家一句我很喜欢的话&#xff1a; 看似不起波澜的日复一日&#xff0c;一定会在某一天让…

Web渗透测试攻防之浅述信息收集

前言 众所周知渗透测试的本质是信息收集&#xff0c;在渗透测试中信息收集的质量直接关系到渗透测试成果的与否。在对系统进行渗透测试前的信息收集是通过各种方式获取所需要的信息&#xff0c;收集的信息越多对目标进行渗透的优势越有利。通过利用获取到的信息对系统进行渗透…

Java Spring Cloud XVIII 之 Kafka I

Java Spring Cloud XVIII 之 Kafka I Kafka 1.Kafka简介 Kafka是由Apache软件基金会开发的一个开源流处理平台&#xff0c;由Scala和Java编写。该项目的目标是为处理实时数据提供一个统一、高吞吐、低延迟的平台。Kafka最初是由LinkedIn开发&#xff0c;并随后于2011年初开源…

C++ 类和对象 (中)

作者&#xff1a;小萌新 专栏&#xff1a;C初阶 作者简介&#xff1a;大二学生 希望能和大家一起进步 本篇博客目标&#xff1a;梳理自己六个小时学到的知识 并且将类和对象知识分享给大家 专注的去做一件事 如果累了就去休息 C 类和对象 中本章学习目标前言一. 构造函数1.1 概…

破解系统密码与重装windows系统

数据来源 一、利用5次shift漏洞破解win7密码 1.1 漏洞 1. 在未登录时&#xff0c;连续按5次shift键&#xff0c;弹出程序C:\Windows\System32\sethc.exe 2. 部分win7及win10系统在未进入系统时&#xff0c;可以通过系统修复漏洞篡改系统文件名&#xff01; 注意&#xff1a;…

使用Maven部署到远程Linux服务器Tomcat

一、安装JDK 首先给服务器安装jdk&#xff0c;访问官网下载&#xff1a;Java Downloads | Oracle&#xff0c;下载图中的版本。首先我使用的是tomcat10&#xff0c;最低支持jdk1.8。安装了jdk19&#xff0c;是当时的最新版实测tomcat开启失败&#xff0c;新版jdk也不自带jre&a…

Windows上使用QEMU创建aarch64(ARM64)虚拟机

前言 随着国产化的推进&#xff0c;现在采用ARM、MIPS的机器越来越多&#xff0c;作为开发、运维人员要调测软件总不能每种架构的机器都去买一台吧&#xff1f;主要像博主这样的穷B&#xff0c;实在也是承受不起。。 需要的工具 1、QEMU Windows版官网下载地址&#xff1a;…

软件测试最最最重要的事

软件测试用例得出软件测试用例的内容&#xff0c;其次&#xff0c;按照软件测试写作方法&#xff0c;落实到文档中&#xff0c;两者是形式和内容的关系&#xff0c;好的测试用例不仅方便自己和别人查看&#xff0c;而且能帮助设计的时候考虑的更周。 一个好的测试用例必须包含…

Articulate360在线学习课件制作工具

Articulate是一款全新理念的在线和移动学习课件制作工具&#xff0c;可以说是目前国际上用户最广泛的e-learning课件制作工具之一。它包含了全新版的Storyline 360和Rise 360以及大量其他创作应用程序。使用Storyline 360开发可在所有设备上运行的自定义交互式课程&#xff0c;…