一 注册中心整体逻辑
1.1 服务注册
Nacos的注册中心是一个服务注册与发现组件,它包含了NacosServer 服务端(也叫注册中心)和NacosClient客户端两部分组成,NacosServer 是独立的服务,而NacosClient需要集成到每个微服务中。
NacosClient(具体的微服务)在项目初始化的时候,会主动的把自己注册到注册中心(NacosServer)提交自己的服务名,ip,端口等,在 NacosServer会形成一个服务注册列表.
1.2 服务发现
微服务(NacosClient,通常是消费者服务)会定期(默认10s/次)从NacosServer拉取服务注册列表缓存到本地,当提供者服务向消费者微服务发起调用的时候,会根据目标服务的服务名从服务注册列表找到一个或者多个服务实例(目标服务可能做了集群),然后使用负载均衡算法(Ribbon)选择其中一个服务实例,最后向该服务实例发起远程调用。
1.3 服务续约
微服务(NacosClient)采用定时(LeaseRenewalIntervalInSeconds:默认5s)发送“心跳”请求向NacosServer发请求进行服务续约,其实就是定时向 NacosServer发请求报告自己的健康状况,目的是告诉NacosServer自己还活着,不要把自己从服务注册列表中剔除掉,那么当微服务(NacosClient)宕机或因为网络波动未向NacosServer续约(默认3次续约失败),注册中心机会从服务地址清单中剔除该续约失败的服务。
1.4 服务下线
微服务(NacosClient)关闭服务前向注册中心发送下线请求,注册中心(NacosServer)接受到下线请求负责将该服务实例从注册列表剔除
二 nacos服务注册源码解析
2.1 注册逻辑的触发
springcloud定义了一套自动注册的标准,实现了WebServerInitializedEvent事件的监听
WebServerInitializedEvent在容器启动完毕后触发改事件,我们可以看看他在监听到改事件后做了什么
2.2 naocs客户端的注册逻辑
在spring-cloud-common中定义了服务注册的标准,交由各种注册注册中心(Eureka,nacos)自己去实现
NacosServiceRegistry 实现标准接口完成自己内部的注册逻辑
public class NacosServiceRegistry implements ServiceRegistry<Registration> {
@Override
public void register(Registration registration) {
if (StringUtils.isEmpty(registration.getServiceId())) {
log.warn("No service to register for nacos client...");
return;
}
NamingService namingService = namingService();
String serviceId = registration.getServiceId();
String group = nacosDiscoveryProperties.getGroup();
// 封装实例对象,分别从registration和nacosDiscoveryProperties配置获取
Instance instance = getNacosInstanceFromRegistration(registration);
try {
// 通过http向NacosServer发送注册请求
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);
}
}
}
注册实例 代码
com.alibaba.nacos.client.naming.NacosNamingService#registerInstance(java.lang.String, com.alibaba.nacos.api.naming.pojo.Instance)
@Override
public void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException {
NamingUtils.checkInstanceIsLegal(instance);
String groupedServiceName = NamingUtils.getGroupedName(serviceName, groupName);
//判断是否为临时实例
if (instance.isEphemeral()) {
// 封装心跳信息
BeatInfo beatInfo = beatReactor.buildBeatInfo(groupedServiceName, instance);
// 开启心跳续约
beatReactor.addBeatInfo(groupedServiceName, beatInfo);
}
// serverProxy注册
serverProxy.registerService(groupedServiceName, groupName, instance);
}
2.3 服务端源码解析
2.3.1 客户端调用服务端注册接口
com.alibaba.nacos.naming.controllers.InstanceController#register
@CanDistro
@PostMapping
@Secured(parser = NamingResourceParser.class, 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 = parseInstance(request);
// 实现注册逻辑
serviceManager.registerInstance(namespaceId, serviceName, instance);
return "ok";
}
总结:服务端其实拿到服务,然后存到Map里面
三 总结
- 必须注意spring cloud commons对服务注册做了标准接口,nacos对这个标准做了整合以及实现
- nacos客户端其实就是发送请求给服务端,nacos服务端将服务注册并检查客户端的健康状态
- 服务的自动注册其实是利用了Spring的WebServerInitializedEvent事件,最终由NamingService完成服务注册工作