簡體   English   中英

Okhttp3,http2多路復用POST在高峰加載時請求高響應時間

[英]Okhttp3, http2 multiplexing POST requests high response time at peak load time

我的應用程序將向我的tomcat服務器(啟用了http / 2)發送大約1000個POST請求/分鍾,它將輪詢給定的url並返回html和響應時間,我想實現真正的http / 2多路復用以重用tcp連接我的應用程序和tomcat服務器之間。 我的客戶端使用okhttp,我可以成功建立連接並重復使用很長一段時間,但是當請求數量增加時(例如,排隊:50,運行:50),響應時間也會增加(它增加到2000ms- 15000ms甚至更糟,通常需要300ms-500ms)。 我可以理解這種情況正在發生,因為tcp連接超載請求太多,所以我決定打開多個tcp連接並允許它跨tcp連接分配請求負載。 我強制客戶端使用打開多個連接

Dispatcher dispatcher = new Dispatcher();
dispatcher.setMaxRequests(100);
dispatcher.setMaxRequestsPerHost(5);
ConnectionPool cp = new ConnectionPool(5, 5, TimeUnit.MINUTES);

在wireshark的幫助下,我可以看到5個連接打開,並且還看到4個請求在第一次握手成功后立即關閉,我不知道這是否是http / 2多路復用的預期行為。

如果這是預期的行為,我如何優化它以減少峰值加載時每個請求的響應時間?

或者是否可以使用多個連接在峰值加載時分配負載?

我的示例程序如下,

Security.insertProviderAt(Conscrypt.newProvider(), 1);
sslContext = SSLContext.getInstance("TLSv1.3");
sslContext.init(null, new TrustManager[] {
    new X509TrustManager() {
        @Override
        public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
        }

        @Override
        public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
        }

        @Override
        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
            return new java.security.cert.X509Certificate[]{};
        }
    }       
}, new java.security.SecureRandom());
sslSocketFactory = sslContext.getSocketFactory();
Dispatcher dispatcher = new Dispatcher();
dispatcher.setMaxRequests(100);
dispatcher.setMaxRequestsPerHost(5);
ConnectionPool cp = new ConnectionPool(5, 1, TimeUnit.DAYS);

okHttpClient = new OkHttpClient().newBuilder()
.sslSocketFactory(sslSocketFactory, (X509TrustManager)trustManager[0])
.dispatcher(dispatcher)
.connectionPool(cp)
.hostnameVerifier(new HostnameVerifier() {
    @Override
    public boolean verify(String hostname, SSLSession session) {
        return true;
    }
})
.build();

我們使用conscryt在jdk8中啟用ALPN,

try {
    String url = "https://localhost:8081/myAent/getOutput?1000000004286_1520339351002_"+System.currentTimeMillis();
    String json = "<?xml version=\"1.0\" standalone=\"no\"?><UC mid=\"1000000005011\" pollNow=\"true\" use_ipv6=\"false\" rca=\"0\" rcaFN=\"P|TA|D|J|M\" pFtct=\"\" sFtct=\"\" issecondarydc=\"false\" ssDc=\"false\" nocache=\"1\" storeHtmlResp=\"true\" storeTroubleScrnSht=\"false\" moConfig=\"false\" xconlen=\"8265\"  noScreenshotRecheckForSSLErrors=\"true\" avgDnsTime=\"null\" isProxyRequired=\"false\" userroles=\"EVAL_USER\" uid=\"102030230293029021\" retryDelay=\"2\" retry=\"true\" idcLocUrl=\"http://localhost:8080/app/receivemultipartdata\" isRemoteAgent=\"true\" sendHeaders=\"false\" api=\"ab_345nnn4l4lj4lk23nl4k23342lk4l23j4\" ut=\""+(System.currentTimeMillis()+"")+"\" mt=\"URL\" dctimeout=\"70\" pollinterval=\"1440\" locid=\"48\" log=\"1\" currentstatus=\"1\" postUrl=\"https://example.com\"><Url acc=\"\" forced_ips=\"\" use_ipv6=\"false\" client_cert=\"\" mid=\"1000000005011\" sotimeout=\"60\" ua=\"\" ds=\"117.20.43.94\" ucc=\"\" md=\"false\" client_cert_pass=\"\" context=\"default\" unavail_alert=\"\" ssl_protocol=\"\" avail_alert=\"\" enabledns=\"false\" enableBouncyCastle=\"false\" cc=\"false\" a=\"https://www.example.com/tools.html\" upStatusCodes=\"\" regex_alert=\"\" probeproxy=\"false\" m=\"G\" keyword_case=\"0\" regex=\"\" rbc=\"\" t=\"30\" lc=\"English\"><PD></PD><CH hn=\"\" hd=\"_sep_\" hv=\"\"/><AI ps=\"\" un=\"\"/></Url></UC>";
    RequestBody body = RequestBody.create(MediaType.get("application/json; charset=utf-8"), json);
    Request request = new Request.Builder()
        .url(url)
        .post(body)
        .build();
    long nanoStartTime = System.nanoTime();
    okHttpClient.newCall(request).enqueue(new Callback() {
        @Override 
        public void onFailure(Call call, IOException e) {
            System.out.println("okhttp3:: Request failed"+ e);
        }

        @Override
        public void onResponse(Call call, okhttp3.Response responseObj) throws IOException {
            try (ResponseBody body = responseObj.body()) {
                long nanoEndTime = System.nanoTime();
                long nanoDiffTime = TimeUnit.NANOSECONDS.toMillis(nanoEndTime - nanoStartTime);
                System.out.println("okhttp3:: Succeded response ***"+body+"$$$");
                System.out.println("okhttp3:: Request Succeded protocol ***"+responseObj.protocol()+"$$$, time is "+nanoDiffTime);
            }
        }
    });

} catch (Exception e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

如何優化okhttpclient以使用多個tcp套接字/連接來實現http / 2多路復用以分配請求負載。

客戶端:Tomcat apache - 9.0.x,Jdk - 8,Http庫 - Okhttp3,Os - Ubuntu / Centos,安全提供程序 - Conscrypt(用於支持jdk 8中的ALPN)。

服務器:Tomcat apache - 9.0.16,Jdk -10.0.1,Os - Ubuntu / Centos,OpenSSL - 1.1.1a支持TLSv1.3

你看到的是OkHttp中連接合並的結果。 OkHttp提前不知道是否將建立HTTP / 2連接(與HTTP / 1.1相比),因此允許連接的setMaxRequestsPerHost值繼續。 目前*沒有客戶端負載平衡,因此這些可以快速合並到您所看到的單個連接。

目前,您可以通過多個客戶端實現此目的,或仔細管理完全不同的主機和連接。 nb OkHttp將在這里對你不利,並針對單個連接優於多個的典型情況進行優化,例如SSL證書指定重疊主題備用名稱


*請按照此問題獲取支持https://github.com/square/okhttp/issues/4530

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM