背景
公司使用nacos-discovery作为服务注册和服务发现,使用nacos-conf作为配置中心,通信的方式使用的是grpc,所以希望关闭http端口,使用grpc端口
技术方案
在application.properties
文件中关闭web配置,即spring.main.web-application-type
设置为 none
,并且添加配置spring.cloud.nacos.discovery.port=XXX
一. 出现问题
启动服务时一切正常,但是发现服务不往nacos上注册,也没有出现任何异常提示,直接调用grpc端口可以正常通信。
二. nacos注册原理和时机
nacos-client主要是通过com.alibaba.cloud.nacos.registry.NacosAutoServiceRegistration
管理注册行为,实际上就是向nacos-server上发送一个POST
请求
该类继承自org.springframework.cloud.client.serviceregistry.AbstractAutoServiceRegistration
,这个类是spirng-cloud对于服务注册的一个通用抽象,其中一个关键的接口是ApplicationListener<WebServerInitializedEvent>
换句话说,服务注册到nacos上的时机是收到spirng中web组件成功实例化后发布的事件后。
那么这就合理了,将web端口关闭后,spring不会去初始化web相关实例,也就不会发布web实例初始化成功的事件,所以nacos不会执行注册的东西,而其他操作毫无影响
三. 解决方案
NacosServiceRegistry
是nacos注册到spring当中的bean,其中的register()
方法也是底层最终实现注册动作的类,所以我们可以通过将该bean注入到我们自己的服务中,并通过手动调用的方式注册到nacos-server上
四. 其他
另有同学说,有没有可能grpc server正在实例化,端口就已经注册到nacos上了,会不会造成短暂的不可用?
spring启动的核心逻辑在org.springframework.context.support.AbstractApplicationContext#refresh
方法中,如下
...
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
···
finishRefresh
方法如下
protected void finishRefresh() {
// Clear context-level resource caches (such as ASM metadata from scanning).
clearResourceCaches();
// Initialize lifecycle processor for this context.
initLifecycleProcessor();
// Propagate refresh to lifecycle processor first.
getLifecycleProcessor().onRefresh();
// Publish the final event.
publishEvent(new ContextRefreshedEvent(this));
// Participate in LiveBeansView MBean, if active.
if (!IN_NATIVE_IMAGE) {
LiveBeansView.registerApplicationContext(this);
}
}
可以很清楚的看到,只有当所有的bean都实例化完成之后才会轮到finishRefresh
,而发布web事件是在getLifecycleProcessor().onRefresh();
,所以只要grpc-server是一个spring的bean,那注册到nacos上的端口就一定可用
结论
spring-cloud环境下,nacos的服务注册是依赖于spring的事件发布的,我们自己的服务也可以利用事件发布在spring初始化后做一些相应的操作