• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

httpClient同步、异步性能对比

武飞扬头像
Java小田
帮助1

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线程管理多个连接,所以只需少量线程即可进行大量的远程接口调用

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhhackcf
系列文章
更多 icon
同类精品
更多 icon
继续加载