fix: Nacos integration with SpringBootAdmin.(2.2.x) (#3308)

pull/3335/head
ZhangBin 2 years ago committed by GitHub
parent 9c83fb8e95
commit 1add62660e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -349,5 +349,6 @@ spring.cloud.nacos.discovery.ip-type=IPv6
|接入点|`spring.cloud.nacos.discovery.endpoint`||地域的某个服务的入口域名,通过此域名可以动态地拿到服务端地址
|是否集成Ribbon|`ribbon.nacos.enabled`|`true`|一般都设置成true即可
|是否开启Nacos Watch|`spring.cloud.nacos.discovery.watch.enabled`|`true`|可以设置成false来关闭 watch
|是否开启Nacos Discovery HeartBeat|`spring.cloud.nacos.discovery.heart-beat.enabled`|`false`|可以设置成 true 来开启 heart beat
|===

@ -352,5 +352,6 @@ The following shows the other configurations of the starter of Nacos Discovery:
|Endpoint|`spring.cloud.nacos.discovery.endpoint`||The domain name of a certain service in a specific region. You can retrieve the server address dynamically with this domain name
|Integrate Ribbon or not|`ribbon.nacos.enabled`|`true`|Set to true in most cases
|Enable Nacos Watch|`spring.cloud.nacos.discovery.watch.enabled`|`false`|set to true to enable watch
|Enable Nacos Discovery HeartBeat|`spring.cloud.nacos.discovery.heart-beat.enabled`|`false`|set to true to enable heart beat
|===

@ -168,10 +168,12 @@ public class FrontController {
resultHolder = productDAO.detail(AppContextClient.getRouteId(), id);
break;
case Constants.REST_TEMPLATE:
resultHolder = productDAO.detailByRestTemplate(AppContextClient.getRouteId(), id);
resultHolder = productDAO.detailByRestTemplate(AppContextClient.getRouteId(),
id);
break;
case Constants.WEB_CLIENT:
resultHolder = productDAO.detailByWebClient(AppContextClient.getRouteId(), id);
resultHolder = productDAO.detailByWebClient(AppContextClient.getRouteId(),
id);
break;
default:
throw new IllegalArgumentException("The web request is malformed.");

@ -46,7 +46,8 @@ public class FeignResponseDecoderInterceptor implements Decoder {
public Object decode(Response response, Type type)
throws IOException, FeignException {
Object object = delegate.decode(response, type);
logger.info("FeignResponseDecoderInterceptor uri {} for request {} got cleared by {}",
logger.info(
"FeignResponseDecoderInterceptor uri {} for request {} got cleared by {}",
UriContext.getUriPath(), response.request().url(), delegate.getClass());
UriContext.clearContext();
return object;

@ -102,6 +102,7 @@
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix-zuul</artifactId>
<optional>true</optional>
<scope>test</scope>
</dependency>
<dependency>

@ -67,18 +67,4 @@ public class NacosDiscoveryClientConfiguration {
return new NacosWatch(nacosServiceManager, nacosDiscoveryProperties);
}
/**
* Spring Cloud Gateway HeartBeat . publish an event every 30 seconds.
* https://github.com/alibaba/spring-cloud-alibaba/issues/2868
* @param nacosDiscoveryProperties nacosDiscoveryProperties
* @return nacosWatch.
*/
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty("spring.cloud.gateway.discovery.locator.enabled")
public GatewayLocatorHeartBeatPublisher gatewayLocatorHeartBeatPublisher(
NacosDiscoveryProperties nacosDiscoveryProperties) {
return new GatewayLocatorHeartBeatPublisher(nacosDiscoveryProperties);
}
}

@ -0,0 +1,106 @@
/*
* Copyright 2013-2023 the original author or authors.
*
* 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
*
* https://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.cloud.nacos.discovery;
import com.alibaba.cloud.nacos.ConditionalOnNacosDiscoveryEnabled;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.client.ConditionalOnBlockingDiscoveryEnabled;
import org.springframework.cloud.client.ConditionalOnDiscoveryEnabled;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
/**
* @author xiaojing
* @author echooymxq
* @author ruansheng
* @author zhangbin
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnDiscoveryEnabled
@ConditionalOnBlockingDiscoveryEnabled
@ConditionalOnNacosDiscoveryEnabled
@AutoConfigureAfter(value = { NacosDiscoveryAutoConfiguration.class }, name = {
"org.springframework.cloud.netflix.zuul.ZuulProxyMarkerConfiguration",
"de.codecentric.boot.admin.server.cloud.config.AdminServerDiscoveryAutoConfiguration" })
public class NacosDiscoveryHeartBeatConfiguration {
/**
* Nacos HeartBeat is no longer enabled by default . publish an event every 30 seconds
* see https://github.com/alibaba/spring-cloud-alibaba/issues/2868 see
* https://github.com/alibaba/spring-cloud-alibaba/issues/3258
* @param nacosDiscoveryProperties nacosDiscoveryProperties
* @return nacosDiscoveryHeartBeatPublisher
*/
@Bean
@ConditionalOnMissingBean
@Conditional(NacosDiscoveryHeartBeatCondition.class)
public NacosDiscoveryHeartBeatPublisher nacosDiscoveryHeartBeatPublisher(
NacosDiscoveryProperties nacosDiscoveryProperties) {
return new NacosDiscoveryHeartBeatPublisher(nacosDiscoveryProperties);
}
private static class NacosDiscoveryHeartBeatCondition extends AnyNestedCondition {
NacosDiscoveryHeartBeatCondition() {
super(ConfigurationPhase.REGISTER_BEAN);
}
/**
* Spring Cloud Gateway HeartBeat .
*/
@ConditionalOnProperty(value = "spring.cloud.gateway.discovery.locator.enabled",
matchIfMissing = false)
static class GatewayLocatorHeartBeatEnabled {
}
/**
* Nacos HeartBeat .
*/
@ConditionalOnBean(name = "zuulProxyMarkerBean")
static class ZuulGatewayHeartBeatEnabled {
}
/**
* Spring Boot Admin HeartBeat .
*/
@ConditionalOnBean(
type = "de.codecentric.boot.admin.server.cloud.discovery.InstanceDiscoveryListener")
static class SpringBootAdminHeartBeatEnabled {
}
/**
* Nacos HeartBeat .
*/
@ConditionalOnProperty(value = "spring.cloud.nacos.discovery.heart-beat.enabled",
matchIfMissing = false)
static class NacosDiscoveryHeartBeatEnabled {
}
}
}

@ -16,6 +16,7 @@
package com.alibaba.cloud.nacos.discovery;
import java.time.Duration;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
@ -34,25 +35,25 @@ import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
* @author yuhuangbin
* @author ruansheng
*/
public class GatewayLocatorHeartBeatPublisher
public class NacosDiscoveryHeartBeatPublisher
implements ApplicationEventPublisherAware, SmartLifecycle {
private static final Logger log = LoggerFactory
.getLogger(GatewayLocatorHeartBeatPublisher.class);
.getLogger(NacosDiscoveryHeartBeatPublisher.class);
private final NacosDiscoveryProperties nacosDiscoveryProperties;
private final ThreadPoolTaskScheduler taskScheduler;
private final AtomicLong nacosWatchIndex = new AtomicLong(0);
private final AtomicLong nacosHeartBeatIndex = new AtomicLong(0);
private final AtomicBoolean running = new AtomicBoolean(false);
private ApplicationEventPublisher publisher;
private ScheduledFuture<?> watchFuture;
private ScheduledFuture<?> heartBeatFuture;
public GatewayLocatorHeartBeatPublisher(
public NacosDiscoveryHeartBeatPublisher(
NacosDiscoveryProperties nacosDiscoveryProperties) {
this.nacosDiscoveryProperties = nacosDiscoveryProperties;
this.taskScheduler = getTaskScheduler();
@ -67,19 +68,23 @@ public class GatewayLocatorHeartBeatPublisher
@Override
public void start() {
log.info("Start nacos gateway locator heartBeat task scheduler.");
this.watchFuture = this.taskScheduler.scheduleWithFixedDelay(
this::publishHeartBeat, this.nacosDiscoveryProperties.getWatchDelay());
if (this.running.compareAndSet(false, true)) {
log.info("Start nacos heartBeat task scheduler.");
this.heartBeatFuture = this.taskScheduler.scheduleWithFixedDelay(
this::publishHeartBeat,
Duration.ofMillis(this.nacosDiscoveryProperties.getWatchDelay()));
}
}
@Override
public void stop() {
if (this.watchFuture != null) {
// shutdown current user-thread,
// then the other daemon-threads will terminate automatic.
this.taskScheduler.shutdown();
this.watchFuture.cancel(true);
if (this.running.compareAndSet(true, false)) {
if (this.heartBeatFuture != null) {
// shutdown current user-thread,
// then the other daemon-threads will terminate automatic.
this.taskScheduler.shutdown();
this.heartBeatFuture.cancel(true);
}
}
}
@ -104,7 +109,7 @@ public class GatewayLocatorHeartBeatPublisher
*/
public void publishHeartBeat() {
HeartbeatEvent event = new HeartbeatEvent(this,
nacosWatchIndex.getAndIncrement());
nacosHeartBeatIndex.getAndIncrement());
this.publisher.publishEvent(event);
}

@ -1,45 +0,0 @@
/*
* Copyright 2013-2023 the original author or authors.
*
* 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
*
* https://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.cloud.nacos.discovery;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cloud.netflix.zuul.ZuulProxyMarkerConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author ruansheng
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(ZuulProxyMarkerConfiguration.class)
@AutoConfigureAfter(ZuulProxyMarkerConfiguration.class)
public class ZuulGatewayLocatorAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnBean(name = "zuulProxyMarkerBean")
public GatewayLocatorHeartBeatPublisher gatewayLocatorHeartBeatPublisher(
NacosDiscoveryProperties nacosDiscoveryProperties) {
return new GatewayLocatorHeartBeatPublisher(nacosDiscoveryProperties);
}
}

@ -47,6 +47,12 @@
"defaultValue": "false",
"description": "enable nacos discovery watch or not ."
},
{
"name": "spring.cloud.nacos.discovery.heart-beat.enabled",
"type": "java.lang.Boolean",
"defaultValue": "false",
"description": "enable nacos discovery heart beat or not ."
},
{
"name": "spring.cloud.nacos.discovery.username",
"type": "java.lang.String",

@ -4,12 +4,12 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.alibaba.cloud.nacos.endpoint.NacosDiscoveryEndpointAutoConfiguration,\
com.alibaba.cloud.nacos.registry.NacosServiceRegistryAutoConfiguration,\
com.alibaba.cloud.nacos.discovery.NacosDiscoveryClientConfiguration,\
com.alibaba.cloud.nacos.discovery.NacosDiscoveryHeartBeatConfiguration,\
com.alibaba.cloud.nacos.discovery.reactive.NacosReactiveDiscoveryClientConfiguration,\
com.alibaba.cloud.nacos.discovery.configclient.NacosConfigServerAutoConfiguration,\
com.alibaba.cloud.nacos.discovery.ZuulGatewayLocatorAutoConfiguration,\
com.alibaba.cloud.nacos.NacosServiceAutoConfiguration,\
com.alibaba.cloud.nacos.util.UtilIPv6AutoConfiguration
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
com.alibaba.cloud.nacos.discovery.configclient.NacosDiscoveryClientConfigServiceBootstrapConfiguration
org.springframework.boot.SpringApplicationRunListener=\
com.alibaba.cloud.nacos.logging.NacosLoggingAppRunListener
com.alibaba.cloud.nacos.logging.NacosLoggingAppRunListener

@ -26,7 +26,6 @@ import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationConfiguration;
import org.springframework.cloud.commons.util.UtilAutoConfiguration;
import org.springframework.cloud.netflix.zuul.ZuulProxyMarkerConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
@ -46,8 +45,7 @@ public class NacosDiscoveryClientConfigurationTest {
UtilAutoConfiguration.class, UtilIPv6AutoConfiguration.class,
NacosServiceAutoConfiguration.class,
NacosDiscoveryAutoConfiguration.class,
NacosDiscoveryClientConfiguration.class,
ZuulGatewayLocatorAutoConfiguration.class, this.getClass()));
NacosDiscoveryClientConfiguration.class, this.getClass()));
@Bean
public TaskScheduler taskScheduler() {
@ -79,37 +77,4 @@ public class NacosDiscoveryClientConfigurationTest {
.run(context -> assertThat(context).hasSingleBean(NacosWatch.class));
}
@Test
public void testDefaultGatewayLocatorHeartBeatPublisher() {
contextRunner.run(context -> assertThat(context)
.doesNotHaveBean(GatewayLocatorHeartBeatPublisher.class));
}
@Test
public void testSpringCloudGatewayLocatorHeartBeatPublisherEnabled() {
contextRunner
.withPropertyValues("spring.cloud.gateway.discovery.locator.enabled=true")
.run(context -> assertThat(context)
.hasSingleBean(GatewayLocatorHeartBeatPublisher.class));
}
@Test
public void testZuulGatewayLocatorHeartBeatPublisherEnabled() {
contextRunner
.withConfiguration(
AutoConfigurations.of(ZuulProxyMarkerConfiguration.class))
.run(context -> assertThat(context)
.hasSingleBean(GatewayLocatorHeartBeatPublisher.class));
}
@Test
public void testZuulAndSpringCloudGatewayLocatorHeartBeatPublisherEnabled() {
contextRunner
.withPropertyValues("spring.cloud.gateway.discovery.locator.enabled=true")
.withConfiguration(
AutoConfigurations.of(ZuulProxyMarkerConfiguration.class))
.run(context -> assertThat(context)
.hasSingleBean(GatewayLocatorHeartBeatPublisher.class));
}
}

@ -0,0 +1,107 @@
/*
* Copyright 2013-2023 the original author or authors.
*
* 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
*
* https://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.cloud.nacos.discovery;
import com.alibaba.cloud.nacos.NacosServiceAutoConfiguration;
import com.alibaba.cloud.nacos.registry.NacosServiceRegistryAutoConfiguration;
import com.alibaba.cloud.nacos.util.UtilIPv6AutoConfiguration;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationConfiguration;
import org.springframework.cloud.commons.util.UtilAutoConfiguration;
import org.springframework.cloud.netflix.zuul.ZuulProxyMarkerConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
/**
* @author <a href="mailto:echooy.mxq@gmail.com">echooymxq</a>
**/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = NacosDiscoveryHeartBeatConfigurationTest.TestConfig.class,
webEnvironment = RANDOM_PORT)
public class NacosDiscoveryHeartBeatConfigurationTest {
private ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(
AutoConfigurations.of(AutoServiceRegistrationConfiguration.class,
NacosServiceRegistryAutoConfiguration.class,
UtilAutoConfiguration.class, UtilIPv6AutoConfiguration.class,
NacosServiceAutoConfiguration.class,
NacosDiscoveryAutoConfiguration.class,
NacosDiscoveryClientConfiguration.class,
NacosDiscoveryHeartBeatConfiguration.class, this.getClass()));
@Test
public void testDefaultNacosDiscoveryHeartBeatPublisher() {
contextRunner.run(context -> assertThat(context)
.doesNotHaveBean(NacosDiscoveryHeartBeatPublisher.class));
}
@Test
public void testNacosDiscoveryHeartBeatPublisherEnabledForGateway() {
contextRunner
.withPropertyValues("spring.cloud.gateway.discovery.locator.enabled=true")
.run(context -> assertThat(context)
.hasSingleBean(NacosDiscoveryHeartBeatPublisher.class));
}
@Test
public void testNacosDiscoveryHeartBeatPublisherEnabledForProperties() {
contextRunner
.withPropertyValues(
"spring.cloud.nacos.discovery.heart-beat.enabled=true")
.run(context -> assertThat(context)
.hasSingleBean(NacosDiscoveryHeartBeatPublisher.class));
}
@Test
public void testZuulGatewayLocatorHeartBeatPublisherEnabled() {
contextRunner
.withConfiguration(
AutoConfigurations.of(ZuulProxyMarkerConfiguration.class))
.run(context -> assertThat(context)
.hasSingleBean(NacosDiscoveryHeartBeatPublisher.class));
}
@Test
public void testZuulAndSpringCloudGatewayLocatorHeartBeatPublisherEnabled() {
contextRunner
.withPropertyValues("spring.cloud.gateway.discovery.locator.enabled=true")
.withConfiguration(
AutoConfigurations.of(ZuulProxyMarkerConfiguration.class))
.run(context -> assertThat(context)
.hasSingleBean(NacosDiscoveryHeartBeatPublisher.class));
}
@Configuration
@EnableAutoConfiguration
@ImportAutoConfiguration({ NacosDiscoveryAutoConfiguration.class,
NacosDiscoveryHeartBeatConfiguration.class })
public static class TestConfig {
}
}

@ -126,10 +126,12 @@ public final class SentinelFeign {
"No %s instance of type %s found for feign client %s",
type, fallbackType, name));
}
// when fallback is a FactoryBean, should determine the type of instance
// when fallback is a FactoryBean, should determine the type of
// instance
if (fallbackInstance instanceof FactoryBean<?>) {
try {
fallbackInstance = ((FactoryBean<Object>)fallbackInstance).getObject();
fallbackInstance = ((FactoryBean<Object>) fallbackInstance)
.getObject();
}
catch (Exception e) {
throw new IllegalStateException(type + " create fail", e);

@ -42,25 +42,29 @@ public class SentinelFallbackSupportFactoryBeanTests {
private final ApplicationContextRunner runner = new ApplicationContextRunner()
.withBean(FactoryBeanFallbackFeignFallback.class)
.withBean(OriginalFeignFallback.class)
.withConfiguration(AutoConfigurations.of(TestConfiguration.class, FeignAutoConfiguration.class))
.withConfiguration(AutoConfigurations.of(TestConfiguration.class,
FeignAutoConfiguration.class))
.withPropertyValues("feign.sentinel.enabled=true");
@Test
public void shouldRunFallbackFromBeanOrFactoryBean() {
runner.run(ctx -> {
assertThat(ctx.getBean(OriginalFeign.class).get()).isEqualTo(ORIGINAL_FALLBACK_MESSAGE);
assertThat(ctx.getBean(FactoryBeanFallbackFeign.class).get()).isEqualTo(FACTORY_BEAN_FALLBACK_MESSAGE);
assertThat(ctx.getBean(OriginalFeign.class).get())
.isEqualTo(ORIGINAL_FALLBACK_MESSAGE);
assertThat(ctx.getBean(FactoryBeanFallbackFeign.class).get())
.isEqualTo(FACTORY_BEAN_FALLBACK_MESSAGE);
});
}
@Configuration(proxyBeanMethods = false)
@EnableFeignClients(clients = {OriginalFeign.class, FactoryBeanFallbackFeign.class })
@EnableFeignClients(clients = { OriginalFeign.class, FactoryBeanFallbackFeign.class })
@EnableAutoConfiguration
public static class TestConfiguration {
}
@FeignClient(name = "original", url = "https://original", fallback = OriginalFeignFallback.class)
@FeignClient(name = "original", url = "https://original",
fallback = OriginalFeignFallback.class)
interface OriginalFeign {
@GetMapping("/")
@ -68,7 +72,8 @@ public class SentinelFallbackSupportFactoryBeanTests {
}
@FeignClient(name = "factoryBean", url = "https://factoryBean", fallback = FactoryBeanFallbackFeignFallback.class)
@FeignClient(name = "factoryBean", url = "https://factoryBean",
fallback = FactoryBeanFallbackFeignFallback.class)
interface FactoryBeanFallbackFeign {
@GetMapping("/")
@ -76,7 +81,8 @@ public class SentinelFallbackSupportFactoryBeanTests {
}
private static class FactoryBeanFallbackFeignFallback implements FactoryBean<FactoryBeanFallbackFeign> {
private static class FactoryBeanFallbackFeignFallback
implements FactoryBean<FactoryBeanFallbackFeign> {
@Override
public FactoryBeanFallbackFeign getObject() {

@ -31,6 +31,7 @@ import io.envoyproxy.envoy.service.discovery.v3.DiscoveryResponse;
/**
* CdsProtocol contains information about service.
*
* @author musi
* @author <a href="liuziming@buaa.edu.cn"></a>
* @since 2.2.10-RC1

Loading…
Cancel
Save