Okhttp3, http2 мультиплексирование POST запрашивает большое время отклика при пиковой загрузке - PullRequest
0 голосов
/ 24 февраля 2019

Мое приложение будет отправлять около 1000 запросов POST / минуту на мой сервер tomcat (который поддерживает http / 2), который будет опрашивать заданный URL-адрес и возвращать html и время ответа. Я хочу добиться истинного мультиплексирования http / 2 дляповторно использовать TCP-соединение между моим приложением и сервером Tomcat.мой клиент использует ohttp, и я могу успешно установить соединение и использовать его в течение длительного времени, но когда количество запросов увеличивается (что-то вроде: в очереди: 50, работает: 50), время отклика также увеличивается (оно достигает 2000 мс-15000 мс или даже хуже, что обычно занимает 300-500 мс).Я могу понять, что это происходит из-за перегрузки соединения 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();

Для включения ALPN мы используем Cristryt.в самом jdk8,

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 (для поддержки ALPN в jdk 8).

Сервер: Tomcat Apache - 9.0.16, Jdk -10.0.1, Os - Ubuntu / Centos, OpenSSL - 1.1.1a для поддержки TLSv1.3

1 Ответ

0 голосов
/ 24 февраля 2019

То, что вы видите, является результатом объединения соединений в OkHttp.OkHttp не знает заранее, будут ли установлены HTTP / 2-соединения (против HTTP / 1.1), поэтому позволяет установить значение соединений setMaxRequestsPerHost.В настоящее время * балансировка нагрузки на стороне клиента отсутствует, поэтому они быстро объединяются в одно соединение, которое вы видите.

В настоящее время вы можете добиться этого с помощью нескольких клиентов или тщательного управления.совершенно разных хостов и соединений.nb OkHttp будет работать против вас здесь и оптимизировать для типичного случая, когда одно соединение лучше, чем несколько, например, SSL-сертификат определяет перекрывающиеся альтернативные имена субъекта


*поддержка https://github.com/square/okhttp/issues/4530

...