nacos服务注册源码分析,分为nacos客户端和服务端代码,下面先看客户端代码的简单分析
spring-cloud-starter-alibaba-nacos-discovery该starter下面spring.factories文件的内容如下:
org.springframework.boot.autoconfigure.EnableAutoConfiguration= com.alibaba.cloud.nacos.discovery.NacosDiscoveryAutoConfiguration, com.alibaba.cloud.nacos.endpoint.NacosDiscoveryEndpointAutoConfiguration, com.alibaba.cloud.nacos.registry.NacosServiceRegistryAutoConfiguration, com.alibaba.cloud.nacos.discovery.NacosDiscoveryClientConfiguration, com.alibaba.cloud.nacos.discovery.reactive.NacosReactiveDiscoveryClientConfiguration, com.alibaba.cloud.nacos.discovery.configclient.NacosConfigServerAutoConfiguration, com.alibaba.cloud.nacos.NacosServiceAutoConfiguration org.springframework.cloud.bootstrap.BootstrapConfiguration= com.alibaba.cloud.nacos.discovery.configclient.NacosDiscoveryClientConfigServiceBootstrapConfiguration客户端服务注册流程
根据这个配置,我们可以知道引入该starter之后会自动注入那些类,根据名称推测服务注册的配置信息在com.alibaba.cloud.nacos.registry.NacosServiceRegistryAutoConfiguration这个类中,
@Configuration(
proxyBeanMethods = false
)
@EnableConfigurationProperties
@ConditionalOnNacosDiscoveryEnabled
@ConditionalOnProperty(
value = {"spring.cloud.service-registry.auto-registration.enabled"},
matchIfMissing = true
)
@AutoConfigureAfter({AutoServiceRegistrationConfiguration.class, AutoServiceRegistrationAutoConfiguration.class, NacosDiscoveryAutoConfiguration.class})
public class NacosServiceRegistryAutoConfiguration {
public NacosServiceRegistryAutoConfiguration() {
}
@Bean
public NacosServiceRegistry nacosServiceRegistry(NacosDiscoveryProperties nacosDiscoveryProperties) {
return new NacosServiceRegistry(nacosDiscoveryProperties);
}
@Bean
@ConditionalOnBean({AutoServiceRegistrationProperties.class})
public NacosRegistration nacosRegistration(ObjectProvider> registrationCustomizers, NacosDiscoveryProperties nacosDiscoveryProperties, ApplicationContext context) {
return new NacosRegistration((List)registrationCustomizers.getIfAvailable(), nacosDiscoveryProperties, context);
}
@Bean
@ConditionalOnBean({AutoServiceRegistrationProperties.class})
public NacosAutoServiceRegistration nacosAutoServiceRegistration(NacosServiceRegistry registry, AutoServiceRegistrationProperties autoServiceRegistrationProperties, NacosRegistration registration) {
return new NacosAutoServiceRegistration(registry, autoServiceRegistrationProperties, registration);
}
}
这里主要是注入了三个bean实例,我们重点关注NacosServiceRegistry,而该类实现了org.springframework.cloud.client.serviceregistry.ServiceRegistry这个接口:
public interface ServiceRegistry{ void register(R var1); void deregister(R var1); void close(); void setStatus(R var1, String var2); T getStatus(R var1); }
该接口主要定义了服务注册相关的规范,我们主要关注NacosServiceRegistry中register方法实现:
public void register(Registration registration) {
if (StringUtils.isEmpty(registration.getServiceId())) {
log.warn("No service to register for nacos client...");
} else {
NamingService namingService = this.namingService();
String serviceId = registration.getServiceId();
String group = this.nacosDiscoveryProperties.getGroup();
Instance instance = this.getNacosInstanceFromRegistration(registration);
try {
// 服务注册
namingService.registerInstance(serviceId, group, instance);
log.info("nacos registry, {} {} {}:{} register finished", new Object[]{group, serviceId, instance.getIp(), instance.getPort()});
} catch (Exception var7) {
log.error("nacos registry, {} register failed...{},", new Object[]{serviceId, registration.toString(), var7});
ReflectionUtils.rethrowRuntimeException(var7);
}
}
}
可以发现,服务注册是通过NamingService来完成的:
namingService.registerInstance(serviceId, group, instance);
来到NacosNamingService#registerInstance
public void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException {
NamingUtils.checkInstanceIsLegal(instance);
String groupedServiceName = NamingUtils.getGroupedName(serviceName, groupName);
// 默认为true
if (instance.isEphemeral()) {
// 构建心跳信息
BeatInfo beatInfo = this.beatReactor.buildBeatInfo(groupedServiceName, instance);
// 发送心跳
this.beatReactor.addBeatInfo(groupedServiceName, beatInfo);
}
// 执行服务注册
this.serverProxy.registerService(groupedServiceName, groupName, instance);
}
先看服务注册:
// 执行服务注册
this.serverProxy.registerService(groupedServiceName, groupName, instance);
来到NamingProxy#registerService
public void registerService(String serviceName, String groupName, Instance instance) throws NacosException {
LogUtils.NAMING_LOGGER.info("[REGISTER-SERVICE] {} registering service {} with instance: {}", new Object[]{this.namespaceId, serviceName, instance});
Map params = new HashMap(16);
params.put("namespaceId", this.namespaceId);
params.put("serviceName", serviceName);
params.put("groupName", groupName);
params.put("clusterName", instance.getClusterName());
params.put("ip", instance.getIp());
params.put("port", String.valueOf(instance.getPort()));
params.put("weight", String.valueOf(instance.getWeight()));
params.put("enable", String.valueOf(instance.isEnabled()));
params.put("healthy", String.valueOf(instance.isHealthy()));
params.put("ephemeral", String.valueOf(instance.isEphemeral()));
params.put("metadata", JacksonUtils.toJson(instance.getmetadata()));
this.reqApi(UtilAndComs.nacosUrlInstance, params, "POST");
}
这里跟踪代码会发现,就是通过post请求/nacos/v1/ns/instance进行服务注册的
在来看下发送心跳的细节:
// 发送心跳 this.beatReactor.addBeatInfo(groupedServiceName, beatInfo);
来到BeatReactor#addBeatInfo
public void addBeatInfo(String serviceName, BeatInfo beatInfo) {
LogUtils.NAMING_LOGGER.info("[BEAT] adding beat: {} to beat map.", beatInfo);
String key = this.buildKey(serviceName, beatInfo.getIp(), beatInfo.getPort());
BeatInfo existBeat = null;
if ((existBeat = (BeatInfo)this.dom2Beat.remove(key)) != null) {
existBeat.setStopped(true);
}
this.dom2Beat.put(key, beatInfo);
// 通过线程池来执行发送心跳的任务
this.executorService.schedule(new BeatReactor.BeatTask(beatInfo), beatInfo.getPeriod(), TimeUnit.MILLISECONDS);
MetricsMonitor.getDom2BeatSizeMonitor().set((double)this.dom2Beat.size());
}
我们重点看BeatTask的实现,它是BeatReactor的一个内部类:
class BeatTask implements Runnable {
BeatInfo beatInfo;
public BeatTask(BeatInfo beatInfo) {
this.beatInfo = beatInfo;
}
public void run() {
if (!this.beatInfo.isStopped()) {
long nextTime = this.beatInfo.getPeriod();
try {
JsonNode result = BeatReactor.this.serverProxy.sendBeat(this.beatInfo, BeatReactor.this.lightBeatEnabled);
long interval = result.get("clientBeatInterval").asLong();
boolean lightBeatEnabled = false;
if (result.has("lightBeatEnabled")) {
lightBeatEnabled = result.get("lightBeatEnabled").asBoolean();
}
BeatReactor.this.lightBeatEnabled = lightBeatEnabled;
if (interval > 0L) {
nextTime = interval;
}
int code = 10200;
if (result.has("code")) {
code = result.get("code").asInt();
}
if (code == 20404) {
Instance instance = new Instance();
instance.setPort(this.beatInfo.getPort());
instance.setIp(this.beatInfo.getIp());
instance.setWeight(this.beatInfo.getWeight());
instance.setmetadata(this.beatInfo.getmetadata());
instance.setClusterName(this.beatInfo.getCluster());
instance.setServiceName(this.beatInfo.getServiceName());
instance.setInstanceId(instance.getInstanceId());
instance.setEphemeral(true);
try {
BeatReactor.this.serverProxy.registerService(this.beatInfo.getServiceName(), NamingUtils.getGroupName(this.beatInfo.getServiceName()), instance);
} catch (Exception var10) {
;
}
}
} catch (NacosException var11) {
LogUtils.NAMING_LOGGER.error("[CLIENT-BEAT] failed to send beat: {}, code: {}, msg: {}", new Object[]{JacksonUtils.toJson(this.beatInfo), var11.getErrCode(), var11.getErrMsg()});
}
BeatReactor.this.executorService.schedule(BeatReactor.this.new BeatTask(this.beatInfo), nextTime, TimeUnit.MILLISECONDS);
}
}
}
其逻辑大致如下:
(1)发送心跳给nacos服务器
(2)如果服务返回的clientBeatInterval大于0,则更新调度任务的间隔时间
(3)如果服务返回的code为20404,则发起服务注册请求
(4)重新调度,循环执行(1)(2)(3),直到BeatInfo的stopped属性为true
补充说明:NacosServiceRegistry中register方法的触发时机服务注册的触发时机是WebServerInitializedEvent事件发布时执行的,因为org.springframework.cloud.client.serviceregistry.AbstractAutoServiceRegistration实现了ApplicationListener接口,进而监听了WebServerInitializedEvent事件。而NacosAutoServiceRegistration又继承了AbstractAutoServiceRegistration,所以可以在WebServerInitializedEvent事件发布时执行服务注册。



