根据SpringBoot的自动装配规则,联想到nacos实现自动注册肯定有一个XXXAutoConfiguration这个类在实现
查看NacosCloud 源码的META-INF
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.alibaba.cloud.nacos.discovery.NacosDiscoveryAutoConfiguration,\
com.alibaba.cloud.nacos.ribbon.RibbonNacosAutoConfiguration,\
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
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
com.alibaba.cloud.nacos.discovery.configclient.NacosDiscoveryClientConfigServiceBootstrapConfiguration
查看META-INF 发现跟自动注册相关和自动配置类NacosDiscoveryAutoConfiguration 和 NacosServiceRegistryAutoConfiguration比较符合我们需求分别查看源码
NacosDiscoveryAutoConfiguration
@Configuration(proxyBeanMethods = false)
@ConditionalOnDiscoveryEnabled
@ConditionalOnNacosDiscoveryEnabled
public class NacosDiscoveryAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public NacosDiscoveryProperties nacosProperties() {
return new NacosDiscoveryProperties();
}
@Bean
@ConditionalOnMissingBean
public NacosServiceDiscovery nacosServiceDiscovery(
NacosDiscoveryProperties discoveryProperties) {
return new NacosServiceDiscovery(discoveryProperties);
}
}
-
该类获取NacosDiscoveryProperties 用于绑定了我们在配置文件中的spring.cloud.nacos.discovery的配置
-
NacosServiceDiscovery 封装了 通过Properties 的配置 底层调用nacos api来创建 获取ServiceInstance这里的ServiceInstance是底层Spring Cloud定义的顶层接口规范
-
NacosServiceDiscovery是封装了获取Spring Cloud 的顶层ServiceInstance ServerName的工具包
/** * Return all instances for the given service. * @param serviceId id of service * @return list of instances * @throws NacosException nacosException */ public List<ServiceInstance> getInstances(String serviceId) throws NacosException { String group = discoveryProperties.getGroup(); List<Instance> instances = discoveryProperties.namingServiceInstance() .selectInstances(serviceId, group, true); return hostToServiceInstanceList(instances, serviceId); }
public NamingService namingServiceInstance() { if (null != namingService) { return namingService; } try { // 底层nacosFacactory 就是nacos 核心 API包中的数据 namingService = NacosFactory.createNamingService(getNacosProperties()); } catch (Exception e) { log.error("create naming service error!properties={},e=,", this, e); return null; } return namingService; }
核心查看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 {
// NacosServiceRegistry implements ServiceRegistry<Registration>
// 实现了顶层Spring Cloud 的 ServiceRegistry接口用来进行服务的注册
// 其中的com.alibaba.cloud.nacos.registry.NacosServiceRegistry#register 方法是真正调用nacos API将服务注册到nacos-server的逻辑
@Bean
public NacosServiceRegistry nacosServiceRegistry(
NacosDiscoveryProperties nacosDiscoveryProperties) {
return new NacosServiceRegistry(nacosDiscoveryProperties);
}
// NacosRegistration implements Registration, ServiceInstance
// 里面包装了注册需要的一些配置信息比如当前节点的ip port 权重 metaData等 和 Spring 上下文绑定做一些初始化操作
@Bean
@ConditionalOnBean(AutoServiceRegistrationProperties.class)
public NacosRegistration nacosRegistration(
NacosDiscoveryProperties nacosDiscoveryProperties,
ApplicationContext context) {
return new NacosRegistration(nacosDiscoveryProperties, context);
}
// 实现自动注册核心类 聚合了registry autoServiceRegistrationProperties registration 注册需要的工具 和 配置属性
@Bean
@ConditionalOnBean(AutoServiceRegistrationProperties.class)
public NacosAutoServiceRegistration nacosAutoServiceRegistration(
NacosServiceRegistry registry,
AutoServiceRegistrationProperties autoServiceRegistrationProperties,
NacosRegistration registration) {
return new NacosAutoServiceRegistration(registry,
autoServiceRegistrationProperties, registration);
}
}
NacosAutoServiceRegistration 类分析
public class NacosAutoServiceRegistration
extends AbstractAutoServiceRegistration<Registration> {
private static final Logger log = LoggerFactory
.getLogger(NacosAutoServiceRegistration.class);
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 NacosRegistration getManagementRegistration() {
return null;
}
@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 void registerManagement() {
if (!this.registration.getNacosDiscoveryProperties().isRegisterEnabled()) {
return;
}
super.registerManagement();
}
@Override
protected Object getConfiguration() {
return this.registration.getNacosDiscoveryProperties();
}
@Override
protected boolean isEnabled() {
return this.registration.getNacosDiscoveryProperties().isRegisterEnabled();
}
@Override
@SuppressWarnings("deprecation")
protected String getAppName() {
String appName = registration.getNacosDiscoveryProperties().getService();
return StringUtils.isEmpty(appName) ? super.getAppName() : appName;
}
}
-
首先NacosAutoServiceRegistration继承了org.springframework.cloud.client.serviceregistry.AbstractAutoServiceRegistration 这个类是SpringCloud 定义的顶层抽象规范
public abstract class AbstractAutoServiceRegistration<R extends Registration> implements AutoServiceRegistration, ApplicationContextAware, ApplicationListener<WebServerInitializedEvent>
-
该抽象类 实现了ApplicationListener 方法在Spring容器发布WebServerInitializedEvent 事件的时候会调用他的onApplicationEvent(WebServerInitializedEvent event)方法 完成服务注册功能
@Override @SuppressWarnings("deprecation") public void onApplicationEvent(WebServerInitializedEvent event) { bind(event); }
-
查看bind里面 拿到Server的启动端口号 和调用了this.start方法 进一步分析start方法
@Deprecated public void bind(WebServerInitializedEvent event) { ApplicationContext context = event.getApplicationContext(); if (context instanceof ConfigurableWebServerApplicationContext) { if ("management".equals(((ConfigurableWebServerApplicationContext) context) .getServerNamespace())) { return; } } this.port.compareAndSet(0, event.getWebServer().getPort()); this.start(); }
-
start方法 核心调用了 register方法实现注册逻辑 然后发布了2个注册相关前后的事件
public void start() { // 判断是否开启 自动注册 if (!isEnabled()) { if (logger.isDebugEnabled()) { logger.debug("Discovery Lifecycle disabled. Not starting"); } return; } // only initialize if nonSecurePort is greater than 0 and it isn't already running // because of containerPortInitializer below 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); } }
-
register方法 底层就调用了registry的register方法
protected void register() { this.serviceRegistry.register(getRegistration()); }
-
这个serverRegistry 是org.springframework.cloud.client.serviceregistry.ServiceRegistry类型是Spring Cloud的底层规范 发现只有一个类实现了该接口NacosServiceRegistry 本质上调用了该类的register方法 真正完成将服务信息注册到nacos-server上
public void register(Registration registration) { if (StringUtils.isEmpty(registration.getServiceId())) { log.warn("No service to register for nacos client..."); return; } String serviceId = registration.getServiceId(); String group = nacosDiscoveryProperties.getGroup(); Instance instance = getNacosInstanceFromRegistration(registration); try { namingService.registerInstance(serviceId, group, instance); log.info("nacos registry, {} {} {}:{} register finished", group, serviceId, instance.getIp(), instance.getPort()); } catch (Exception e) { log.error("nacos registry, {} register failed...{},", serviceId, registration.toString(), e); // rethrow a RuntimeException if the registration is failed. // issue : https://github.com/alibaba/spring-cloud-alibaba/issues/1132 rethrowRuntimeException(e); } }
-
-
现在回到NacosAutoServiceRegistration 类 AbstractAutoServiceRegistration是抽象类最终是调用到了NacosAutoServiceRegistration .register 方法
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(); }
-
NacosAutoServiceRegistration 最核心的就是调用配super.register(); 调用了父类的方法
protected void register() { this.serviceRegistry.register(getRegistration()); }
-
父类的方法调用了serviceRegistry.register方法 这里的registry 其实就是实例化NacosAutoServiceRegistration 传入构造器的registry
-
底层就来到了com.alibaba.cloud.nacos.registry.NacosServiceRegistry#register方法
-
进一步分析该类的register 方法
@Override public void register(Registration registration) { if (StringUtils.isEmpty(registration.getServiceId())) { log.warn("No service to register for nacos client..."); return; } // 在NacosAutoServiceRegistration 已经聚合registration 获取注册的ServiceInstance的Id String serviceId = registration.getServiceId(); // 获取用户配置的groupId String group = nacosDiscoveryProperties.getGroup(); // 将registration 中的配置信息保存到Instance中封装成SC的标准Instance /** private Instance getNacosInstanceFromRegistration(Registration registration) { Instance instance = new Instance(); instance.setIp(registration.getHost()); instance.setPort(registration.getPort()); instance.setWeight(nacosDiscoveryProperties.getWeight()); instance.setClusterName(nacosDiscoveryProperties.getClusterName()); instance.setMetadata(registration.getMetadata()); return instance; } */ Instance instance = getNacosInstanceFromRegistration(registration); try { // 正在实现注册到nacos上的是调用了namingService的reigsterInstance方法注册到nacos-server namingService.registerInstance(serviceId, group, instance); log.info("nacos registry, {} {} {}:{} register finished", group, serviceId, instance.getIp(), instance.getPort()); } catch (Exception e) { log.error("nacos registry, {} register failed...{},", serviceId, registration.toString(), e); // rethrow a RuntimeException if the registration is failed. // issue : https://github.com/alibaba/spring-cloud-alibaba/issues/1132 rethrowRuntimeException(e); } }
-
分析namingService.registerInstance方法 namingService已经从Spring Cloud Alibaba Nacos项目走到Nacos Client包中了
public void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException { if (instance.isEphemeral()) { // 心跳检测的逻辑 BeatInfo beatInfo = new BeatInfo(); beatInfo.setServiceName(NamingUtils.getGroupedName(serviceName, groupName)); beatInfo.setIp(instance.getIp()); beatInfo.setPort(instance.getPort()); beatInfo.setCluster(instance.getClusterName()); beatInfo.setWeight(instance.getWeight()); beatInfo.setMetadata(instance.getMetadata()); beatInfo.setScheduled(false); beatInfo.setPeriod(instance.getInstanceHeartBeatInterval()); beatReactor.addBeatInfo(NamingUtils.getGroupedName(serviceName, groupName), beatInfo); } // 注册逻辑 serverProxy.registerService(NamingUtils.getGroupedName(serviceName, groupName), groupName, instance); }
-
serverProxy.registerService 实现注册逻辑
-
进一步分析registerService方法 本质上是封装了各种请求参数 然后发送一个POST请求到nacos-server上完成注册
public void registerService(String serviceName, String groupName, Instance instance) throws NacosException { NAMING_LOGGER.info("[REGISTER-SERVICE] {} registering service {} with instance: {}", namespaceId, serviceName, instance); // 组装各种请求参数 final Map<String, String> params = new HashMap<String, String>(9); params.put(CommonParams.NAMESPACE_ID, namespaceId); params.put(CommonParams.SERVICE_NAME, serviceName); params.put(CommonParams.GROUP_NAME, groupName); params.put(CommonParams.CLUSTER_NAME, 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", JSON.toJSONString(instance.getMetadata())); // 本质上发送了一个HTTP 的Post请求到Nacos-server // UtilAndComs.NACOS_URL_INSTANCE = /nacos/v1/ns/instance reqAPI(UtilAndComs.NACOS_URL_INSTANCE, params, HttpMethod.POST); }
-
查看reqAPI底层源码 本质上对所有nacos-server 随机选择一台发送POST请求 调用callServer方法来发送请求 即可完整服务自动注册
public String reqAPI(String api, Map<String, String> params, String body, List<String> servers, String method) throws NacosException { params.put(CommonParams.NAMESPACE_ID, getNamespaceId()); if (CollectionUtils.isEmpty(servers) && StringUtils.isEmpty(nacosDomain)) { throw new NacosException(NacosException.INVALID_PARAM, "no server available"); } NacosException exception = new NacosException(); if (servers != null && !servers.isEmpty()) { Random random = new Random(System.currentTimeMillis()); int index = random.nextInt(servers.size()); for (int i = 0; i < servers.size(); i++) { String server = servers.get(index); try { return callServer(api, params, body, server, method); } catch (NacosException e) { exception = e; if (NAMING_LOGGER.isDebugEnabled()) { NAMING_LOGGER.debug("request {} failed.", server, e); } } index = (index + 1) % servers.size(); } } if (StringUtils.isNotBlank(nacosDomain)) { for (int i = 0; i < UtilAndComs.REQUEST_DOMAIN_RETRY_COUNT; i++) { try { return callServer(api, params, body, nacosDomain, method); } catch (NacosException e) { exception = e; if (NAMING_LOGGER.isDebugEnabled()) { NAMING_LOGGER.debug("request {} failed.", nacosDomain, e); } } } } NAMING_LOGGER.error("request: {} failed, servers: {}, code: {}, msg: {}", api, servers, exception.getErrCode(), exception.getErrMsg()); throw new NacosException(exception.getErrCode(), "failed to req API:/api/" + api + " after all servers(" + servers + ") tried: " + exception.getMessage()); }