Nacos核心功能点
服务注册:Nacos客户端会通过发送REST请求的方式向Nacos服务端注册自己的服务,发布元数据,比如ip地址、端口等信息。Nacos服务端接收到注册请求后,就会把这些元数据信息存储在一个双层的内存Map中。
服务心跳:在服务注册后,Nacos客户端会维护一个定时心跳来持续通知Nacos服务端,说明服务一直处于可用状态,防止被剔除。默认5s发送一次心跳。
服务健康检查:Nacos服务端会开启一个定时任务用来检查注册服务实例的健康情况,对于超过15s没有收到客户端心跳的实例会将它的healthy属性置为false(客户端服务发现时不会发现),如果某个实例超过30秒没有收到心跳,直接剔除该实例(被剔除的实例如果恢复发送心跳则会重新注册)
服务发现:服务消费者在调用服务提供者的服务时,会发送一个REST请求给Nacos服务端,获取上面注册的服务清单,并且缓存在Nacos客户端本地,同时会在Nacos客户端本地开启一个定时任务定时拉取服务端最新的注册表信息更新到本地缓存
服务同步:Nacos服务端集群之间会互相同步服务实例,用来保证服务信息的一致性。
源码入口
熟悉SpringBoot源码的同学们应该知道,SpringBoot启动的时候会自动去找jar包下面的Spring.factories里面的配置并且自动装配成Bean,而Nacos也是借助SpringBoot的自动装配机制完成核心功能,找到spring-cloud-alibaba-nacos-discovery.jar里面的spring.factories文件里的EnableAutoConfiguration对应的NacosDiscoveryAutoConfiguration这个类
在这个类里面会初始化一个叫NacosAutoServiceRegistration的类,而这个NacosAutoServiceRegistration类继承了AbstractAutoServiceRegistration类,然后父类AbstractAutoServiceRegistration实现了ApplicationListener
看过spring源码的应该很熟悉这个类了,实现了这个ApplicationListener这个类,spring容器启动后会回调里面onApplicationEvent
方法,在父类这个方法里面有一个bind(event)方法,跟进去
@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()方法
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); } }
在start方法里面有一个关键的代码,register()方法,跟进源码最后会调用到
@Override 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); } }
里面又调用了namingService.registerInstance(serviceId, group, instance);继续跟进源码
@Override 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); long instanceInterval = instance.getInstanceHeartBeatInterval(); beatInfo.setPeriod(instanceInterval == 0 ? DEFAULT_HEART_BEAT_INTERVAL : instanceInterval); beatReactor.addBeatInfo(NamingUtils.getGroupedName(serviceName, groupName), beatInfo); } serverProxy.registerService(NamingUtils.getGroupedName(serviceName, groupName), groupName, instance); }
在这个方法里面有两个核心的操作,一个发送心跳,一个是客户端注册服务。我们今天只看这个注册服务的主线,发送心跳暂时跳过,后面会详细讲,
serverProxy.registerService(NamingUtils.getGroupedName(serviceName, groupName), groupName, instance);
继续跟进这个主线逻辑里面的代码
首先会封装一些请求的参数信息,然后调用reqAPI方法,最终会调用httpclient发送http请求向服务端注册服务
到这里客户端是如何注册服务到注册中心的完整流程已经结束,下节内容一起来学习服务端是如何将客户端注册的服务存储的