图解+源讲解:Nacos 服务端处理下线请求
2023-10-19 01:12:36
Nacos 服务治理:服务下线请求的处理
在分布式系统中,服务下线是确保服务可用性和弹性的关键环节。Nacos 作为一款领先的服务发现和治理平台,在处理服务下线请求时有着高效、可靠的机制。本文将深入探讨 Nacos 服务端在处理服务下线请求时的流程和实现细节。
服务下线流程
当服务提供者不再提供服务时,它需要通知 Nacos 服务端下线。下线请求处理流程如下:
- 验证请求合法性: Nacos 验证请求格式、参数是否合法。
- 查找服务实例: 根据服务名称和实例 ID 查找对应的服务实例。
- 标记服务实例为下线: 将找到的实例标记为下线状态,表明不再提供服务。
- 通知服务消费者: 通知所有订阅该服务的消费者,告知服务实例已下线。
- 清理服务实例: 定期清理下线状态的实例,从服务列表中删除长时间未上线的实例。
源码分析
Nacos 服务端处理服务下线请求的代码位于 com.alibaba.nacos.server.controller.InstanceController
类中。deregisterInstance
方法是处理服务下线请求的入口,主要步骤如下:
@PostMapping(value = "/{serviceName}/deregisterInstance")
public String deregisterInstance(@PathVariable String serviceName, HttpRequestWrapper request) {
String namespaceId = getNamespaceId(request);
return registerOrDeregisterInstance(namespaceId, serviceName, null, request, NACOS_INSTANCE_DEREGISTER);
}
- 获取服务名称、命名空间 ID。
- 调用
registerOrDeregisterInstance
方法处理服务下线请求。
private String registerOrDeregisterInstance(String namespaceId, String serviceName, String groupName, HttpRequestWrapper request, String operation) {
String content = getPayload(request);
if (Text.isEmpty(content)) {
throw new IllegalArgumentException("the content of request can not be null.");
}
Properties properties = new Properties();
try {
properties.load(new StringReader(content));
} catch (IOException e) {
throw new IllegalArgumentException("failed to parse content of request.", e);
}
String instanceId = properties.getProperty("instanceId");
if (Text.isEmpty(instanceId)) {
throw new IllegalArgumentException("instanceId can not be null.");
}
PropertiesWrapper propertiesWrapper = new PropertiesWrapper(properties);
return registerOrDeregisterInstance(namespaceId, serviceName, groupName, instanceId, propertiesWrapper, operation);
}
- 解析请求内容,获取实例 ID。
- 调用
registerOrDeregisterInstance
方法继续处理请求。
private String registerOrDeregisterInstance(String namespaceId, String serviceName, String groupName, String instanceId, PropertiesWrapper propertiesWrapper, String operation) {
Instance instance = getInstanceFromRequest(serviceName, groupName, instanceId, propertiesWrapper);
boolean ephemeral = false;
if (propertiesWrapper.containsKey(Constants.EPHEMERAL)) {
ephemeral = Boolean.parseBoolean(propertiesWrapper.getProperty(Constants.EPHEMERAL));
}
return registerOrDeregisterInstance(namespaceId, serviceName, groupName, instance, operation, ephemeral);
}
- 构建服务实例对象。
- 根据
operation
类型(注册或下线)调用registerService
或deregisterService
方法。
private String registerOrDeregisterInstance(String namespaceId, String serviceName, String groupName, Instance instance, String operation, boolean ephemeral) {
long timeout = getLongProperty(namespaceId, serviceName, groupName, instance.getInstanceId(), Constants.REGISTER_TIMEOUT, Constants.DEFAULT_REGISTER_TIMEOUT);
long expireTime = getLongProperty(namespaceId, serviceName, groupName, instance.getInstanceId(), Constants.REGISTER_EXPIRE_TIMEOUT, Constants.DEFAULT_REGISTER_EXPIRE_TIMEOUT);
boolean success;
if (NACOS_INSTANCE_REGISTER.equals(operation)) {
success = registerService(namespaceId, serviceName, groupName, instance, timeout, expireTime, ephemeral);
} else if (NACOS_INSTANCE_DEREGISTER.equals(operation)) {
success = deregisterService(namespaceId, serviceName, groupName, instance, ephemeral);
} else {
throw new IllegalArgumentException("the operation is invalid, operation=" + operation);
}
if (success) {
return "ok";
} else {
return "failed";
}
}
- 根据
timeout
和expireTime
配置,注册或下线服务。
常见问题解答
1. 为什么服务下线后还会收到请求?
Nacos 服务端收到服务下线请求后,不会立即停止向该服务实例发送请求。原因是 Nacos 在通知所有服务消费者之前,会有一定的延迟时间。在这段时间内,服务实例仍然可以收到请求。
2. 服务下线后,什么时候从 Nacos 中删除?
Nacos 会定期清理下线状态的服务实例。如果一个实例长时间处于下线状态,则会从 Nacos 服务列表中删除。
3. 如何控制服务下线的延迟时间?
可以通过配置 discovery.client.shutdown.delay
参数来控制服务下线后,Nacos 停止向该服务实例发送请求的延迟时间。
4. 如何避免服务下线造成服务中断?
可以使用服务注册表的健康检查机制,定期检测服务实例的状态。当一个实例下线时,健康检查会检测到并将其从服务列表中移除,避免服务中断。
5. 如何处理服务实例异常下线的情况?
当服务实例异常下线时,Nacos 会自动将该实例标记为下线状态。但是,如果实例重新上线,需要手动将其重新注册到 Nacos。