前言:
1.nacos是基于推送事件模式获取服务列表信息的
2.ribbon是本地每隔一定时间主动去拉取服务信息的
这两个特性就可能会导致nacos上已经获取到了服务列表但是ribbon还没开始拉取最新的服务信息,导致在调用服务的时候会找不到nacos上的服务。
查了一下网上资料并没有很可靠的解决方案,还是看一看源码吧
解决办法:
1.在本地nacos监听到事件消息的时候,通过ribbon主动拉取一下服务信息,让ribbon上的服务信息和nacos保持一致,达到同步的目的。
nacos官方文档:https://nacos.io/zh-cn/docs/sdk.html
代码如下:
1.本地配置nacosService
@Configuration
@Data
@Slf4j
public class NacosNamingConfig {
@Value("${spring.cloud.nacos.discovery.server-addr}")
private String serverAddr;
@Value("${spring.cloud.nacos.discovery.namespace}")
private String namespace;
private static NamingService namingService;
@Bean(name = "namingService")
public NamingService get() throws NacosException {
log.info("NacosNamingConfig namingService 执行");
Properties properties = System.getProperties();
properties.setProperty("serverAddr", serverAddr);
properties.setProperty("namespace", namespace);
namingService = NamingFactory.createNamingService(properties);
return namingService;
}
}
2.配置updater,更新ribbon服务列表有用
@Component("ribbonServerListUpdater")
public class MyPollingServerListUpdater extends PollingServerListUpdater {
private UpdateAction updateAction;
@Override
public synchronized void start(UpdateAction updateAction) {
this.updateAction = updateAction;
super.start(updateAction);
}
public UpdateAction getUpdateAction(){
return updateAction;
}
}
2.开启nacos订阅
@Component
@Slf4j
public class ServerStatusRunner implements CommandLineRunner {
@Autowired
private NamingService namingService;
@Override
public void run(String... args) throws Exception {
namingService.subscribe("服务名", new NacosListener());
}
}
3.nacos事件监听里面主动刷新ribbon里面的服务列表信息
@Slf4j
public class NacosListener implements EventListener {
private MyPollingServerListUpdater myListUpdater;
private NacosNamingService namingService;
private HostReactor hostReactor;
@Override
public void onEvent(Event event) {
log.info("nacos监听事件消息:"+ JSON.toJSONString(event));
log.info("nacos监听事件,刷新ribbon本地缓存");
namingService = SpringJobBeanFactory.getBean(NacosNamingService.class);
Class cls = Class.forName("com.alibaba.nacos.client.naming.NacosNamingService");
Field field = cls.getDeclaredField("hostReactor");
field.setAccessible(true);
Object hostReactorValue = field.get(namingService);
this.hostReactor = (HostReactor) hostReactorValue;
this.hostReactor.updateServiceNow("服务名", "");
log.info("nacos监听事件,更新ribbon服务列表");
myListUpdater = SpringJobBeanFactory.getBean(MyPollingServerListUpdater.class);
ServerListUpdater.UpdateAction updateAction = myListUpdater.getUpdateAction();
if (updateAction != null) {
updateAction.doUpdate();
}
}
}
4.工具类:
@Component
public class SpringJobBeanFactory implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringJobBeanFactory.applicationContext=applicationContext;
}
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
@SuppressWarnings("unchecked")
public static <T> T getBean(String name) throws BeansException {
if (applicationContext == null){
return null;
}
return (T)applicationContext.getBean(name);
}
public static <T> T getBean(Class<T> name) throws BeansException {
if (applicationContext == null){
return null;
}
return applicationContext.getBean(name);
}
}