登录
原创

HTTP API请求的耗时优化-Java 版

发布于 2024-08-26 阅读 841
  • 后端
原创

背景

搞 Java 的同学,在学习数据库的阶段,JDBC一定是你绕不开的技术。而使用 JDBC,你一定听说过数据库连接池。

我记得在我学习 Java 到数据库这个阶段的时候,老师告诉我们说:数据库的连接是很昂贵的,开销也是很大的,因此,我们可以尽可能的去复用。当时老师举了一个例子,说他老爸去外地打工,打来长途电话,拨通后,家里的兄弟姐妹轮流和老爸通电话。这个使用场景中,只需要拨通一次,就可以实现和家里的所有成员通话,其实就是连接复用的一个现实场景。同时我们也可以知道,拨通一次电话,的确是很耗时的。在那个使用固定电话和共用电话的年代,你如果想给家里打电话,你需要走到共用电话厅,接电话的人,也有可能出门了。如果正好在家里,接电话的人是首先听到电话铃声响起,然后走到电话跟前,拿起电话,问清对方身份,然后进行通话。我记得当时老师还说,编程的世界里面,很多技术,都是来源于现实。随着学习的深入,这种感受,也是越来越深。

延伸

连接池的概念在编程的世界中得到了很广的应用。我们知道,在 JDBC 中,网络通信通常使用的是 TCP/IP 协议 ,而 HTTP 是基于 TCP之上的应用层协议,在Java的标准库 java.net 包中,并没有内置的连接池机制。如果我们也想使用连接池,通常可以使用第三方库或框架,比如Apache HttpClientOkHttp,具体的实现细节,大家可以 debug 一步一步去查看,在这里,我使用Apache HttpClient这个第三方库来进行应用的举例。

实战

pom 依赖

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

使用的具体版本号,你可能需要和自己的其他依赖包或者框架进行兼容,这个需要开发者自己确定。

测试代码,不使用连接池

package com.example;

import org.apache.http.ParseException;
import org.apache.http.client.HttpResponseException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.util.EntityUtils;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.concurrent.TimeUnit;

@SpringBootTest
class ApidemoApplicationTests {

  

    @Test
    void noPoolDemo() throws IOException {
        for (int i = 0; i < 100; i++) {
            long start = System.currentTimeMillis();
            String apiUrl = "https://news.qq.com/";
            URL url = new URL(apiUrl);
            BufferedReader in = new BufferedReader(new InputStreamReader((url.openConnection()).getInputStream()));
            String inputLine;
            StringBuffer response = new StringBuffer();
            while ((inputLine = in.readLine()) != null) {
                response.append(inputLine);
            }
            in.close();
//            System.out.println(response);
            System.out.println("第" + i + "次请求耗时: " + (System.currentTimeMillis() - start) + "毫秒");

        }

    }


}

测试结果:

