《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 })
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的其他核心机制,并手写一个注册中心,让大家更好的了解这些分布式中间件。