From aa2f3c90f60d72bae65498cdbd694946d1a9adbf Mon Sep 17 00:00:00 2001 From: zrlw Date: Mon, 13 Dec 2021 20:02:33 +0800 Subject: [PATCH] apply pull #6441 to avoid IO reactor stopped --- .../http/AbstractHttpClientFactory.java | 99 ++++++++++++++++--- 1 file changed, 86 insertions(+), 13 deletions(-) diff --git a/common/src/main/java/com/alibaba/nacos/common/http/AbstractHttpClientFactory.java b/common/src/main/java/com/alibaba/nacos/common/http/AbstractHttpClientFactory.java index 368c602a5ec..18c732ad4f3 100644 --- a/common/src/main/java/com/alibaba/nacos/common/http/AbstractHttpClientFactory.java +++ b/common/src/main/java/com/alibaba/nacos/common/http/AbstractHttpClientFactory.java @@ -16,6 +16,33 @@ package com.alibaba.nacos.common.http; +import java.io.IOException; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; + +import org.apache.http.client.config.RequestConfig; +import org.apache.http.config.Registry; +import org.apache.http.config.RegistryBuilder; +import org.apache.http.conn.ssl.DefaultHostnameVerifier; +import org.apache.http.impl.nio.client.HttpAsyncClientBuilder; +import org.apache.http.impl.nio.client.HttpAsyncClients; +import org.apache.http.impl.nio.conn.PoolingNHttpClientConnectionManager; +import org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor; +import org.apache.http.impl.nio.reactor.IOReactorConfig; +import org.apache.http.nio.conn.NHttpClientConnectionManager; +import org.apache.http.nio.conn.NoopIOSessionStrategy; +import org.apache.http.nio.conn.SchemeIOSessionStrategy; +import org.apache.http.nio.conn.ssl.SSLIOSessionStrategy; +import org.apache.http.nio.reactor.IOReactorException; +import org.apache.http.nio.reactor.IOReactorExceptionHandler; +import org.apache.http.protocol.RequestContent; +import org.apache.http.ssl.SSLContexts; +import org.slf4j.Logger; + import com.alibaba.nacos.common.http.client.NacosAsyncRestTemplate; import com.alibaba.nacos.common.http.client.NacosRestTemplate; import com.alibaba.nacos.common.http.client.request.DefaultAsyncHttpClientRequest; @@ -25,18 +52,6 @@ import com.alibaba.nacos.common.tls.TlsHelper; import com.alibaba.nacos.common.tls.TlsSystemConfig; import com.alibaba.nacos.common.utils.BiConsumer; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.impl.nio.client.HttpAsyncClients; -import org.apache.http.protocol.RequestContent; -import org.apache.http.impl.nio.reactor.IOReactorConfig; -import org.slf4j.Logger; - -import javax.net.ssl.HostnameVerifier; -import javax.net.ssl.HttpsURLConnection; -import javax.net.ssl.SSLContext; -import java.io.IOException; -import java.security.KeyManagementException; -import java.security.NoSuchAlgorithmException; /** * AbstractHttpClientFactory Let the creator only specify the http client config. @@ -77,7 +92,65 @@ public NacosAsyncRestTemplate createNacosAsyncRestTemplate() { .setDefaultRequestConfig(getRequestConfig()) .setMaxConnTotal(originalRequestConfig.getMaxConnTotal()) .setMaxConnPerRoute(originalRequestConfig.getMaxConnPerRoute()) - .setUserAgent(originalRequestConfig.getUserAgent()).build())); + .setUserAgent(originalRequestConfig.getUserAgent()) + .setConnectionManager(getConnectionManager(originalRequestConfig)) + .build())); + } + + /** + * create the {@link NHttpClientConnectionManager}, the code mainly from {@link HttpAsyncClientBuilder#build()}. + * we add the {@link IOReactorExceptionHandler} to handle the {@link IOException} and {@link RuntimeException} + * thrown by the {@link org.apache.http.impl.nio.reactor.BaseIOReactor} when process the event of Network. + * Using this way to avoid the {@link DefaultConnectingIOReactor} killed by unknown error of network. + * + * @param originalRequestConfig request config. + * @return {@link NHttpClientConnectionManager}. + */ + private NHttpClientConnectionManager getConnectionManager(HttpClientConfig originalRequestConfig) { + SSLContext sslcontext = SSLContexts.createDefault(); + HostnameVerifier hostnameVerifier = new DefaultHostnameVerifier(); + SchemeIOSessionStrategy sslStrategy = new SSLIOSessionStrategy(sslcontext, null, null, hostnameVerifier); + + final DefaultConnectingIOReactor ioreactor; + try { + ioreactor = new DefaultConnectingIOReactor(getIoReactorConfig()); + } catch (IOReactorException e) { + assignLogger().error("[NHttpClientConnectionManager] Create DefaultConnectingIOReactor failed", e); + throw new IllegalStateException(); + } + + // if the handle return true, then the exception thrown by IOReactor will be ignore, and will not finish the IOReactor. + ioreactor.setExceptionHandler(new IOReactorExceptionHandler() { + + @Override + public boolean handle(IOException ex) { + assignLogger().warn("[NHttpClientConnectionManager] handle IOException, ignore it.", ex); + return true; + } + + @Override + public boolean handle(RuntimeException ex) { + assignLogger().warn("[NHttpClientConnectionManager] handle RuntimeException, ignore it.", ex); + return true; + } + }); + + Registry registry = RegistryBuilder.create() + .register("http", NoopIOSessionStrategy.INSTANCE) + .register("https", sslStrategy) + .build(); + final PoolingNHttpClientConnectionManager poolingmgr = new PoolingNHttpClientConnectionManager(ioreactor, registry); + + int maxTotal = originalRequestConfig.getMaxConnTotal(); + if (maxTotal > 0) { + poolingmgr.setMaxTotal(maxTotal); + } + + int maxPerRoute = originalRequestConfig.getMaxConnPerRoute(); + if (maxPerRoute > 0) { + poolingmgr.setDefaultMaxPerRoute(maxPerRoute); + } + return poolingmgr; } protected IOReactorConfig getIoReactorConfig() {