diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/net/NamingHttpClientManager.java b/client/src/main/java/com/alibaba/nacos/client/naming/net/NamingHttpClientManager.java new file mode 100644 index 00000000000..359ec9dff14 --- /dev/null +++ b/client/src/main/java/com/alibaba/nacos/client/naming/net/NamingHttpClientManager.java @@ -0,0 +1,61 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.alibaba.nacos.client.naming.net; + +import com.alibaba.nacos.common.http.*; +import com.alibaba.nacos.common.http.client.NacosRestTemplate; + +/** + * http Manager + * + * @author mai.jh + * @date 2020/6/14 + */ +public class NamingHttpClientManager { + + private static final int READ_TIME_OUT_MILLIS = Integer + .getInteger("com.alibaba.nacos.client.naming.rtimeout", 50000); + private static final int CON_TIME_OUT_MILLIS = Integer + .getInteger("com.alibaba.nacos.client.naming.ctimeout", 3000); + private static final boolean ENABLE_HTTPS = Boolean + .getBoolean("com.alibaba.nacos.client.naming.tls.enable"); + private static final int MAX_REDIRECTS = 5; + + private static final HttpClientFactory HTTP_CLIENT_FACTORY = new NamingHttpClientFactory(); + + public static String getPrefix() { + if (ENABLE_HTTPS) { + return "https://"; + } + return "http://"; + } + + public static NacosRestTemplate getNacosRestTemplate() { + return HttpClientBeanHolder.getNacosRestTemplate(HTTP_CLIENT_FACTORY); + } + + private static class NamingHttpClientFactory extends AbstractHttpClientFactory { + + @Override + protected HttpClientConfig buildHttpClientConfig() { + return HttpClientConfig.builder() + .setConTimeOutMillis(CON_TIME_OUT_MILLIS) + .setReadTimeOutMillis(READ_TIME_OUT_MILLIS) + .setMaxRedirects(MAX_REDIRECTS).build(); + } + } +} diff --git a/common/src/main/java/com/alibaba/nacos/common/constant/HttpHeaderConsts.java b/common/src/main/java/com/alibaba/nacos/common/constant/HttpHeaderConsts.java index baa43b84331..bc46c0d97e1 100644 --- a/common/src/main/java/com/alibaba/nacos/common/constant/HttpHeaderConsts.java +++ b/common/src/main/java/com/alibaba/nacos/common/constant/HttpHeaderConsts.java @@ -30,5 +30,8 @@ public interface HttpHeaderConsts { String ACCEPT_CHARSET = "Accept-Charset"; String ACCEPT_ENCODING = "Accept-Encoding"; String CONTENT_ENCODING = "Content-Encoding"; + String CONNECTION = "Connection"; + String REQUEST_ID = "RequestId"; + String REQUEST_MODULE = "REQUEST_MODULE"; } 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 new file mode 100644 index 00000000000..9ad2edc2815 --- /dev/null +++ b/common/src/main/java/com/alibaba/nacos/common/http/AbstractHttpClientFactory.java @@ -0,0 +1,68 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.alibaba.nacos.common.http; + +import com.alibaba.nacos.common.http.client.DefaultAsyncHttpClientRequest; +import com.alibaba.nacos.common.http.client.DefaultHttpClientRequest; +import com.alibaba.nacos.common.http.client.NacosAsyncRestTemplate; +import com.alibaba.nacos.common.http.client.NacosRestTemplate; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.impl.nio.client.HttpAsyncClients; + +/** + * AbstractHttpClientFactory + * Let the creator only specify the http client config + * + * @author mai.jh + * @date 2020/6/15 + */ +public abstract class AbstractHttpClientFactory implements HttpClientFactory { + + @Override + public final NacosRestTemplate createNacosRestTemplate() { + RequestConfig requestConfig = getRequestConfig(); + return new NacosRestTemplate( + new DefaultHttpClientRequest( + HttpClients.custom() + .setDefaultRequestConfig(requestConfig).build())); + } + + @Override + public final NacosAsyncRestTemplate createNacosAsyncRestTemplate() { + RequestConfig requestConfig = getRequestConfig(); + return new NacosAsyncRestTemplate( + new DefaultAsyncHttpClientRequest( + HttpAsyncClients.custom() + .setDefaultRequestConfig(requestConfig).build())); + } + + private RequestConfig getRequestConfig() { + HttpClientConfig httpClientConfig = buildHttpClientConfig(); + return RequestConfig.custom() + .setConnectTimeout(httpClientConfig.getConTimeOutMillis()) + .setSocketTimeout(httpClientConfig.getReadTimeOutMillis()) + .setMaxRedirects(httpClientConfig.getMaxRedirects()) + .build(); + } + + /** + * build http client config + * @return HttpClientConfig + */ + protected abstract HttpClientConfig buildHttpClientConfig(); +} diff --git a/common/src/main/java/com/alibaba/nacos/common/http/DefaultHttpClientFactory.java b/common/src/main/java/com/alibaba/nacos/common/http/DefaultHttpClientFactory.java new file mode 100644 index 00000000000..08d0aadbba9 --- /dev/null +++ b/common/src/main/java/com/alibaba/nacos/common/http/DefaultHttpClientFactory.java @@ -0,0 +1,36 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.alibaba.nacos.common.http; + +/** + * default http client factory + * + * @author mai.jh + * @date 2020/6/15 + */ +public class DefaultHttpClientFactory extends AbstractHttpClientFactory { + + private static final int TIMEOUT = Integer.getInteger("nacos.http.timeout", 5000); + + @Override + protected HttpClientConfig buildHttpClientConfig() { + return HttpClientConfig.builder() + .setConTimeOutMillis(TIMEOUT) + .setReadTimeOutMillis(TIMEOUT >> 1) + .build(); + } +} diff --git a/common/src/main/java/com/alibaba/nacos/common/http/HttpClientBeanHolder.java b/common/src/main/java/com/alibaba/nacos/common/http/HttpClientBeanHolder.java new file mode 100644 index 00000000000..059350ac00c --- /dev/null +++ b/common/src/main/java/com/alibaba/nacos/common/http/HttpClientBeanHolder.java @@ -0,0 +1,143 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.alibaba.nacos.common.http; + +import com.alibaba.nacos.common.http.client.NacosAsyncRestTemplate; +import com.alibaba.nacos.common.http.client.NacosRestTemplate; +import com.alibaba.nacos.common.utils.ExceptionUtil; +import com.alibaba.nacos.common.utils.ThreadUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * Create a rest template + * to ensure that each custom client config and rest template are in one-to-one correspondence + * + * @author mai.jh + * @date 2020/6/16 + */ +@SuppressWarnings("all") +public final class HttpClientBeanHolder { + + private static final Logger logger = LoggerFactory.getLogger(HttpClientManager.class); + + private static final int TIMEOUT = Integer.getInteger("nacos.http.timeout", 5000); + + private static HttpClientConfig HTTP_CLIENT_CONFIG = HttpClientConfig.builder() + .setConTimeOutMillis(TIMEOUT).setReadTimeOutMillis(TIMEOUT >> 1).build(); + + private static final Map singletonRest = new HashMap(10); + private static final Map singletonAsyncRest = new HashMap(10); + + private static final AtomicBoolean alreadyShutdown = new AtomicBoolean(false); + + static { + ThreadUtils.addShutdownHook(new Runnable() { + @Override + public void run() { + shutdown(); + } + }); + } + + public static NacosRestTemplate getNacosRestTemplate() { + return getNacosRestTemplate(new DefaultHttpClientFactory()); + } + + public static NacosRestTemplate getNacosRestTemplate(HttpClientFactory httpClientFactory) { + if (httpClientFactory == null) { + throw new NullPointerException("httpClientFactory is null"); + } + String factoryName = httpClientFactory.getClass().getName(); + NacosRestTemplate nacosRestTemplate = singletonRest.get(factoryName); + if (nacosRestTemplate == null) { + synchronized (singletonRest) { + nacosRestTemplate = singletonRest.get(factoryName); + if (nacosRestTemplate != null) { + return nacosRestTemplate; + } + nacosRestTemplate = httpClientFactory.createNacosRestTemplate(); + singletonRest.put(factoryName, nacosRestTemplate); + } + } + return nacosRestTemplate; + } + + public static NacosAsyncRestTemplate getNacosAsyncRestTemplate() { + return getNacosAsyncRestTemplate(new DefaultHttpClientFactory()); + } + + public static NacosAsyncRestTemplate getNacosAsyncRestTemplate(HttpClientFactory httpClientFactory) { + if (httpClientFactory == null) { + throw new NullPointerException("httpClientFactory is null"); + } + String factoryName = httpClientFactory.getClass().getName(); + NacosAsyncRestTemplate nacosAsyncRestTemplate = singletonAsyncRest.get(factoryName); + if (nacosAsyncRestTemplate == null) { + synchronized (singletonAsyncRest) { + nacosAsyncRestTemplate = singletonAsyncRest.get(factoryName); + if (nacosAsyncRestTemplate != null) { + return nacosAsyncRestTemplate; + } + nacosAsyncRestTemplate = httpClientFactory.createNacosAsyncRestTemplate(); + singletonAsyncRest.put(factoryName, nacosAsyncRestTemplate); + } + } + return nacosAsyncRestTemplate; + } + + public static void shutdown() { + if (!alreadyShutdown.compareAndSet(false, true)) { + return; + } + logger.warn("[HttpClientBeanFactory] Start destroying NacosRestTemplate"); + try { + nacostRestTemplateShutdown(); + nacosAsyncRestTemplateShutdown(); + } + catch (Exception ex) { + logger.error("[HttpClientBeanFactory] An exception occurred when the HTTP client was closed : {}", + ExceptionUtil.getStackTrace(ex)); + } + logger.warn("[HttpClientBeanFactory] Destruction of the end"); + } + + private static void nacostRestTemplateShutdown() throws Exception{ + if (!singletonRest.isEmpty()) { + Collection nacosRestTemplates = singletonRest.values(); + for (NacosRestTemplate nacosRestTemplate : nacosRestTemplates) { + nacosRestTemplate.close(); + } + singletonRest.clear(); + } + } + + private static void nacosAsyncRestTemplateShutdown() throws Exception{ + if (!singletonAsyncRest.isEmpty()) { + Collection nacosAsyncRestTemplates = singletonAsyncRest.values(); + for (NacosAsyncRestTemplate nacosAsyncRestTemplate : nacosAsyncRestTemplates) { + nacosAsyncRestTemplate.close(); + } + singletonAsyncRest.clear(); + } + } +} diff --git a/common/src/main/java/com/alibaba/nacos/common/http/HttpClientConfig.java b/common/src/main/java/com/alibaba/nacos/common/http/HttpClientConfig.java new file mode 100644 index 00000000000..83e6af6c12e --- /dev/null +++ b/common/src/main/java/com/alibaba/nacos/common/http/HttpClientConfig.java @@ -0,0 +1,79 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.alibaba.nacos.common.http; + +/** + * http client config build + * + * @author mai.jh + * @date 2020/6/14 + */ +public class HttpClientConfig { + + private int conTimeOutMillis; + + private int readTimeOutMillis; + + private int maxRedirects; + + public HttpClientConfig(int conTimeOutMillis, int readTimeOutMillis, int maxRedirects) { + this.conTimeOutMillis = conTimeOutMillis; + this.readTimeOutMillis = readTimeOutMillis; + this.maxRedirects = maxRedirects; + } + + public int getConTimeOutMillis() { + return conTimeOutMillis; + } + + public int getReadTimeOutMillis() { + return readTimeOutMillis; + } + + public int getMaxRedirects() { + return maxRedirects; + } + + public static HttpClientConfigBuilder builder() { + return new HttpClientConfigBuilder(); + } + + public static final class HttpClientConfigBuilder { + private int conTimeOutMillis = -1; + private int readTimeOutMillis = -1; + private int maxRedirects = 50; + + public HttpClientConfigBuilder setConTimeOutMillis(int conTimeOutMillis) { + this.conTimeOutMillis = conTimeOutMillis; + return this; + } + + public HttpClientConfigBuilder setReadTimeOutMillis(int readTimeOutMillis) { + this.readTimeOutMillis = readTimeOutMillis; + return this; + } + + public HttpClientConfigBuilder setMaxRedirects(int maxRedirects) { + this.maxRedirects = maxRedirects; + return this; + } + + public HttpClientConfig build() { + return new HttpClientConfig(conTimeOutMillis, readTimeOutMillis, maxRedirects); + } + } +} diff --git a/common/src/main/java/com/alibaba/nacos/common/http/HttpClientFactory.java b/common/src/main/java/com/alibaba/nacos/common/http/HttpClientFactory.java new file mode 100644 index 00000000000..6c0d2f53ff8 --- /dev/null +++ b/common/src/main/java/com/alibaba/nacos/common/http/HttpClientFactory.java @@ -0,0 +1,42 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.alibaba.nacos.common.http; + +import com.alibaba.nacos.common.http.client.NacosAsyncRestTemplate; +import com.alibaba.nacos.common.http.client.NacosRestTemplate; + +/** + * http Client Factory + * + * @author mai.jh + * @date 2020/6/15 + */ +public interface HttpClientFactory { + + /** + * create new nacost rest + * @return NacosRestTemplate + */ + NacosRestTemplate createNacosRestTemplate(); + + /** + * create new nacos async rest + * @return NacosAsyncRestTemplate + */ + NacosAsyncRestTemplate createNacosAsyncRestTemplate(); + +} diff --git a/common/src/main/java/com/alibaba/nacos/common/http/HttpClientManager.java b/common/src/main/java/com/alibaba/nacos/common/http/HttpClientManager.java index c43b75c4c5c..a1af07faf77 100644 --- a/common/src/main/java/com/alibaba/nacos/common/http/HttpClientManager.java +++ b/common/src/main/java/com/alibaba/nacos/common/http/HttpClientManager.java @@ -16,10 +16,6 @@ package com.alibaba.nacos.common.http; -import com.alibaba.nacos.common.http.client.DefaultAsyncHttpClientRequest; -import com.alibaba.nacos.common.http.client.DefaultHttpClientRequest; -import com.alibaba.nacos.common.http.client.NacosAsyncRestTemplate; -import com.alibaba.nacos.common.http.client.NacosRestTemplate; import com.alibaba.nacos.common.utils.ExceptionUtil; import com.alibaba.nacos.common.utils.ThreadUtils; import org.apache.http.client.config.RequestConfig; @@ -51,15 +47,8 @@ public class HttpClientManager { private static final NAsyncHttpClient ASYNC_HTTP_CLIENT = new NacosAsyncHttpClient( HttpAsyncClients.custom().setDefaultRequestConfig(DEFAULT_CONFIG).build()); - private static final NacosRestTemplate NACOS_REST_TEMPLATE = new NacosRestTemplate( - new DefaultHttpClientRequest(HttpClients.custom().setDefaultRequestConfig(DEFAULT_CONFIG).build())); - - private static final NacosAsyncRestTemplate NACOS_ASYNC_REST_TEMPLATE = new NacosAsyncRestTemplate( - new DefaultAsyncHttpClientRequest(HttpAsyncClients.custom().setDefaultRequestConfig(DEFAULT_CONFIG).build())); - private static final AtomicBoolean alreadyShutdown = new AtomicBoolean(false); - static { ThreadUtils.addShutdownHook(new Runnable() { @Override @@ -78,14 +67,6 @@ public static NAsyncHttpClient getAsyncHttpClient() { return ASYNC_HTTP_CLIENT; } - public static NacosRestTemplate getNacosRestTemplate() { - return NACOS_REST_TEMPLATE; - } - - public static NacosAsyncRestTemplate getNacosAsyncRestTemplate() { - return NACOS_ASYNC_REST_TEMPLATE; - } - public static void shutdown() { if (!alreadyShutdown.compareAndSet(false, true)) { return; @@ -94,8 +75,6 @@ public static void shutdown() { try { SYNC_HTTP_CLIENT.close(); ASYNC_HTTP_CLIENT.close(); - NACOS_REST_TEMPLATE.close(); - NACOS_ASYNC_REST_TEMPLATE.close(); } catch (Exception ex) { logger.error("An exception occurred when the HTTP client was closed : {}", diff --git a/common/src/main/java/com/alibaba/nacos/common/http/handler/ResponseHandler.java b/common/src/main/java/com/alibaba/nacos/common/http/handler/ResponseHandler.java index 6bd7d1ed1b0..f133487c6bd 100644 --- a/common/src/main/java/com/alibaba/nacos/common/http/handler/ResponseHandler.java +++ b/common/src/main/java/com/alibaba/nacos/common/http/handler/ResponseHandler.java @@ -58,7 +58,7 @@ public static HttpRestResult responseEntityExtractor(HttpClientResponse r String contentType = headers.getValue(HttpHeaderConsts.CONTENT_TYPE); InputStream body = response.getBody(); T extractBody = null; - if (MediaType.APPLICATION_JSON.equals(contentType) && HttpStatus.SC_OK == response.getStatusCode()) { + if (contentType != null && contentType.startsWith(MediaType.APPLICATION_JSON) && HttpStatus.SC_OK == response.getStatusCode()) { extractBody = convert(body, type); } if (extractBody == null) { diff --git a/test/src/test/java/com/alibaba/nacos/test/common/NacosAsyncRestTemplate_ITCase.java b/test/src/test/java/com/alibaba/nacos/test/common/NacosAsyncRestTemplate_ITCase.java index bd318b92353..951e7531f5b 100644 --- a/test/src/test/java/com/alibaba/nacos/test/common/NacosAsyncRestTemplate_ITCase.java +++ b/test/src/test/java/com/alibaba/nacos/test/common/NacosAsyncRestTemplate_ITCase.java @@ -18,7 +18,7 @@ import com.alibaba.nacos.Nacos; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.common.http.Callback; -import com.alibaba.nacos.common.http.HttpClientManager; +import com.alibaba.nacos.common.http.HttpClientBeanHolder; import com.alibaba.nacos.common.http.HttpRestResult; import com.alibaba.nacos.common.http.client.NacosAsyncRestTemplate; import com.alibaba.nacos.common.http.param.Header; @@ -53,7 +53,7 @@ public class NacosAsyncRestTemplate_ITCase { @LocalServerPort private int port; - private NacosAsyncRestTemplate nacosRestTemplate = HttpClientManager.getNacosAsyncRestTemplate(); + private NacosAsyncRestTemplate nacosRestTemplate = HttpClientBeanHolder.getNacosAsyncRestTemplate(); private final String CONFIG_INSTANCE_PATH = "/nacos/v1/ns"; private String IP = null; diff --git a/test/src/test/java/com/alibaba/nacos/test/common/NacosRestTemplate_ITCase.java b/test/src/test/java/com/alibaba/nacos/test/common/NacosRestTemplate_ITCase.java index 1e2a8c0a806..d33fe1f61cb 100644 --- a/test/src/test/java/com/alibaba/nacos/test/common/NacosRestTemplate_ITCase.java +++ b/test/src/test/java/com/alibaba/nacos/test/common/NacosRestTemplate_ITCase.java @@ -17,7 +17,7 @@ import com.alibaba.nacos.Nacos; import com.alibaba.nacos.api.exception.NacosException; -import com.alibaba.nacos.common.http.HttpClientManager; +import com.alibaba.nacos.common.http.HttpClientBeanHolder; import com.alibaba.nacos.common.http.HttpRestResult; import com.alibaba.nacos.common.http.client.NacosRestTemplate; import com.alibaba.nacos.common.http.param.Header; @@ -51,7 +51,7 @@ public class NacosRestTemplate_ITCase { @LocalServerPort private int port; - private NacosRestTemplate nacosRestTemplate = HttpClientManager.getNacosRestTemplate(); + private NacosRestTemplate nacosRestTemplate = HttpClientBeanHolder.getNacosRestTemplate(); private final String INSTANCE_PATH = "/nacos/v1/ns"; private final String CONFIG_PATH = "/nacos/v1/cs"; @@ -62,6 +62,19 @@ public void init() throws NacosException { IP = String.format("http://localhost:%d", port); } + @Test + public void test_url_post_config() throws Exception { + String url = IP + CONFIG_PATH + "/configs"; + Map param = new HashMap<>(); + param.put("dataId", "test-1"); + param.put("group", "DEFAULT_GROUP"); + param.put("content", "aaa=b"); + HttpRestResult restResult = nacosRestTemplate.postFrom(url, Header.newInstance(), Query.EMPTY, param, String.class); + Assert.assertTrue(restResult.ok()); + System.out.println(restResult.getData()); + System.out.println(restResult.getHeader()); + } + @Test public void test_url_get_return_restResult() throws Exception{ String url = IP + CONFIG_PATH + "/configs";