第0次请求耗时: 479毫秒
第1次请求耗时: 134毫秒
第2次请求耗时: 114毫秒
第3次请求耗时: 115毫秒
第4次请求耗时: 107毫秒
第5次请求耗时: 130毫秒
第6次请求耗时: 130毫秒
第7次请求耗时: 104毫秒
第8次请求耗时: 113毫秒
第9次请求耗时: 123毫秒
第10次请求耗时: 135毫秒
第11次请求耗时: 119毫秒
第12次请求耗时: 109毫秒
第13次请求耗时: 125毫秒
第14次请求耗时: 110毫秒
第15次请求耗时: 120毫秒
第16次请求耗时: 102毫秒
第17次请求耗时: 133毫秒
第18次请求耗时: 112毫秒
第19次请求耗时: 127毫秒
第20次请求耗时: 108毫秒
第21次请求耗时: 104毫秒
第22次请求耗时: 124毫秒
第23次请求耗时: 111毫秒
第24次请求耗时: 102毫秒
第25次请求耗时: 111毫秒
第26次请求耗时: 130毫秒
第27次请求耗时: 133毫秒
第28次请求耗时: 111毫秒
第29次请求耗时: 116毫秒
第30次请求耗时: 102毫秒
第31次请求耗时: 110毫秒
第32次请求耗时: 97毫秒
第33次请求耗时: 112毫秒
第34次请求耗时: 116毫秒
第35次请求耗时: 131毫秒
第36次请求耗时: 104毫秒
第37次请求耗时: 122毫秒
第38次请求耗时: 123毫秒
第39次请求耗时: 107毫秒
第40次请求耗时: 113毫秒
第41次请求耗时: 109毫秒
第42次请求耗时: 126毫秒
第43次请求耗时: 111毫秒
第44次请求耗时: 116毫秒
第45次请求耗时: 136毫秒
第46次请求耗时: 108毫秒
第47次请求耗时: 111毫秒
第48次请求耗时: 110毫秒
第49次请求耗时: 132毫秒
第50次请求耗时: 106毫秒
第51次请求耗时: 104毫秒
第52次请求耗时: 125毫秒
第53次请求耗时: 120毫秒
第54次请求耗时: 132毫秒
第55次请求耗时: 108毫秒
第56次请求耗时: 112毫秒
第57次请求耗时: 115毫秒
第58次请求耗时: 118毫秒
第59次请求耗时: 123毫秒
第60次请求耗时: 125毫秒
第61次请求耗时: 112毫秒
第62次请求耗时: 125毫秒
第63次请求耗时: 113毫秒
第64次请求耗时: 109毫秒
第65次请求耗时: 124毫秒
第66次请求耗时: 110毫秒
第67次请求耗时: 117毫秒
第68次请求耗时: 120毫秒
第69次请求耗时: 118毫秒
第70次请求耗时: 112毫秒
第71次请求耗时: 109毫秒
第72次请求耗时: 118毫秒
第73次请求耗时: 122毫秒
第74次请求耗时: 120毫秒
第75次请求耗时: 116毫秒
第76次请求耗时: 129毫秒
第77次请求耗时: 113毫秒
第78次请求耗时: 119毫秒
第79次请求耗时: 107毫秒
第80次请求耗时: 109毫秒
第81次请求耗时: 109毫秒
第82次请求耗时: 121毫秒
第83次请求耗时: 116毫秒
第84次请求耗时: 110毫秒
第85次请求耗时: 112毫秒
第86次请求耗时: 112毫秒
第87次请求耗时: 132毫秒
第88次请求耗时: 111毫秒
第89次请求耗时: 101毫秒
第90次请求耗时: 125毫秒
第91次请求耗时: 109毫秒
第92次请求耗时: 115毫秒
第93次请求耗时: 108毫秒
第94次请求耗时: 101毫秒
第95次请求耗时: 111毫秒
第96次请求耗时: 123毫秒
第97次请求耗时: 117毫秒
第98次请求耗时: 105毫秒
第99次请求耗时: 136毫秒

测试代码,使用连接池

package com.example;

import org.apache.http.ParseException;
import org.apache.http.client.HttpResponseException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.util.EntityUtils;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.concurrent.TimeUnit;

@SpringBootTest
class ApidemoApplicationTests {

   


