目录
核心类(NacosAutoServiceRegistration)解析
本篇开始我们正式讲解客户端源码
本篇学习目标:通过本篇学习我们要知道 nacos 服务注册是如何集成到springcloud, 什么时候触发服务注册的,服务注册的主流程是什么样的,服务注册又有哪些核心类。
加载自动装配类
spring boot 在启动后扫描【spring-cloud-alibaba-nacos-discovery】jar包 下的spring.factory文件。
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.alibaba.nacos.NacosDiscoveryAutoConfiguration,\
org.springframework.cloud.alibaba.nacos.ribbon.RibbonNacosAutoConfiguration,\
org.springframework.cloud.alibaba.nacos.endpoint.NacosDiscoveryEndpointAutoConfiguration,\
org.springframework.cloud.alibaba.nacos.discovery.NacosDiscoveryClientAutoConfiguration
其中负责服务注册的自动化配置类是配置文件中的第一个 org.springframework.cloud.alibaba.nacos.NacosDiscoveryAutoConfiguration
该类会创建NacosServiceRegistry
NacosRegistration
NacosAutoServiceRegistration 3个Bean
public class NacosDiscoveryAutoConfiguration {
@Bean
public NacosServiceRegistry nacosServiceRegistry(NacosDiscoveryProperties nacosDiscoveryProperties) {
return new NacosServiceRegistry(nacosDiscoveryProperties);
}
@Bean
@ConditionalOnBean({AutoServiceRegistrationProperties.class})
public NacosRegistration nacosRegistration(NacosDiscoveryProperties nacosDiscoveryProperties, ApplicationContext context) {
return new NacosRegistration(nacosDiscoveryProperties, context);
}
@Bean
@ConditionalOnBean({AutoServiceRegistrationProperties.class})
public NacosAutoServiceRegistration nacosAutoServiceRegistration(NacosServiceRegistry registry, AutoServiceRegistrationProperties autoServiceRegistrationProperties, NacosRegistration registration) {
return new NacosAutoServiceRegistration(registry, autoServiceRegistrationProperties, registration);
}
}
NacosRegistration:封装服务注册的基本信息 包括服务的serviceId、 服务的Ip 和 port 、cluster(所属集群名) 、weight(实例的权重)、isSecure、uri(http[https]://ip:port/)、metaData(实例扩展数据或者元数据)、除了namingService是手动创建的, 其他的这些属性基本上都是通过项目的属性文件配置的
NacosAutoServiceRegistration继承自AbstractAutoServiceRegistration : 服务启动后父类会监听web server 初始化事件从而触发当前服务实例的注册流程
NacosServiceRegistry 负责服务的注册和下线功能
服务注册触发时机
NacosAutoServiceRegistration 的父类AbstractAutoServiceRegistration监听了WebServer 初始化事件,当监听到web server 初始化事件时执行 bind(WebServerInitializedEvent event) 方法
服务启动事件触发方法:bind(WebServerInitializedEvent event):
public class AbstractAutoServiceRegistration{
//web服务器在初始化时唤醒该方法
public void bind(WebServerInitializedEvent event) {
ApplicationContext context = event.getApplicationContext();
...
//如果当前的端口号是0就用服务启动的端口比如8080
this.port.compareAndSet(0, event.getWebServer().getPort());
//调用启动发放
this.start();
}
public void start() {
// 如果服务如果已注册过则跳过
if (!this.running.get()) {
//发布注册前事件
this.context.publishEvent(
new InstancePreRegisteredEvent(this, getRegistration()));
// 服务注册入口
register();
if (shouldRegisterManagement()) {
registerManagement();
}
//发布注册后事件
this.context.publishEvent(
new InstanceRegisteredEvent<>(this, getConfiguration()));
//设置服务注册状态为已注册
this.running.compareAndSet(false, true);
}
}
// 服务注册接着调用NacosServiceRegistry 的注册方法
protected void register() {
this.serviceRegistry.register(getRegistration());
}
}
WebServerInitializedEvent—>Web服务器已初始化事件.当Web服务器初始化完毕后会通知此事件,上面的代码很简单根据一个cas变量判断当前服务是否完成服务的注册
另外在服务注册前后会发布2个事件提供扩展能力。
通过代码可以看出服务注册内部调用的serviceRegistry.registry方法.
这里的serviceRegistry 就是我们上面自动装配类里面的NacosServiceRegistry类,提供了服务注册和下线的统一操作入口,这也是springcloud提供的接口,nacos实现了自己的NacosRegistration,下面的内容会讲解具体代码。
服务注册核心流程
核心类(NacosAutoServiceRegistration)解析
//服务启动后该类监听web server 初始化事件触发 服务注册流程
public class NacosAutoServiceRegistration
extends AbstractAutoServiceRegistration<Registration> {
//包装了服务注册基本信息[注册信息其属性大部分来源于配置文件]
private NacosRegistration registration;
public NacosAutoServiceRegistration(ServiceRegistry<Registration> serviceRegistry,
AutoServiceRegistrationProperties autoServiceRegistrationProperties,
NacosRegistration registration) {
super(serviceRegistry, autoServiceRegistrationProperties);
this.registration = registration;
}
@Deprecated
public void setPort(int port) {
getPort().set(port);
}
//如果属性文件配置的端口不合法重新设置端口
@Override
protected NacosRegistration getRegistration() {
if (this.registration.getPort() < 0 && this.getPort().get() > 0) {
this.registration.setPort(this.getPort().get());
}
Assert.isTrue(this.registration.getPort() > 0, "service.port has not been set");
return this.registration;
}
...
//调用父方法实现注册
@Override
protected void register() {
if (!this.registration.getNacosDiscoveryProperties().isRegisterEnabled()) {
log.debug("Registration disabled.");
return;
}
if (this.registration.getPort() < 0) {
this.registration.setPort(getPort().get());
}
super.register();
}
...
// 用户在配置文件中配置的属性
@Override
protected Object getConfiguration() {
return this.registration.getNacosDiscoveryProperties();
}
//是否可以启用服务注册[纯消费者可以设置该属性为false]
@Override
protected boolean isEnabled() {
return this.registration.getNacosDiscoveryProperties().isRegisterEnabled();
}
//首先从[spring.cloud.nacos.discovery.service]获取appName, 如果没有获取到则从
// [spring.application.name]属性获取
@Override
@SuppressWarnings("deprecation")
protected String getAppName() {
String appName = registration.getNacosDiscoveryProperties().getService();
return StringUtils.isEmpty(appName) ? super.getAppName() : appName;
}
}
核心类(NacosServiceRegistry)解析
//实现ServiceRegistry接口的服务注册 spring正是通过该接口为nacos的注册服务提供了可能
public class NacosServiceRegistry implements ServiceRegistry<Registration> {
...
//nacos 配置属性
private final NacosDiscoveryProperties nacosDiscoveryProperties;
//可以理解为 nacos 客户端统一的服务管理器
private final NamingService namingService;
...
@Override
public void register(Registration registration) {
if (StringUtils.isEmpty(registration.getServiceId())) {
log.warn("No service to register for nacos client...");
return;
}
//如果没有配置属性【spring.cloud.nacos.discovery.service】
//就使用【spring.application.name】
String serviceId = registration.getServiceId();
//创建要注册的实例
Instance instance = new Instance();
instance.setIp(registration.getHost());
instance.setPort(registration.getPort());
instance.setWeight(nacosDiscoveryProperties.getWeight());
instance.setClusterName(nacosDiscoveryProperties.getClusterName());
//扩展数据
instance.setMetadata(registration.getMetadata());
...
//调用namingService注册服务
namingService.registerInstance(serviceId, instance);
log.info("nacos registry, {} {}:{} register finished", serviceId,
instance.getIp(), instance.getPort());
...
}
//服务下线功能
@Override
public void deregister(Registration registration) {
...
NamingService namingService = nacosDiscoveryProperties.namingServiceInstance();
String serviceId = registration.getServiceId();
...
namingService.deregisterInstance(serviceId, registration.getHost(),
registration.getPort(), nacosDiscoveryProperties.getClusterName());
...
}
@Override
public void close() {
}
@Override
public void setStatus(Registration registration, String status) {
// nacos doesn't support set status of a particular registration.
}
@Override
public <T> T getStatus(Registration registration) {
// nacos doesn't support query status of a particular registration.
return null;
}
}
上面的serverId 一般不用手动配置默认会匀服务名,也可以通过【spring.cloud.nacos.discovery.service】属性配置。
通过代码可以看出首先通过 NacosRegistration 和 nacosDiscoveryProperties[配置文件相关属性] 构建一个Instance 对象 然后调用下一章要详细讲解的 NacosNamingService 统一发起注册和下线功能
注意: NacosRegistration 负责包装 配置属性文件里面的属性 。比spring cloud的 Registration 接口提供了更多的属性比如 registerWeight、Cluster、isRegisterEnabled等。因此你可以理解了 为什么 weight 性和 clusterName 要从 nacosDiscoveryProperties 去获而不是通过 registration 去获取
instance.setWeight(nacosDiscoveryProperties.getWeight());
instance.setClusterName(nacosDiscoveryProperties.getClusterName());欢迎关注公众号【奇点架构】
Instance: 注册的实例信息 字段如下类图:
- Instance:有几个重要的属性分别是: ip、端口、权重(不能小于1)、集群名、扩展信息
- nacos: 的数据模型从上到下 namespace -> service -> group -> cluster -> instance
- namespace : 待注册实例所属的空间一般可以表示不同的环境 默认是PUBLIC
- service:注册服务名
- group: 待注册实例所属分组 默认是:DEFAULT_GROUP
- cluster: 待注册实例所属集群 不是必须的
总结
Nacos 之所以可以作为独立的第三方注册中心集成到spring cloud 我觉得有下面几点原因
1、spring cloud提供下面3个接口或抽象类作为扩展点
class AbstractAutoServiceRegistration<R extends Registration>
Interface ServiceRegistry
Interface Registration
所以nacos的第一个启动的自动装配类【NacosDiscoveryAutoConfiguration】就是 创建基于上面3个接口的实现类。
2、spring boog 通过spring.factories文件配置帮助提供了服务的扩展能力
本文我们讲解了3个核心类 NacosAutoServiceRegistration 和 NacosServiceRegistry
NacosRegistration类么有专门讲解因为它注意是对nacos 配置属性的封装,并没有太多的业务功能。
后面接下来的注册流程就完全进入到了nacos自己的实现中。