diff --git a/dubbo-configcenter/dubbo-configcenter-nacos/src/main/java/org/apache/dubbo/configcenter/support/nacos/NacosDynamicConfiguration.java b/dubbo-configcenter/dubbo-configcenter-nacos/src/main/java/org/apache/dubbo/configcenter/support/nacos/NacosDynamicConfiguration.java index 2f43c85f27..beb2400860 100644 --- a/dubbo-configcenter/dubbo-configcenter-nacos/src/main/java/org/apache/dubbo/configcenter/support/nacos/NacosDynamicConfiguration.java +++ b/dubbo-configcenter/dubbo-configcenter-nacos/src/main/java/org/apache/dubbo/configcenter/support/nacos/NacosDynamicConfiguration.java @@ -17,12 +17,20 @@ package org.apache.dubbo.configcenter.support.nacos; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArraySet; +import java.util.concurrent.Executor; + import org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.configcenter.ConfigChangeType; import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent; import org.apache.dubbo.common.config.configcenter.ConfigItem; import org.apache.dubbo.common.config.configcenter.ConfigurationListener; import org.apache.dubbo.common.config.configcenter.DynamicConfiguration; +import org.apache.dubbo.common.constants.LoggerCodeConstants; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.MD5Utils; @@ -34,17 +42,12 @@ import com.alibaba.nacos.api.config.ConfigService; import com.alibaba.nacos.api.config.listener.AbstractSharedListener; import com.alibaba.nacos.api.exception.NacosException; -import java.util.Map; -import java.util.Properties; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CopyOnWriteArraySet; -import java.util.concurrent.Executor; - import static com.alibaba.nacos.api.PropertyKeyConst.PASSWORD; import static com.alibaba.nacos.api.PropertyKeyConst.SERVER_ADDR; import static com.alibaba.nacos.api.PropertyKeyConst.USERNAME; +import static com.alibaba.nacos.client.constant.Constants.HealthCheck.UP; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_ERROR_NACOS; +import static org.apache.dubbo.common.constants.LoggerCodeConstants.INTERNAL_INTERRUPTED; import static org.apache.dubbo.common.constants.RemotingConstants.BACKUP_KEY; import static org.apache.dubbo.common.utils.StringConstantFieldValuePredicate.of; import static org.apache.dubbo.common.utils.StringUtils.HYPHEN_CHAR; @@ -64,6 +67,12 @@ public class NacosDynamicConfiguration implements DynamicConfiguration { private Properties nacosProperties; + private static final String NACOS_RETRY_KEY = "nacos.retry"; + + private static final String NACOS_RETRY_WAIT_KEY = "nacos.retry-wait"; + + private static final String NACOS_CHECK_KEY = "nacos.check"; + /** * The nacos configService */ @@ -83,16 +92,40 @@ public class NacosDynamicConfiguration implements DynamicConfiguration { } private NacosConfigServiceWrapper buildConfigService(URL url) { - ConfigService configService = null; + int retryTimes = url.getPositiveParameter(NACOS_RETRY_KEY, 10); + int sleepMsBetweenRetries = url.getPositiveParameter(NACOS_RETRY_WAIT_KEY, 1000); + boolean check = url.getParameter(NACOS_CHECK_KEY, true); + ConfigService tmpConfigServices = null; try { - configService = NacosFactory.createConfigService(nacosProperties); - } catch (NacosException e) { - if (logger.isErrorEnabled()) { - logger.error(CONFIG_ERROR_NACOS, "", "", e.getMessage(), e); + for (int i = 0; i < retryTimes + 1; i++) { + tmpConfigServices = NacosFactory.createConfigService(nacosProperties); + if (!check || UP.equals(tmpConfigServices.getServerStatus())) { + break; + } else { + logger.warn(LoggerCodeConstants.CONFIG_ERROR_NACOS, "", "", + "Failed to connect to nacos config server. " + + (i < retryTimes ? "Dubbo will try to retry in " + sleepMsBetweenRetries + ". " : "Exceed retry max times.") + + "Try times: " + (i + 1)); + } + tmpConfigServices.shutDown(); + tmpConfigServices = null; + Thread.sleep(sleepMsBetweenRetries); } + } catch (NacosException e) { + logger.error(CONFIG_ERROR_NACOS, "", "", e.getErrMsg(), e); + throw new IllegalStateException(e); + } catch (InterruptedException e) { + logger.error(INTERNAL_INTERRUPTED, "", "", "Interrupted when creating nacos config service client.", e); + Thread.currentThread().interrupt(); throw new IllegalStateException(e); } - return new NacosConfigServiceWrapper(configService); + + if (tmpConfigServices == null) { + logger.error(CONFIG_ERROR_NACOS, "", "", "Failed to create nacos config service client. Reason: server status check failed."); + throw new IllegalStateException("Failed to create nacos config service client. Reason: server status check failed."); + } + + return new NacosConfigServiceWrapper(tmpConfigServices); } private Properties buildNacosProperties(URL url) { diff --git a/dubbo-configcenter/dubbo-configcenter-nacos/src/test/java/org/apache/dubbo/configcenter/support/nacos/MockConfigService.java b/dubbo-configcenter/dubbo-configcenter-nacos/src/test/java/org/apache/dubbo/configcenter/support/nacos/MockConfigService.java new file mode 100644 index 0000000000..bba7741e64 --- /dev/null +++ b/dubbo-configcenter/dubbo-configcenter-nacos/src/test/java/org/apache/dubbo/configcenter/support/nacos/MockConfigService.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.dubbo.configcenter.support.nacos; + +import com.alibaba.nacos.api.config.ConfigService; +import com.alibaba.nacos.api.config.listener.Listener; +import com.alibaba.nacos.api.exception.NacosException; + +public class MockConfigService implements ConfigService { + @Override + public String getConfig(String dataId, String group, long timeoutMs) throws NacosException { + return null; + } + + @Override + public String getConfigAndSignListener(String dataId, String group, long timeoutMs, Listener listener) throws NacosException { + return null; + } + + @Override + public void addListener(String dataId, String group, Listener listener) throws NacosException { + + } + + @Override + public boolean publishConfig(String dataId, String group, String content) throws NacosException { + return false; + } + + @Override + public boolean publishConfig(String dataId, String group, String content, String type) throws NacosException { + return false; + } + + @Override + public boolean publishConfigCas(String dataId, String group, String content, String casMd5) throws NacosException { + return false; + } + + @Override + public boolean publishConfigCas(String dataId, String group, String content, String casMd5, String type) throws NacosException { + return false; + } + + @Override + public boolean removeConfig(String dataId, String group) throws NacosException { + return false; + } + + @Override + public void removeListener(String dataId, String group, Listener listener) { + + } + + @Override + public String getServerStatus() { + return null; + } + + @Override + public void shutDown() throws NacosException { + + } +} diff --git a/dubbo-configcenter/dubbo-configcenter-nacos/src/test/java/org/apache/dubbo/configcenter/support/nacos/RetryTest.java b/dubbo-configcenter/dubbo-configcenter-nacos/src/test/java/org/apache/dubbo/configcenter/support/nacos/RetryTest.java new file mode 100644 index 0000000000..e90a5cb016 --- /dev/null +++ b/dubbo-configcenter/dubbo-configcenter-nacos/src/test/java/org/apache/dubbo/configcenter/support/nacos/RetryTest.java @@ -0,0 +1,86 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.dubbo.configcenter.support.nacos; + +import java.util.Properties; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.dubbo.common.URL; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import org.mockito.Mockito; + +import com.alibaba.nacos.api.NacosFactory; +import com.alibaba.nacos.api.config.ConfigService; + +import static com.alibaba.nacos.client.constant.Constants.HealthCheck.DOWN; +import static com.alibaba.nacos.client.constant.Constants.HealthCheck.UP; +import static org.mockito.ArgumentMatchers.any; + +class RetryTest { + + @Test + void testRetryCreate() { + try (MockedStatic nacosFactoryMockedStatic = Mockito.mockStatic(NacosFactory.class)) { + AtomicInteger atomicInteger = new AtomicInteger(0); + ConfigService mock = new MockConfigService() { + @Override + public String getServerStatus() { + return atomicInteger.incrementAndGet() > 10 ? UP : DOWN; + } + }; + nacosFactoryMockedStatic.when(() -> NacosFactory.createConfigService((Properties) any())).thenReturn(mock); + + + URL url = URL.valueOf("nacos://127.0.0.1:8848") + .addParameter("nacos.retry", 5) + .addParameter("nacos.retry-wait", 10); + Assertions.assertThrows(IllegalStateException.class, () -> new NacosDynamicConfiguration(url)); + + try { + new NacosDynamicConfiguration(url); + } catch (Throwable t) { + Assertions.fail(t); + } + } + } + + @Test + void testDisable() { + try (MockedStatic nacosFactoryMockedStatic = Mockito.mockStatic(NacosFactory.class)) { + ConfigService mock = new MockConfigService() { + @Override + public String getServerStatus() { + return DOWN; + } + }; + nacosFactoryMockedStatic.when(() -> NacosFactory.createConfigService((Properties) any())).thenReturn(mock); + + + URL url = URL.valueOf("nacos://127.0.0.1:8848") + .addParameter("nacos.retry", 5) + .addParameter("nacos.retry-wait", 10) + .addParameter("nacos.check", "false"); + try { + new NacosDynamicConfiguration(url); + } catch (Throwable t) { + Assertions.fail(t); + } + } + } +} diff --git a/dubbo-metadata/dubbo-metadata-report-nacos/src/main/java/org/apache/dubbo/metadata/store/nacos/NacosMetadataReport.java b/dubbo-metadata/dubbo-metadata-report-nacos/src/main/java/org/apache/dubbo/metadata/store/nacos/NacosMetadataReport.java index 88f65ba642..9d2a93c24a 100644 --- a/dubbo-metadata/dubbo-metadata-report-nacos/src/main/java/org/apache/dubbo/metadata/store/nacos/NacosMetadataReport.java +++ b/dubbo-metadata/dubbo-metadata-report-nacos/src/main/java/org/apache/dubbo/metadata/store/nacos/NacosMetadataReport.java @@ -17,11 +17,24 @@ package org.apache.dubbo.metadata.store.nacos; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArraySet; +import java.util.concurrent.Executor; + import org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.configcenter.ConfigChangeType; import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent; import org.apache.dubbo.common.config.configcenter.ConfigItem; import org.apache.dubbo.common.config.configcenter.ConfigurationListener; +import org.apache.dubbo.common.constants.LoggerCodeConstants; import org.apache.dubbo.common.utils.JsonUtils; import org.apache.dubbo.common.utils.MD5Utils; import org.apache.dubbo.common.utils.StringUtils; @@ -38,23 +51,15 @@ import org.apache.dubbo.metadata.report.support.AbstractMetadataReport; import com.alibaba.nacos.api.NacosFactory; import com.alibaba.nacos.api.PropertyKeyConst; +import com.alibaba.nacos.api.config.ConfigService; import com.alibaba.nacos.api.config.listener.AbstractSharedListener; import com.alibaba.nacos.api.exception.NacosException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CopyOnWriteArraySet; -import java.util.concurrent.Executor; - import static com.alibaba.nacos.api.PropertyKeyConst.SERVER_ADDR; +import static com.alibaba.nacos.client.constant.Constants.HealthCheck.UP; import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; +import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_ERROR_NACOS; +import static org.apache.dubbo.common.constants.LoggerCodeConstants.INTERNAL_INTERRUPTED; import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_NACOS_EXCEPTION; import static org.apache.dubbo.common.constants.RemotingConstants.BACKUP_KEY; import static org.apache.dubbo.common.utils.StringConstantFieldValuePredicate.of; @@ -81,25 +86,57 @@ public class NacosMetadataReport extends AbstractMetadataReport { private MD5Utils md5Utils = new MD5Utils(); + private static final String NACOS_RETRY_KEY = "nacos.retry"; + + private static final String NACOS_RETRY_WAIT_KEY = "nacos.retry-wait"; + + private static final String NACOS_CHECK_KEY = "nacos.check"; + public NacosMetadataReport(URL url) { super(url); this.configService = buildConfigService(url); group = url.getParameter(GROUP_KEY, DEFAULT_ROOT); } - public NacosConfigServiceWrapper buildConfigService(URL url) { + private NacosConfigServiceWrapper buildConfigService(URL url) { Properties nacosProperties = buildNacosProperties(url); + int retryTimes = url.getPositiveParameter(NACOS_RETRY_KEY, 10); + int sleepMsBetweenRetries = url.getPositiveParameter(NACOS_RETRY_WAIT_KEY, 1000); + boolean check = url.getParameter(NACOS_CHECK_KEY, true); + ConfigService tmpConfigServices = null; try { - configService = new NacosConfigServiceWrapper(NacosFactory.createConfigService(nacosProperties)); - } catch (NacosException e) { - if (logger.isErrorEnabled()) { - logger.error(REGISTRY_NACOS_EXCEPTION, "", "", e.getErrMsg(), e); + for (int i = 0; i < retryTimes + 1; i++) { + tmpConfigServices = NacosFactory.createConfigService(nacosProperties); + if (!check || UP.equals(tmpConfigServices.getServerStatus())) { + break; + } else { + logger.warn(LoggerCodeConstants.CONFIG_ERROR_NACOS, "", "", + "Failed to connect to nacos config server. " + + (i < retryTimes ? "Dubbo will try to retry in " + sleepMsBetweenRetries + ". " : "Exceed retry max times.") + + "Try times: " + (i + 1)); + } + tmpConfigServices.shutDown(); + tmpConfigServices = null; + Thread.sleep(sleepMsBetweenRetries); } + } catch (NacosException e) { + logger.error(CONFIG_ERROR_NACOS, "", "", e.getErrMsg(), e); + throw new IllegalStateException(e); + } catch (InterruptedException e) { + logger.error(INTERNAL_INTERRUPTED, "", "", "Interrupted when creating nacos config service client.", e); + Thread.currentThread().interrupt(); throw new IllegalStateException(e); } - return configService; + + if (tmpConfigServices == null) { + logger.error(CONFIG_ERROR_NACOS, "", "", "Failed to create nacos config service client. Reason: server status check failed."); + throw new IllegalStateException("Failed to create nacos config service client. Reason: server status check failed."); + } + + return new NacosConfigServiceWrapper(tmpConfigServices); } + private Properties buildNacosProperties(URL url) { Properties properties = new Properties(); setServerAddr(url, properties); diff --git a/dubbo-metadata/dubbo-metadata-report-nacos/src/test/java/org/apache/dubbo/metadata/store/nacos/MockConfigService.java b/dubbo-metadata/dubbo-metadata-report-nacos/src/test/java/org/apache/dubbo/metadata/store/nacos/MockConfigService.java new file mode 100644 index 0000000000..86b5b879f7 --- /dev/null +++ b/dubbo-metadata/dubbo-metadata-report-nacos/src/test/java/org/apache/dubbo/metadata/store/nacos/MockConfigService.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.dubbo.metadata.store.nacos; + +import com.alibaba.nacos.api.config.ConfigService; +import com.alibaba.nacos.api.config.listener.Listener; +import com.alibaba.nacos.api.exception.NacosException; + +public class MockConfigService implements ConfigService { + @Override + public String getConfig(String dataId, String group, long timeoutMs) throws NacosException { + return null; + } + + @Override + public String getConfigAndSignListener(String dataId, String group, long timeoutMs, Listener listener) throws NacosException { + return null; + } + + @Override + public void addListener(String dataId, String group, Listener listener) throws NacosException { + + } + + @Override + public boolean publishConfig(String dataId, String group, String content) throws NacosException { + return false; + } + + @Override + public boolean publishConfig(String dataId, String group, String content, String type) throws NacosException { + return false; + } + + @Override + public boolean publishConfigCas(String dataId, String group, String content, String casMd5) throws NacosException { + return false; + } + + @Override + public boolean publishConfigCas(String dataId, String group, String content, String casMd5, String type) throws NacosException { + return false; + } + + @Override + public boolean removeConfig(String dataId, String group) throws NacosException { + return false; + } + + @Override + public void removeListener(String dataId, String group, Listener listener) { + + } + + @Override + public String getServerStatus() { + return null; + } + + @Override + public void shutDown() throws NacosException { + + } +} diff --git a/dubbo-metadata/dubbo-metadata-report-nacos/src/test/java/org/apache/dubbo/metadata/store/nacos/RetryTest.java b/dubbo-metadata/dubbo-metadata-report-nacos/src/test/java/org/apache/dubbo/metadata/store/nacos/RetryTest.java new file mode 100644 index 0000000000..9159e076fc --- /dev/null +++ b/dubbo-metadata/dubbo-metadata-report-nacos/src/test/java/org/apache/dubbo/metadata/store/nacos/RetryTest.java @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.dubbo.metadata.store.nacos; + +import java.util.Properties; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.dubbo.common.URL; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import org.mockito.Mockito; + +import com.alibaba.nacos.api.NacosFactory; +import com.alibaba.nacos.api.config.ConfigService; + +import static com.alibaba.nacos.client.constant.Constants.HealthCheck.DOWN; +import static com.alibaba.nacos.client.constant.Constants.HealthCheck.UP; +import static org.mockito.ArgumentMatchers.any; + +class RetryTest { + + @Test + void testRetryCreate() { + try (MockedStatic nacosFactoryMockedStatic = Mockito.mockStatic(NacosFactory.class)) { + AtomicInteger atomicInteger = new AtomicInteger(0); + ConfigService mock = new MockConfigService() { + @Override + public String getServerStatus() { + return atomicInteger.incrementAndGet() > 10 ? UP : DOWN; + } + }; + nacosFactoryMockedStatic.when(() -> NacosFactory.createConfigService((Properties) any())).thenReturn(mock); + + + URL url = URL.valueOf("nacos://127.0.0.1:8848") + .addParameter("nacos.retry", 5) + .addParameter("nacos.retry-wait", 10); + Assertions.assertThrows(IllegalStateException.class, () -> new NacosMetadataReport(url)); + + try { + new NacosMetadataReport(url); + } catch (Throwable t) { + Assertions.fail(t); + } + } + } + @Test + void testDisable() { + try (MockedStatic nacosFactoryMockedStatic = Mockito.mockStatic(NacosFactory.class)) { + ConfigService mock = new MockConfigService() { + @Override + public String getServerStatus() { + return DOWN; + } + }; + nacosFactoryMockedStatic.when(() -> NacosFactory.createConfigService((Properties) any())).thenReturn(mock); + + + URL url = URL.valueOf("nacos://127.0.0.1:8848") + .addParameter("nacos.retry", 5) + .addParameter("nacos.retry-wait", 10) + .addParameter("nacos.check", "false"); + try { + new NacosMetadataReport(url); + } catch (Throwable t) { + Assertions.fail(t); + } + } + } +} diff --git a/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosNamingServiceWrapper.java b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosNamingServiceWrapper.java index e1185aa15b..6bc12e17b6 100644 --- a/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosNamingServiceWrapper.java +++ b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosNamingServiceWrapper.java @@ -117,7 +117,7 @@ public class NacosNamingServiceWrapper { logger.warn(LoggerCodeConstants.REGISTRY_NACOS_EXCEPTION, "", "", "Failed to request nacos naming server. " + (times < retryTimes ? "Dubbo will try to retry in " + sleepMsBetweenRetries + ". " : "Exceed retry max times.") + - "Try times: " + times + 1, e); + "Try times: " + (times + 1), e); if (times < retryTimes) { try { Thread.sleep(sleepMsBetweenRetries); @@ -151,7 +151,7 @@ public class NacosNamingServiceWrapper { logger.warn(LoggerCodeConstants.REGISTRY_NACOS_EXCEPTION, "", "", "Failed to request nacos naming server. " + (times < retryTimes ? "Dubbo will try to retry in " + sleepMsBetweenRetries + ". " : "Exceed retry max times.") + - "Try times: " + times + 1, e); + "Try times: " + (times + 1), e); if (times < retryTimes) { try { Thread.sleep(sleepMsBetweenRetries); diff --git a/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/util/NacosNamingServiceUtils.java b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/util/NacosNamingServiceUtils.java index c085701d86..b7809911a2 100644 --- a/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/util/NacosNamingServiceUtils.java +++ b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/util/NacosNamingServiceUtils.java @@ -20,6 +20,7 @@ import java.util.Map; import java.util.Properties; import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.constants.LoggerCodeConstants; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.StringUtils; @@ -39,8 +40,10 @@ import static com.alibaba.nacos.api.PropertyKeyConst.PASSWORD; import static com.alibaba.nacos.api.PropertyKeyConst.SERVER_ADDR; import static com.alibaba.nacos.api.PropertyKeyConst.USERNAME; import static com.alibaba.nacos.api.common.Constants.DEFAULT_GROUP; +import static com.alibaba.nacos.client.constant.Constants.HealthCheck.UP; import static com.alibaba.nacos.client.naming.utils.UtilAndComs.NACOS_NAMING_LOG_NAME; import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; +import static org.apache.dubbo.common.constants.LoggerCodeConstants.INTERNAL_INTERRUPTED; import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_NACOS_EXCEPTION; import static org.apache.dubbo.common.constants.RemotingConstants.BACKUP_KEY; import static org.apache.dubbo.common.utils.StringConstantFieldValuePredicate.of; @@ -57,6 +60,8 @@ public class NacosNamingServiceUtils { private static final String NACOS_RETRY_KEY = "nacos.retry"; + private static final String NACOS_CHECK_KEY = "nacos.check"; + private static final String NACOS_RETRY_WAIT_KEY = "nacos.retry-wait"; @@ -119,17 +124,39 @@ public class NacosNamingServiceUtils { */ public static NacosNamingServiceWrapper createNamingService(URL connectionURL) { Properties nacosProperties = buildNacosProperties(connectionURL); - NamingService namingService; + int retryTimes = connectionURL.getPositiveParameter(NACOS_RETRY_KEY, 10); + int sleepMsBetweenRetries = connectionURL.getPositiveParameter(NACOS_RETRY_WAIT_KEY, 1000); + boolean check = connectionURL.getParameter(NACOS_CHECK_KEY, true); + NamingService namingService = null; try { - namingService = NacosFactory.createNamingService(nacosProperties); - } catch (NacosException e) { - if (logger.isErrorEnabled()) { - logger.error(REGISTRY_NACOS_EXCEPTION, "", "", e.getErrMsg(), e); + for (int i = 0; i < retryTimes + 1; i++) { + namingService = NacosFactory.createNamingService(nacosProperties); + if (!check || UP.equals(namingService.getServerStatus())) { + break; + } else { + logger.warn(LoggerCodeConstants.REGISTRY_NACOS_EXCEPTION, "", "", + "Failed to connect to nacos naming server. " + + (i < retryTimes ? "Dubbo will try to retry in " + sleepMsBetweenRetries + ". " : "Exceed retry max times.") + + "Try times: " + (i + 1)); + } + namingService.shutDown(); + namingService = null; + Thread.sleep(sleepMsBetweenRetries); } + } catch (NacosException e) { + logger.error(REGISTRY_NACOS_EXCEPTION, "", "", e.getErrMsg(), e); + throw new IllegalStateException(e); + } catch (InterruptedException e) { + logger.error(INTERNAL_INTERRUPTED, "", "", "Interrupted when creating nacos naming service client.", e); + Thread.currentThread().interrupt(); throw new IllegalStateException(e); } - int retryTimes = connectionURL.getParameter(NACOS_RETRY_KEY, 10); - int sleepMsBetweenRetries = connectionURL.getParameter(NACOS_RETRY_WAIT_KEY, 10); + + if (namingService == null) { + logger.error(REGISTRY_NACOS_EXCEPTION, "", "", "Failed to create nacos naming service client. Reason: server status check failed."); + throw new IllegalStateException("Failed to create nacos naming service client. Reason: server status check failed."); + } + return new NacosNamingServiceWrapper(namingService, retryTimes, sleepMsBetweenRetries); } diff --git a/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/registry/nacos/NacosRegistryFactoryTest.java b/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/registry/nacos/NacosRegistryFactoryTest.java index 1df7d4d760..2af57626a4 100644 --- a/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/registry/nacos/NacosRegistryFactoryTest.java +++ b/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/registry/nacos/NacosRegistryFactoryTest.java @@ -18,7 +18,6 @@ package org.apache.dubbo.registry.nacos; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.NetUtils; - import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; @@ -42,7 +41,7 @@ class NacosRegistryFactoryTest { @Test void testCreateRegistryCacheKey() { - URL url = URL.valueOf("dubbo://" + NetUtils.getLocalAddress().getHostAddress() + ":8080"); + URL url = URL.valueOf("dubbo://" + NetUtils.getLocalAddress().getHostAddress() + ":8080?nacos.check=false"); String registryCacheKey1 = nacosRegistryFactory.createRegistryCacheKey(url); String registryCacheKey2 = nacosRegistryFactory.createRegistryCacheKey(url); Assertions.assertEquals(registryCacheKey1, registryCacheKey2); @@ -50,7 +49,7 @@ class NacosRegistryFactoryTest { @Test void testCreateRegistryCacheKeyWithNamespace() { - URL url = URL.valueOf("dubbo://" + NetUtils.getLocalAddress().getHostAddress() + ":8080?namespace=test"); + URL url = URL.valueOf("dubbo://" + NetUtils.getLocalAddress().getHostAddress() + ":8080?namespace=test&nacos.check=false"); String registryCacheKey1 = nacosRegistryFactory.createRegistryCacheKey(url); String registryCacheKey2 = nacosRegistryFactory.createRegistryCacheKey(url); Assertions.assertEquals(registryCacheKey1, registryCacheKey2); diff --git a/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/registry/nacos/NacosRegistryTest.java b/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/registry/nacos/NacosRegistryTest.java index c0568d3c52..82efa81bd9 100644 --- a/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/registry/nacos/NacosRegistryTest.java +++ b/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/registry/nacos/NacosRegistryTest.java @@ -69,7 +69,7 @@ class NacosRegistryTest { int nacosServerPort = NetUtils.getAvailablePort(); - this.registryUrl = URL.valueOf("nacos://localhost:" + nacosServerPort); + this.registryUrl = URL.valueOf("nacos://localhost:" + nacosServerPort + "?nacos.check=false"); this.nacosRegistryFactory = new NacosRegistryFactory(); diff --git a/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/registry/nacos/NacosServiceDiscoveryFactoryTest.java b/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/registry/nacos/NacosServiceDiscoveryFactoryTest.java index c52466d9a6..6f9003490c 100644 --- a/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/registry/nacos/NacosServiceDiscoveryFactoryTest.java +++ b/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/registry/nacos/NacosServiceDiscoveryFactoryTest.java @@ -20,7 +20,6 @@ import org.apache.dubbo.common.URL; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.registry.client.ServiceDiscovery; import org.apache.dubbo.rpc.model.ApplicationModel; - import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -42,7 +41,7 @@ class NacosServiceDiscoveryFactoryTest { @Test void testGetServiceDiscoveryWithCache() { - URL url = URL.valueOf("dubbo://test:8080"); + URL url = URL.valueOf("dubbo://test:8080?nacos.check=false"); ServiceDiscovery discovery = nacosServiceDiscoveryFactory.createDiscovery(url); Assertions.assertTrue(discovery instanceof NacosServiceDiscovery); diff --git a/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/registry/nacos/NacosServiceDiscoveryTest.java b/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/registry/nacos/NacosServiceDiscoveryTest.java index 962acca8af..d994a08f59 100644 --- a/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/registry/nacos/NacosServiceDiscoveryTest.java +++ b/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/registry/nacos/NacosServiceDiscoveryTest.java @@ -16,6 +16,12 @@ */ package org.apache.dubbo.registry.nacos; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.config.ApplicationConfig; @@ -25,10 +31,6 @@ import org.apache.dubbo.registry.client.event.ServiceInstancesChangedEvent; import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ScopeModelUtil; - -import com.alibaba.nacos.api.exception.NacosException; -import com.alibaba.nacos.api.naming.pojo.Instance; -import com.alibaba.nacos.api.naming.pojo.ListView; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; @@ -37,11 +39,9 @@ import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.mockito.internal.util.collections.Sets; -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.pojo.Instance; +import com.alibaba.nacos.api.naming.pojo.ListView; import static com.alibaba.nacos.api.common.Constants.DEFAULT_GROUP; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -63,7 +63,7 @@ class NacosServiceDiscoveryTest { private static final String LOCALHOST = "127.0.0.1"; - protected URL registryUrl = URL.valueOf("nacos://127.0.0.1:" + NetUtils.getAvailablePort()); + protected URL registryUrl = URL.valueOf("nacos://127.0.0.1:" + NetUtils.getAvailablePort() + "?nacos.check=false"); private NacosServiceDiscovery nacosServiceDiscovery; @@ -79,7 +79,7 @@ class NacosServiceDiscoveryTest { public NacosServiceDiscoveryGroupTest1() { super(); group = "test-group1"; - registryUrl = URL.valueOf("nacos://127.0.0.1:" + NetUtils.getAvailablePort()).addParameter("group", group); + registryUrl = URL.valueOf("nacos://127.0.0.1:" + NetUtils.getAvailablePort() + "?nacos.check=false").addParameter("group", group); } } @@ -87,7 +87,7 @@ class NacosServiceDiscoveryTest { public NacosServiceDiscoveryGroupTest2() { super(); group = "test-group2"; - registryUrl = URL.valueOf("nacos://127.0.0.1:" + NetUtils.getAvailablePort()).addParameter("group", group); + registryUrl = URL.valueOf("nacos://127.0.0.1:" + NetUtils.getAvailablePort() + "?nacos.check=false").addParameter("group", group); } } @@ -97,7 +97,7 @@ class NacosServiceDiscoveryTest { public NacosServiceDiscoveryGroupTest3() { super(); group = DEFAULT_GROUP; - registryUrl = URL.valueOf("nacos://127.0.0.1:" + NetUtils.getAvailablePort()).addParameter("group", "test-group3"); + registryUrl = URL.valueOf("nacos://127.0.0.1:" + NetUtils.getAvailablePort() + "?nacos.check=false").addParameter("group", "test-group3"); } @BeforeAll diff --git a/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/registry/nacos/util/NacosNamingServiceUtilsTest.java b/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/registry/nacos/util/NacosNamingServiceUtilsTest.java index 701d97cffa..35c13d6a41 100644 --- a/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/registry/nacos/util/NacosNamingServiceUtilsTest.java +++ b/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/registry/nacos/util/NacosNamingServiceUtilsTest.java @@ -16,19 +16,29 @@ */ package org.apache.dubbo.registry.nacos.util; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.atomic.AtomicInteger; + import org.apache.dubbo.common.URL; import org.apache.dubbo.metadata.report.MetadataReport; import org.apache.dubbo.registry.client.ServiceInstance; +import org.apache.dubbo.registry.nacos.MockNamingService; import org.apache.dubbo.registry.nacos.NacosNamingServiceWrapper; - -import com.alibaba.nacos.api.naming.pojo.Instance; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; import org.mockito.Mockito; -import java.util.HashMap; -import java.util.Map; +import com.alibaba.nacos.api.NacosFactory; +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.NamingService; +import com.alibaba.nacos.api.naming.pojo.Instance; +import static com.alibaba.nacos.client.constant.Constants.HealthCheck.DOWN; +import static com.alibaba.nacos.client.constant.Constants.HealthCheck.UP; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; /** @@ -67,8 +77,35 @@ class NacosNamingServiceUtilsTest { @Test void testCreateNamingService() { - URL url = URL.valueOf("test://test:8080/test?backup=backup"); + URL url = URL.valueOf("test://test:8080/test?backup=backup&nacos.check=false"); NacosNamingServiceWrapper namingService = NacosNamingServiceUtils.createNamingService(url); Assertions.assertNotNull(namingService); } + + + @Test + void testRetryCreate() throws NacosException { + try (MockedStatic nacosFactoryMockedStatic = Mockito.mockStatic(NacosFactory.class)) { + AtomicInteger atomicInteger = new AtomicInteger(0); + NamingService mock = new MockNamingService() { + @Override + public String getServerStatus() { + return atomicInteger.incrementAndGet() > 10 ? UP : DOWN; + } + }; + nacosFactoryMockedStatic.when(() -> NacosFactory.createNamingService((Properties) any())).thenReturn(mock); + + + URL url = URL.valueOf("nacos://127.0.0.1:8848") + .addParameter("nacos.retry", 5) + .addParameter("nacos.retry-wait", 10); + Assertions.assertThrows(IllegalStateException.class, () -> NacosNamingServiceUtils.createNamingService(url)); + + try { + NacosNamingServiceUtils.createNamingService(url); + } catch (Throwable t) { + Assertions.fail(t); + } + } + } }