一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第26天,点击查看活动详情
图解+源讲解 Nacos 服务端处理下线请求
人的才华就如海绵的水,没有外力的挤压,它是绝对流不出来的。流出来后,海绵才能吸收新的源泉 Nacos 源码分析系列相关文章
- 从零开始看 Nacos 源码环境搭建
- 图解+源码讲解 Nacos 客户端发起注册流程
- 图解+源码讲解 Nacos 服务端处理注册请求逻辑
- 图解+源码讲解 Nacos 客户端下线流程
- 图解+源码讲解 Nacos 服务端处理下线请求
- 图解+源码讲解 Nacos 客户端发起心跳请求
- 图解+源码讲解 Nacos 服务端处理心跳请求
- 图解+源码讲解 Nacos 服务端处理配置获取请求
从哪里开始看起
指定是服务端接收到客户端的请求才进行下线处理的,那么客户端的请求地址是什么呢?客户端下线流程中已经表明了,请求路径就是:/nacos/v1/ns/instance,一看是这个路径那么就知道是哪个controller下面的了,指定是和instance 注册是一个类里面的,果不其然就是这里面的
处理入口 InstanceController#deregister
@CanDistro
@DeleteMapping
@Secured(parser = NamingResourceParser.class, action = ActionTypes.WRITE)
public String deregister(HttpServletRequest request) throws Exception {
// 根据请求获取实例信息
Instance instance = getIpAddress(request);
// 获取命名空间Id信息
String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID,
Constants.DEFAULT_NAMESPACE_ID);
// 获取服务名称
String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME);
NamingUtils.checkServiceNameFormat(serviceName);
//进行服务下线处理
getInstanceOperator().removeInstance(namespaceId, serviceName, instance);
return "ok";
}
获取实例信息
从请求中解析出来下线的实例信息
private Instance getIpAddress(HttpServletRequest request) {
String enabledString = WebUtils.optional(request, "enabled", StringUtils.EMPTY);
boolean enabled;
if (StringUtils.isBlank(enabledString)) {
enabled = BooleanUtils.toBoolean(WebUtils.optional(request, "enable", "true"));
} else {
enabled = BooleanUtils.toBoolean(enabledString);
}
// 权重
String weight = WebUtils.optional(request, "weight", "1");
// 健康状态
boolean healthy = BooleanUtils.toBoolean(
WebUtils.optional(request, "healthy", "true"));
// 获取基础服务实例信息
Instance instance = getBasicIpAddress(request);
// 设置实例权重
instance.setWeight(Double.parseDouble(weight));
// 设置实例的健康状态
instance.setHealthy(healthy);
instance.setEnabled(enabled);
return instance;
}
// 获取基础服务实例信息
private Instance getBasicIpAddress(HttpServletRequest request) {
// IP
final String ip = WebUtils.required(request, "ip");
// 端口号
final String port = WebUtils.required(request, "port");
// 集群名称,这里是 default
String cluster = WebUtils.optional(request, CommonParams.CLUSTER_NAME,
StringUtils.EMPTY);
if (StringUtils.isBlank(cluster)) {
cluster = WebUtils.optional(request, "cluster",
UtilsAndCommons.DEFAULT_CLUSTER_NAME);
}
// 是否是虚拟节点
boolean ephemeral = BooleanUtils.toBoolean(
WebUtils.optional(request, "ephemeral",
String.valueOf(switchDomain.isDefaultInstanceEphemeral())));
// 构建实例信息
Instance instance = new Instance();
// 设置端口号
instance.setPort(Integer.parseInt(port));
// 设置IP地址
instance.setIp(ip);
// 是否是虚拟节点
instance.setEphemeral(ephemeral);
// 设置集群名称
instance.setClusterName(cluster);
//返回实例信息
return instance;
}
服务下线处理 getInstanceOperator().removeInstance
@Override
public void removeInstance(String namespaceId, String serviceName, Instance instance) {
// 是否是临时节点
boolean ephemeral = instance.isEphemeral();
// 获取 clientId 信息
String clientId = IpPortBasedClient.getClientId(instance.toInetAddr(), ephemeral);
if (!clientManager.contains(clientId)) {
Loggers.SRV_LOG.warn("remove instance from non-exist client: {}", clientId);
return;
}
// 获取服务信息
Service service = getService(namespaceId, serviceName, ephemeral);
// 下线实例
clientOperationService.deregisterInstance(service, instance, clientId);
}
下线实例 clientOperationService#deregisterInstance
将当前下线实例信息,从发布者集合中移除出去,之后发布事件告知其他订阅者
@Override
public void deregisterInstance(Service service, Instance instance, String clientId) {
if (!ServiceManager.getInstance().containSingleton(service)) {
Loggers.SRV_LOG.warn("remove instance from non-exist service: {}", service);
return;
}
// 就是从客户端管理器中获取实例出来
Service singleton = ServiceManager.getInstance().getSingleton(service);
Client client = clientManager.getClient(clientId);
// 服务下线的时候从发布者中将实例移除
InstancePublishInfo removedInstance = client.removeServiceInstance(singleton);
// 设置一下客户端更新事件
client.setLastUpdatedTime();
if (null != removedInstance) {
// 发布两个事件,客户端下线事件、实例元数据事件
NotifyCenter.publishEvent(
new ClientOperationEvent.ClientDeregisterServiceEvent(singleton, clientId));
NotifyCenter.publishEvent(
new MetadataEvent.InstanceMetadataEvent(singleton,
removedInstance.getMetadataId(), true));
}
}
小结
其实主要就是做了两件事件,一个是将发布者中的当前下线实例移除掉。之后再告诉其他订阅者一声我下线了,别订阅我了就完事了。
其他系列源码分析
feign 源码分析系列相关文章
- 图解+源码讲解 Feign 如何将客户端注入到容器中
- 图解+源码讲解动态代理获取 FeignClient 代理对象
- 图解+源码讲解代理对象 ReflectiveFeign 分析
- 图解+源码讲解 Feign 如何选取指定服务
- 图解+源码讲解 Feign 请求的流程
ribbon 源码分析系列相关文章 - Ribbon 原理初探
- 图解+源码讲解 Ribbon 如何获取注册中心的实例
- 图解+源码讲解 Ribbon 服务列表更新
- 图解+源码讲解 Ribbon 服务选择原理
- 图解+源码讲解 Ribbon 如何发起网络请求
eureka 源码分析系列相关文章
- eureka-server 项目结构分析
- 图解+源码讲解 Eureka Server 启动流程分析
- 图解+源码讲解 Eureka Client 启动流程分析
- 图解+源码讲解 Eureka Server 注册表缓存逻辑
- 图解+源码讲解 Eureka Client 拉取注册表流程
- 图解+源码讲解 Eureka Client 服务注册流程
- 图解+源码讲解 Eureka Client 心跳机制流程
- 图解+源码讲解 Eureka Client 下线流程分析
- 图解+源码讲解 Eureka Server 服务剔除逻辑
- 图解+源码讲解 Eureka Server 集群注册表同步机制