Open joyuce opened 4 years ago
welcome to contribute it!
这个功能确实很急需啊
is there any progress?
如果仅仅基于Ribbon,可以自行扩展,可以参考https://gitee.com/nepxion/Discovery/blob/6.x.x/discovery-plugin-register-center/discovery-plugin-register-center-starter-nacos/src/main/java/com/nepxion/discovery/plugin/registercenter/nacos/decorator/NacosServerListDecorator.java
getUpdatedListOfServers上触发出一个事件即可,仅供参考
最终我基于namingService subscribe
API重写了ServerListUpdater
接口,基本实现基于事件机制的ServerListUpdater
,但是在实测中发现,nacos的事件推送很不稳定,在服务节点下线后基本需要5-10秒以后client才能收到NamingEvent
,作为事件订阅本身来讲,这个延迟是否可以接受?是否还有优化空间?
顺着eventListener的源码往上扒了扒,发现Nacos的事件订阅实际上是基于pull模式的“伪订阅”…,然后去阿里云看了一下MSE的服务下线优势…大概明白为啥Eureka有基于事件监听机制的ServerListUpdater而Nacos没有了…
最终我基于
namingService subscribe
API重写了ServerListUpdater
接口,基本实现基于事件机制的ServerListUpdater
,但是在实测中发现,nacos的事件推送很不稳定,在服务节点下线后基本需要5-10秒以后client才能收到NamingEvent
,作为事件订阅本身来讲,这个延迟是否可以接受?是否还有优化空间?顺着eventListener的源码往上扒了扒,发现Nacos的事件订阅实际上是基于pull模式的“伪订阅”…,然后去阿里云看了一下MSE的服务下线优势…大概明白为啥Eureka有基于事件监听机制的ServerListUpdater而Nacos没有了…
您好,可以麻烦您提供下基于事件机制的ServerListUpdater的源码吗?谢谢。
/**
* @author: ruansheng
* @date: 2022-08-08
*/
@RequiredArgsConstructor
public class NacosInstancesChangeNotifier extends Subscriber<InstancesChangeEvent> implements SmartInitializingSingleton {
private final SpringClientFactory springClientFactory;
private final String applicationName;
@Override
public void onEvent(InstancesChangeEvent event) {
ILoadBalancer loadBalancer = this.springClientFactory.getLoadBalancer(event.getServiceName());
if (this.applicationName.equals(event.getServiceName()) || !(loadBalancer instanceof DynamicServerListLoadBalancer)) {
return;
}
((DynamicServerListLoadBalancer<?>) loadBalancer).updateListOfServers();
}
@Override
public Class<? extends Event> subscribeType() {
return InstancesChangeEvent.class;
}
@Override
public void afterSingletonsInstantiated() {
NotifyCenter.registerSubscriber(this);
}
}
@Bean
public NacosInstancesChangeNotifier nacosInstancesChangeNotifier(SpringClientFactory springClientFactory, Environment environment) {
String applicationName = environment.getProperty("spring.application.name", "");
return new NacosInstancesChangeNotifier(springClientFactory, applicationName);
}
监听器实现参考
/** * @author: ruansheng * @date: 2022-08-08 */ @RequiredArgsConstructor public class NacosInstancesChangeNotifier extends Subscriber<InstancesChangeEvent> implements SmartInitializingSingleton { private final SpringClientFactory springClientFactory; private final String applicationName; @Override public void onEvent(InstancesChangeEvent event) { ILoadBalancer loadBalancer = this.springClientFactory.getLoadBalancer(event.getServiceName()); if (this.applicationName.equals(event.getServiceName()) || !(loadBalancer instanceof DynamicServerListLoadBalancer)) { return; } ((DynamicServerListLoadBalancer<?>) loadBalancer).updateListOfServers(); } @Override public Class<? extends Event> subscribeType() { return InstancesChangeEvent.class; } @Override public void afterSingletonsInstantiated() { NotifyCenter.registerSubscriber(this); } }
定义监听器
@Bean public NacosInstancesChangeNotifier nacosInstancesChangeNotifier(SpringClientFactory springClientFactory, Environment environment) { String applicationName = environment.getProperty("spring.application.name", ""); return new NacosInstancesChangeNotifier(springClientFactory, applicationName); }
nacos的事件推送是不稳定的, 所以这样也不能很好的解决
@zhaoxilingcheng 长期的使用验证下来,nacos 的推送事件稳定性还是很高的。
当然,还可以同时配合其他机制来进一步保证稳定性,例如:当前服务实例下线时,基于消息订阅等相关方式将当前下线的实例通知给其他服务,然后其他服务将“下线的服务实例”缓存到当前服务的“黑名单”中,然后在负载均衡选择实例的时候跳过“黑名单”中的实例即可。
InstancesChangeEvent
监听这个事件 只有自身服务实例变化的时候才会触发,nacos中其它服务上下线根本不会触发到网关增加的这段代码里面来。是我版本不对,还是我理解的不对?
@shenhuaxx 服务实例变化、上下线均会触发这个事件
InstancesChangeEvent
监听这个事件 只有自身服务实例变化的时候才会触发,nacos中其它服务上下线根本不会触发到网关增加的这段代码里面来。是我版本不对,还是我理解的不对?
这里补充一点,如果没有收到来自Nacos的实例上下线事件通知,需要先触发一次对应服务的远程调用,这是因为无论是 Spring Cloud Gateway 还是 OpenFeign ,LoadBalancerClient
对象默认都是懒加载的,每个不同的服务都会对应创建一个 LoadBalancerClient
对象,所以只有当 LoadBalancerClient
对象被创建后,才会拥有对应服务的监听能力。
Spring Cloud Gateway 相关源码参考:
@Configuration(proxyBeanMethods = false)
@ConditionalOnDiscoveryEnabled
public class LoadBalancerClientConfiguration {
@Bean
@ConditionalOnMissingBean
public ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer(Environment environment,
LoadBalancerClientFactory loadBalancerClientFactory) {
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
return new RoundRobinLoadBalancer(
loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
}
......
}
@shenhuaxx 服务实例变化、上下线均会触发这个事件
测试后发现2.2.x版本收不到这个事件,高版本可以正常收到事件。
使用feign调用服务时,ribbon默认使用PollingServerListUpdater定时任务更新服务,eureka有对应的EurekaNotificationServerListUpdater基于事件的更新,目前nacos是否有提供对应的基于事件的ServerListUpdater,可以更快的感知到服务的上下线。