继续分析昨天boot-strap启动报错NPE的问题,提示:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'nacosSyncDataService' defined in class path resource [org/dromara/soul/springboot/starter/sync/data/nacos/NacosSyncDataConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.dromara.soul.sync.data.api.SyncDataService]: Factory method 'nacosSyncDataService' threw exception; nested exception is com.alipay.sofa.rpc.core.exception.SofaRpcRuntimeException: RPC-010010015: 构造客户端代理出错!
原因是:soul-bootstrap 启动的时候会去,nacos获取网关数据,通过debugger,拿到的是空数据,使用nacos同步数据的时候,如果网关只代理http服务(无元数据),soul-bootstrap是启动不起来的。
2021-01-28 22:56:36.092 INFO 14652 --- [ main] d.s.s.s.s.d.n.NacosSyncDataConfiguration : you use nacos sync soul data.......
2021-01-28 22:56:37.318 INFO 14652 --- [ main] o.s.b.a.e.web.EndpointLinksResolver : Exposing 2 endpoint(s) beneath base path '/actuator'
2021-01-28 22:56:39.379 INFO 14652 --- [ main] o.s.b.web.embedded.netty.NettyWebServer : Netty started on port(s): 9195
2021-01-28 22:56:39.394 INFO 14652 --- [ main] o.d.s.b.SoulBootstrapApplication : Started SoulBootstrapApplication in 10.686 seconds (JVM running for 16.254)
小结:soul-bootstrap 启动不起来报空指针异常,首先soul-admin启动后不会主动向nacos同步网关数据,需要手动同步,这个问题困扰了挺长时间,最后是看了其他同学的分享,才恍然大悟。
数据初始化更新的时候 NacosCacheHandler
,通过调用 watcherData
方法,使用Nacos提供的 Listener
来注册当Nacos发布配置时候,可以监听到事件。
public class NacosCacheHandler {
。。。。。。
protected void watcherData(final String dataId, final OnChange oc) {
Listener listener = new Listener() {
@Override
public void receiveConfigInfo(final String configInfo) {
oc.change(configInfo);
}
@Override
public Executor getExecutor() {
return null;
}
};
oc.change(getConfigAndSignListener(dataId, listener));
LISTENERS.computeIfAbsent(dataId, key -> new ArrayList<>()).add(listener);
}
soul-admin的Nacos
配置类 NacosConfiguration
#nacosConfigService
初始化bean时通过 NacosFactory
创建配置服务。 DataChangedEventDispatcher
负责调用具体的监听实现类对 DataChangedEvent
事件进行处理,实现类NacosDataChangedListener
监听器会监听配置的变化,NacosDataChangedListener
会执行 onRuleChanged,updateRuleMap 先将网关数据同步至内存,在通过publishConfig
同步至nacos。
public ConfigService nacosConfigService(final NacosProperties nacosProp) throws Exception {
Properties properties = new Properties();
if (nacosProp.getAcm() != null && nacosProp.getAcm().isEnabled()) {
// Use aliyun ACM service
properties.put(PropertyKeyConst.ENDPOINT, nacosProp.getAcm().getEndpoint());
properties.put(PropertyKeyConst.NAMESPACE, nacosProp.getAcm().getNamespace());
// Use subaccount ACM administrative authority
properties.put(PropertyKeyConst.ACCESS_KEY, nacosProp.getAcm().getAccessKey());
properties.put(PropertyKeyConst.SECRET_KEY, nacosProp.getAcm().getSecretKey());
} else {
properties.put(PropertyKeyConst.SERVER_ADDR, nacosProp.getUrl());
properties.put(PropertyKeyConst.NAMESPACE, nacosProp.getNamespace());
}
return NacosFactory.createConfigService(properties);
}
public void onSelectorChanged(final List changed, final DataEventTypeEnum eventType) {
updateSelectorMap(getConfig(NacosPathConstants.SELECTOR_DATA_ID));
.。。。
publishConfig(NacosPathConstants.SELECTOR_DATA_ID, SELECTOR_MAP);
}
soul-bootstrap 添加了nacos依赖soul-spring-boot-starter-sync-data-nacos
,启动时自动注入类 NacosSyncDataConfiguration
在初始化bean是创建了 NacosSyncDataService
用于监听配置数据的变化并进行同步。
public SyncDataService nacosSyncDataService(final ObjectProvider configService, final ObjectProvider pluginSubscriber,
final ObjectProvider> metaSubscribers, final ObjectProvider> authSubscribers) {
log.info("you use nacos sync soul data.......");
return new NacosSyncDataService(configService.getIfAvailable(), pluginSubscriber.getIfAvailable(),
metaSubscribers.getIfAvailable(Collections::emptyList), authSubscribers.getIfAvailable(Collections::emptyList));
}
数据同步原理目前看都是类似的,下一章计划深入分析 admin 和 bootstrap的整个过程,验证下。