当前位置:网站首页>【七夕快乐篇】Nacos是如何实现服务注册功能的?
【七夕快乐篇】Nacos是如何实现服务注册功能的?
2022-08-04 23:08:00 【步尔斯特】
今天是一个美好的日子,祝大家七夕快乐。
很多订阅 《微服务核心技术》 专栏的读者在后台私信说:看Nacos源码时没有思路,面试中还总被问到一些细节。
那么接下的几天里,我们就来逐步分析一下Nacos的源码以及Nacos的核心功能与机制,并着手写一个注册中心,来帮助大家更好的了解分布式中间件。
大家都知道Nacos有两大模块:注册中心和配置中心。
那么Nacos是如何实现注册中心的服务注册的功能呢?我们来一探究竟。
在SpringBoot的基底下,每当我们引入一个新的适配组件,理应看一下该组件下的/META-INF/spring.factories文件,上一篇文章《注解@EnableAutoConfiguration的作用以及如何使用》提到,@SpringBootApplication会自动加载/META-INF/spring.factories文件。

跟进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 {
...
...
...
// 服务注册核心bean
@Bean
@ConditionalOnBean(AutoServiceRegistrationProperties.class)
public NacosAutoServiceRegistration nacosAutoServiceRegistration(
NacosServiceRegistry registry,
AutoServiceRegistrationProperties autoServiceRegistrationProperties,
NacosRegistration registration) {
return new NacosAutoServiceRegistration(registry,
autoServiceRegistrationProperties, registration);
}
}
跟进NacosAutoServiceRegistration
public NacosAutoServiceRegistration(ServiceRegistry<Registration> serviceRegistry,
AutoServiceRegistrationProperties autoServiceRegistrationProperties,
NacosRegistration registration) {
super(serviceRegistry, autoServiceRegistrationProperties);
this.registration = registration;
}
跟进super【AbstractAutoServiceRegistration】
protected AbstractAutoServiceRegistration(ServiceRegistry<R> serviceRegistry,
AutoServiceRegistrationProperties properties) {
this.serviceRegistry = serviceRegistry;
this.properties = properties;
}
在这个类下,有个监听的方法,这个就是服务注册的核心方法了。
@Override
@SuppressWarnings("deprecation")
public void onApplicationEvent(WebServerInitializedEvent event) {
// 服务注册的核心方法
bind(event);
}
那么有监听的事件,就应该有发布的事件,那么事件是在哪里发布的呢?
事件是在WebServerStartStopLifecycle#start时发布的
spring容器启动过程中核心方法:finishRefresh(),让我们看一看这个方法,算了,我还是专门写一篇吧…移步《Spring源码之finishRefresh()》
SpringApplication#run启动过程中核心方法:
finishRefresh()getLifecycleProcessor().onRefresh();
WebServerStartStopLifecycle(实现SmartLifecycle接口)会发布 ServletWebServerInitializedEvent事件。
NacosAutoServiceRegistration的onApplicationEvent方法处理WebServerInitializedEvent事件。
@Override
public void start() {
this.webServer.start();
this.running = true;
this.applicationContext
.publishEvent(new ServletWebServerInitializedEvent(this.webServer, this.applicationContext));
}
知道了它是如何发布的,我们来看一下AbstractAutoServiceRegistration#bind
@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();
}
跟进this.start()

跟进register(),几经辗转,我们来到NacosServiceRegistry#register

跟进namingService.registerInstance(serviceId, group, instance);,我们来到NacosNamingService#registerInstance,我们重点关注一下

