🥇 《Java系核心技术》 《中间件核心技术》
🥇 《微服务核心技术》 《云原生核心技术》
今天是一个美好的日子,祝大家七夕快乐。
很多订阅 《微服务核心技术》 专栏的读者在后台私信说:看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})publicclassNacosServiceRegistryAutoConfiguration{.........// 服务注册核心bean@Bean@ConditionalOnBean(AutoServiceRegistrationProperties.class)public NacosAutoServiceRegistration nacosAutoServiceRegistration(
NacosServiceRegistry registry,
AutoServiceRegistrationProperties autoServiceRegistrationProperties,
NacosRegistration registration){returnnewNacosAutoServiceRegistration(registry,
autoServiceRegistrationProperties, registration);}}
跟进
NacosAutoServiceRegistration
publicNacosAutoServiceRegistration(ServiceRegistry<Registration> serviceRegistry,
AutoServiceRegistrationProperties autoServiceRegistrationProperties,
NacosRegistration registration){super(serviceRegistry, autoServiceRegistrationProperties);this.registration = registration;}
跟进super【
AbstractAutoServiceRegistration
】
protectedAbstractAutoServiceRegistration(ServiceRegistry<R> serviceRegistry,
AutoServiceRegistrationProperties properties){this.serviceRegistry = serviceRegistry;this.properties = properties;}
在这个类下,有个监听的方法,这个就是服务注册的核心方法了。
@Override@SuppressWarnings("deprecation")publicvoidonApplicationEvent(WebServerInitializedEvent event){// 服务注册的核心方法bind(event);}
那么有监听的事件,就应该有发布的事件,那么事件是在哪里发布的呢?
事件是在
WebServerStartStopLifecycle#start
时发布的
spring容器启动过程中核心方法:
finishRefresh()
,让我们看一看这个方法,算了,我还是专门写一篇吧…移步《Spring源码之finishRefresh()》
SpringApplication#run
启动过程中核心方法:
finishRefresh()
getLifecycleProcessor().onRefresh();
WebServerStartStopLifecycle
(实现SmartLifecycle接口)会发布
ServletWebServerInitializedEvent
事件。
NacosAutoServiceRegistration
的
onApplicationEvent
方法处理
WebServerInitializedEvent
事件。
@Overridepublicvoidstart(){this.webServer.start();this.running =true;this.applicationContext
.publishEvent(newServletWebServerInitializedEvent(this.webServer,this.applicationContext));}
知道了它是如何发布的,我们来看一下
AbstractAutoServiceRegistration#bind
@Deprecatedpublicvoidbind(WebServerInitializedEvent event){
ApplicationContext context = event.getApplicationContext();if(context instanceofConfigurableWebServerApplicationContext){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
publicstatic String webContext ="/nacos";publicstatic String nacosUrlBase = webContext +"/v1/ns";publicstatic 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(newRegisterInstanceTraceEvent(System.currentTimeMillis(),"",false, namespaceId, NamingUtils.getGroupName(serviceName), NamingUtils.getServiceName(serviceName),
instance.getIp(), instance.getPort()));return"ok";}
跟进
getInstanceOperator().registerInstance(namespaceId, serviceName, instance);
InstanceOperatorServiceImpl#registerInstance
@OverridepublicvoidregisterInstance(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
publicvoidregisterInstance(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
publicvoidcreateServiceIfAbsent(String namespaceId, String serviceName,boolean local, Cluster cluster)throws NacosException {
Service service =getService(namespaceId, serviceName);//return if service already existsif(service != null){return;}
Loggers.SRV_LOG.info("creating empty service {}:{}", namespaceId, serviceName);
service =newService();
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);
privatevoidputServiceAndInit(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);
publicvoidputService(Service service){if(!serviceMap.containsKey(service.getNamespaceId())){// 将客户端信息存入服务端内存中
serviceMap.putIfAbsent(service.getNamespaceId(),newConcurrentSkipListMap<>());}
serviceMap.get(service.getNamespaceId()).putIfAbsent(service.getName(), service);}
再看一眼这个
serviceMap
的定义
/**
* Map(namespace, Map(group::serviceName, Service)).
*/privatefinal Map<String, Map<String, Service>> serviceMap =newConcurrentHashMap<>();
来吧,总结一下吧,大体分为这么几步:
- 在
Spring
启动时,先发布Nacos
服务注册的事件 - 实例化
Nacos
服务注册的核心类NacosAutoServiceRegistration
,并监听事件 - 监听到事件之后,对事件进行处理,封装
Nacos
客户端信息,并发送API请求到Nacos
服务端接口 - 服务端接收到请求之后,将数据存到初始化的
ConcurrentHashMap
中,一次完成服务注册
接下来,我们逐步来分析一下Nacos的其他核心机制,并手写一个注册中心,让大家更好的了解这些分布式中间件。
版权归原作者 步尔斯特 所有, 如有侵权,请联系我们删除。