返回

图解+源讲解:Nacos 服务端处理下线请求

后端

Nacos 服务治理:服务下线请求的处理

在分布式系统中,服务下线是确保服务可用性和弹性的关键环节。Nacos 作为一款领先的服务发现和治理平台,在处理服务下线请求时有着高效、可靠的机制。本文将深入探讨 Nacos 服务端在处理服务下线请求时的流程和实现细节。

服务下线流程

当服务提供者不再提供服务时,它需要通知 Nacos 服务端下线。下线请求处理流程如下:

  1. 验证请求合法性: Nacos 验证请求格式、参数是否合法。
  2. 查找服务实例: 根据服务名称和实例 ID 查找对应的服务实例。
  3. 标记服务实例为下线: 将找到的实例标记为下线状态,表明不再提供服务。
  4. 通知服务消费者: 通知所有订阅该服务的消费者,告知服务实例已下线。
  5. 清理服务实例: 定期清理下线状态的实例,从服务列表中删除长时间未上线的实例。

源码分析

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);
}
  1. 获取服务名称、命名空间 ID。
  2. 调用 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);
}
  1. 解析请求内容,获取实例 ID。
  2. 调用 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);
}
  1. 构建服务实例对象。
  2. 根据 operation 类型(注册或下线)调用 registerServicederegisterService 方法。
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";
    }
}
  1. 根据 timeoutexpireTime 配置,注册或下线服务。

常见问题解答

1. 为什么服务下线后还会收到请求?

Nacos 服务端收到服务下线请求后,不会立即停止向该服务实例发送请求。原因是 Nacos 在通知所有服务消费者之前,会有一定的延迟时间。在这段时间内,服务实例仍然可以收到请求。

2. 服务下线后,什么时候从 Nacos 中删除?

Nacos 会定期清理下线状态的服务实例。如果一个实例长时间处于下线状态,则会从 Nacos 服务列表中删除。

3. 如何控制服务下线的延迟时间?

可以通过配置 discovery.client.shutdown.delay 参数来控制服务下线后,Nacos 停止向该服务实例发送请求的延迟时间。

4. 如何避免服务下线造成服务中断?

可以使用服务注册表的健康检查机制,定期检测服务实例的状态。当一个实例下线时,健康检查会检测到并将其从服务列表中移除,避免服务中断。

5. 如何处理服务实例异常下线的情况?

当服务实例异常下线时,Nacos 会自动将该实例标记为下线状态。但是,如果实例重新上线,需要手动将其重新注册到 Nacos。