    @Test
    void poolDemo() {
        // 创建连接池管理器
        PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
        connectionManager.setMaxTotal(20); // 设置最大连接数
        connectionManager.setDefaultMaxPerRoute(5); // 设置每个路由的默认最大连接数

        // 创建 HttpClient 并传入连接池管理器
        try (CloseableHttpClient httpClient = HttpClients.custom()
                .setConnectionManager(connectionManager)
                .evictIdleConnections(60L, TimeUnit.SECONDS) // 设置空闲连接回收时间
                .build()) {

            for (int i = 0; i < 100; i++) {
                long start = System.currentTimeMillis();

                // 创建一个GET请求
                HttpGet request = new HttpGet("https://news.qq.com/");

                try (CloseableHttpResponse response = httpClient.execute(request)) {
                    if (response.getStatusLine().getStatusCode() == 200) {
                        String responseBody = EntityUtils.toString(response.getEntity());
                        // System.out.println(responseBody);
                    } else {
                        System.err.println("响应状态码: " + response.getStatusLine().getStatusCode());
                    }
                } catch (HttpResponseException e) {
                    System.err.println("请求失败: " + e.getMessage());
                } catch (IOException | ParseException e) {
                    e.printStackTrace();
                }

                System.out.println("第" + i + "次请求耗时: " + (System.currentTimeMillis() - start) + "毫秒");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }


    }

}

测试结果:

第0次请求耗时: 331毫秒
第1次请求耗时: 113毫秒
第2次请求耗时: 100毫秒
第3次请求耗时: 87毫秒
第4次请求耗时: 90毫秒
第5次请求耗时: 100毫秒
第6次请求耗时: 107毫秒
第7次请求耗时: 105毫秒
第8次请求耗时: 103毫秒
第9次请求耗时: 90毫秒
第10次请求耗时: 93毫秒
第11次请求耗时: 108毫秒
第12次请求耗时: 86毫秒
第13次请求耗时: 102毫秒
第14次请求耗时: 94毫秒
第15次请求耗时: 88毫秒
第16次请求耗时: 92毫秒
第17次请求耗时: 87毫秒
第18次请求耗时: 85毫秒
第19次请求耗时: 98毫秒
第20次请求耗时: 116毫秒
第21次请求耗时: 103毫秒
第22次请求耗时: 86毫秒
第23次请求耗时: 94毫秒
第24次请求耗时: 102毫秒
第25次请求耗时: 107毫秒
第26次请求耗时: 95毫秒
第27次请求耗时: 89毫秒
第28次请求耗时: 85毫秒
第29次请求耗时: 92毫秒
第30次请求耗时: 95毫秒
第31次请求耗时: 90毫秒
第32次请求耗时: 91毫秒
第33次请求耗时: 101毫秒
第34次请求耗时: 86毫秒
第35次请求耗时: 90毫秒
第36次请求耗时: 102毫秒
第37次请求耗时: 84毫秒
第38次请求耗时: 96毫秒
第39次请求耗时: 100毫秒
第40次请求耗时: 84毫秒
第41次请求耗时: 85毫秒
第42次请求耗时: 102毫秒
第43次请求耗时: 117毫秒
第44次请求耗时: 92毫秒
第45次请求耗时: 108毫秒
第46次请求耗时: 103毫秒
第47次请求耗时: 102毫秒
第48次请求耗时: 129毫秒
第49次请求耗时: 121毫秒
第50次请求耗时: 102毫秒
第51次请求耗时: 84毫秒
第52次请求耗时: 89毫秒
第53次请求耗时: 90毫秒
第54次请求耗时: 96毫秒
第55次请求耗时: 101毫秒
第56次请求耗时: 106毫秒
第57次请求耗时: 105毫秒
第58次请求耗时: 80毫秒
第59次请求耗时: 103毫秒
第60次请求耗时: 109毫秒
第61次请求耗时: 107毫秒
第62次请求耗时: 98毫秒
第63次请求耗时: 111毫秒
第64次请求耗时: 90毫秒
第65次请求耗时: 92毫秒
第66次请求耗时: 92毫秒
第67次请求耗时: 96毫秒
第68次请求耗时: 108毫秒
第69次请求耗时: 88毫秒
第70次请求耗时: 88毫秒
第71次请求耗时: 87毫秒
第72次请求耗时: 104毫秒
第73次请求耗时: 102毫秒
第74次请求耗时: 103毫秒
第75次请求耗时: 103毫秒
第76次请求耗时: 104毫秒
第77次请求耗时: 95毫秒
第78次请求耗时: 92毫秒
第79次请求耗时: 105毫秒
第80次请求耗时: 91毫秒
第81次请求耗时: 96毫秒
第82次请求耗时: 89毫秒
第83次请求耗时: 125毫秒
第84次请求耗时: 93毫秒
第85次请求耗时: 99毫秒
第86次请求耗时: 98毫秒
第87次请求耗时: 89毫秒
第88次请求耗时: 97毫秒
第89次请求耗时: 91毫秒
第90次请求耗时: 104毫秒
第91次请求耗时: 93毫秒
第92次请求耗时: 92毫秒
第93次请求耗时: 93毫秒
第94次请求耗时: 86毫秒
第95次请求耗时: 85毫秒
第96次请求耗时: 93毫秒
第97次请求耗时: 105毫秒
第98次请求耗时: 100毫秒
第99次请求耗时: 107毫秒

说明,测试结果会因为网络等情况有所差异。

评论区

眉上的汗水,眉下的泪水,你总要选择一样

1

0

1

举报