跟进NamingProxy#registerService,组装客户端信息向服务端发送请求。
由此可见,服务注册的请求是POST,请求路径是/nacos/v1/ns/instance
public static String webContext = "/nacos";
public static String nacosUrlBase = webContext + "/v1/ns";
public static String nacosUrlInstance = nacosUrlBase + "/instance";
根据上述结论,我们可以找到服务端对应的API接口:InstanceController#register
@CanDistro
@PostMapping
@Secured(action = ActionTypes.WRITE)
public String register(HttpServletRequest request) throws Exception {
final String namespaceId = WebUtils
.optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID);
final String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME);
NamingUtils.checkServiceNameFormat(serviceName);
final Instance instance = HttpRequestInstanceBuilder.newBuilder()
.setDefaultInstanceEphemeral(switchDomain.isDefaultInstanceEphemeral()).setRequest(request).build();
// 跟进
getInstanceOperator().registerInstance(namespaceId, serviceName, instance);
NotifyCenter.publishEvent(new RegisterInstanceTraceEvent(System.currentTimeMillis(), "",
false, namespaceId, NamingUtils.getGroupName(serviceName), NamingUtils.getServiceName(serviceName),
instance.getIp(), instance.getPort()));
return "ok";
}
跟进getInstanceOperator().registerInstance(namespaceId, serviceName, instance);
InstanceOperatorServiceImpl#registerInstance
@Override
public void registerInstance(String namespaceId, String serviceName, Instance instance) throws NacosException {
com.alibaba.nacos.naming.core.Instance coreInstance = parseInstance(instance);
// 跟进
serviceManager.registerInstance(namespaceId, serviceName, coreInstance);
}
跟进serviceManager.registerInstance(namespaceId, serviceName, coreInstance);
ServiceManager#registerInstance
public void registerInstance(String namespaceId, String serviceName, Instance instance) throws NacosException {
NamingUtils.checkInstanceIsLegal(instance);
// 跟进
createEmptyService(namespaceId, serviceName, instance.isEphemeral());
Service service = getService(namespaceId, serviceName);
checkServiceIsNull(service, namespaceId, serviceName);
addInstance(namespaceId, serviceName, instance.isEphemeral(), instance);
}
跟进createEmptyService(namespaceId, serviceName, instance.isEphemeral());
最后我们来到ServiceManager#createServiceIfAbsent
public void createServiceIfAbsent(String namespaceId, String serviceName, boolean local, Cluster cluster)
throws NacosException {
Service service = getService(namespaceId, serviceName);
//return if service already exists
if (service != null) {
return;
}
Loggers.SRV_LOG.info("creating empty service {}:{}", namespaceId, serviceName);
service = new Service();
service.setName(serviceName);
service.setNamespaceId(namespaceId);
service.setGroupName(NamingUtils.getGroupName(serviceName));
// now validate the service. if failed, exception will be thrown
service.setLastModifiedMillis(System.currentTimeMillis());
service.recalculateChecksum();
if (cluster != null) {
cluster.setService(service);
service.getClusterMap().put(cluster.getName(), cluster);
}
service.validate();
// 跟进
putServiceAndInit(service);
if (!local) {
addOrReplaceService(service);
}
}
跟进putServiceAndInit(service);
private void putServiceAndInit(Service service) throws NacosException {
// 跟进
putService(service);
service = getService(service.getNamespaceId(), service.getName());
service.init();
consistencyService
.listen(KeyBuilder.buildInstanceListKey(service.getNamespaceId(), service.getName(), true), service);
consistencyService
.listen(KeyBuilder.buildInstanceListKey(service.getNamespaceId(), service.getName(), false), service);
Loggers.SRV_LOG.info("[NEW-SERVICE] {}", service.toJson());
}
跟进putService(service);
public void putService(Service service) {
if (!serviceMap.containsKey(service.getNamespaceId())) {
// 将客户端信息存入服务端内存中
serviceMap.putIfAbsent(service.getNamespaceId(), new ConcurrentSkipListMap<>());
}
serviceMap.get(service.getNamespaceId()).putIfAbsent(service.getName(), service);
}
再看一眼这个serviceMap的定义
/** * Map(namespace, Map(group::serviceName, Service)). */
private final Map<String, Map<String, Service>> serviceMap = new ConcurrentHashMap<>();
来吧,总结一下吧,大体分为这么几步:
- 在
Spring启动时,先发布Nacos服务注册的事件 - 实例化
Nacos服务注册的核心类NacosAutoServiceRegistration,并监听事件 - 监听到事件之后,对事件进行处理,封装
Nacos客户端信息,并发送API请求到Nacos服务端接口 - 服务端接收到请求之后,将数据存到初始化的
ConcurrentHashMap中,一次完成服务注册
接下来,我们逐步来分析一下Nacos的其他核心机制,并手写一个注册中心,让大家更好的了解这些分布式中间件。
边栏推荐
- 今天又做了三个梦,其中一个梦梦里的我还有意识会思考?
- web3.js
- SSM整合完整流程讲解
- 为何越来越多人选择进入软件测试行业?深度剖析软件测试的优势...
- 基于内容的图像检索系统设计与实现--颜色信息--纹理信息--形状信息--PHASH--SHFT特征点的综合检测项目,包含简易版与完整版的源码及数据!
- typeScript-部分应用函数
- 【内存操作函数内功修炼】memcpy + memmove + memcmp + memset(四)
- [Paper Notes KDD2021] MixGCF: An Improved Training Method for Graph Neural Network-based Recommender Systems
- 轮播图动态渲染
- Web安全开发 | 青训营笔记
猜你喜欢
随机推荐
xss总结
the warmest home
【游戏建模模型制作全流程】ZBrush蜥蜴模型雕刻教程
go语言的日志实现(打印日志、日志写入文件、日志切割)
Service Mesh落地路径
文献阅读十——Detect Rumors on Twitter by Promoting Information Campaigns with Generative Adversarial Learn
【内存操作函数内功修炼】memcpy + memmove + memcmp + memset(四)
亿流量大考(3):不加机器,如何抗住每天百亿级高并发流量?
【3D建模制作技巧分享】ZBrush纹理贴图怎么导入
为何越来越多人选择进入软件测试行业?深度剖析软件测试的优势...
956. 最高的广告牌
中国的顶级黑客在国际上是一个什么样的水平?
360市值四年蒸发3900亿,政企安全能救命吗?
typeScript-闭包函数的使用
历史上的今天:PHP公开发布;iPhone 4 问世;万维网之父诞生
Latex fast insert author ORCID
Linear DP (bottom)
【项目实战】仿照Room实现简单管理系统
自从新来了个字节20K出来的,就见识到了什么是天花板
Service Mesh落地路径

![[Mock Interview - 10 Years of Work] Are more projects an advantage?](/img/fa/2652629d1ff4653aca0d626ac89bf8.jpg)







