Consul注销实例时候的问题

当我们在Spring Cloud应用中使用Consul来实现服务治理时,由于Consul不会自动将不可用的服务实例注销掉(deregister),这使得在实际使用过程中,可能因为一些操作失误、环境变更等原因让Consul中存在一些无效实例信息,而这些实例在Consul中会长期存在,并处于断开状态。它们虽然不会影响到正常的服务消费过程,但是它们会干扰我们的监控,所以我们可以实现一个清理接口,在确认故障实例可以清理的时候进行调用来将这些无效信息清理掉。

开始以为只要简单的调用注销接口就能轻松完成,但是实际实践的发现并非如此。因此,分享一下整个实现过程以及中间遇到的一些坑。

借鉴Spring Cloud Consul

在实现之初,先参考了Spring Cloud Consul在关闭程序时候实现的注销方法,具体如下:

public class ConsulLifecycle extends AbstractDiscoveryLifecycle {
    ...

    private void deregister(String serviceId) {
        if (!this.properties.isRegister()) {
            return;
        }
        if (ttlScheduler != null) {
            ttlScheduler.remove(serviceId);
        }
        log.info("Deregistering service with consul: {}", serviceId);
        client.agentServiceDeregister(serviceId);
    }

    ...
}

我们可以看到,当应用关闭时候的注销操作是通过调用client.agentServiceDeregister(serviceId)来实现的。其中client是consul-api的com.ecwid.consul.v1.ConsulClient实例。而agentServiceDeregister方法则是对/v1/agent/service/deregister/<serviceID> 接口的实现,该接口主要用来从Consul Agent中根据serviceId来注销实例。

以此实现为范例,于是开始的思路是这样的:

  • 先通过consulClient.getHealthServices(serviceId, false, null)根据serviceId来获取服务实例清单
  • 遍历实例清单中有不是PASSING状态的实例,就调用client.agentServiceDeregister(serviceId)来剔除

具体实现如下:

@RestController
public class ApiController {

    @Autowired
    private ConsulClient consulClient;

    @RequestMapping(value = "/unregister/{id}", method = RequestMethod.POST)
    public String unregisterServiceAll(@PathVariable String id) {
        List<HealthService> response = consulClient.getHealthServices(id, false, null).getValue();
        for(HealthService service : response) {
            service.getChecks().forEach(check -> {
                if(!check.getStatus().name().equals(Check.CheckStatus.PASSING.name())) {
                    logger.info("unregister : {}", check.getServiceId());
                    consulClient.agentServiceDeregister(check.getServiceId());
                }
            });
        }
        return null;
    }
}

但是,在测试后发现该方法只能剔除同一个agent上的非PASSING实例。

Catalog误区

继续搜索了一下Consul的文档,发现了这个接口:/v1/catalog/deregister : Deregisters a node, service, or check。于是,尝试了用该接口来替换之前的consulClient.agentServiceDeregister(check.getServiceId());实现。

CatalogDeregistration catalogDeregistration = new CatalogDeregistration();
catalogDeregistration.setDatacenter("dc1");
catalogDeregistration.setNode(check.getNode());
catalogDeregistration.setServiceId(check.getServiceId());
catalogDeregistration.setCheckId(check.getCheckId());
consulClient.catalogDeregister(catalogDeregistration);

经过测试,该方法可以实现短暂的剔除,但是过一段时间之后这些被剔除的实例又都恢复回来了……也就是说这个接口完全没有什么卵用!

那么为什么会出现这种情况呢?我们可以在Github中找到这个维持了一年多的问题讨论:https://github.com/hashicorp/consul/issues/1188 。整个讨论过程非常曲折,虽然当前该问题还依然在open状态,但是一些回复也基本够我们去理解它的原因了。比如下面这条评论:

You cannot deregister a service from the agent on a different node, service only exists on the agent you have registered with. It also exists in the catalog on all nodes, but that is not related to the agent itself. And to be honest I don't understand why there is a catalog/deregister endpoint at all, in my opinion catalog should be a read-only service list.

从该评论中,我们可以知道一个重要信息:服务实例只能在注册的Agent上进行注销!另外,对于/v1/catalog/deregister接口,目前还是有不少争议的,因为根本没啥用。

最终实现

既然服务实例只能在注册的Agent上进行注销,那么我们的实现完全可以按照该思路来实现,方法很简单,只需要对一开始实现的内容做一些调整,依然使用client.agentServiceDeregister(serviceId)方法,只是我们需要调整client连接的agent必须是serviceId注册的agent。所以,最终的修改结果如下:

List<HealthService> response = consulClient.getHealthServices(id, false, null).getValue();
for(HealthService service : response) {
    // 创建一个用来剔除无效实例的ConsulClient,连接到无效实例注册的agent
    ConsulClient clearClient = new ConsulClient(service.getNode().getAddress(), 8500);
    service.getChecks().forEach(check -> {
        if(check.getStatus() != Check.CheckStatus.PASSING) {
            logger.info("unregister : {}", check.getServiceId());
            clearClient.agentServiceDeregister(check.getServiceId());
        }
    });
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Consul中,当服务或节点失效时,Consul不会自动将其注销。相反,它会将其状态标记为不可用。如果你担心失效的服务或节点会对监控产生影响,你可以使用Consul的HTTP API来注销这些不健康的服务。你可以使用以下API来注销节点和服务: - 注销任意节点和服务:/catalog/deregister - 注销当前节点的服务:/agent/service/deregister/:service_id 此外,如果某个节点不再使用,你可以在该节点上使用"consul leave"命令,或者在其他节点上使用"consul force-leave 节点ID"命令来注销该节点。 在Spring Cloud应用中使用Consul实现服务治理时,Consul不会自动注销不可用的服务实例。这可能导致Consul中存在一些无效的实例信息。这些无效的实例会长期存在并处于断开状态,有时会影响服务的请求。因此,我们需要手动删除这些无效的服务。你可以使用Consul的HTTP API来注销这些无效的服务。 总结起来,如果你想注销不健康的服务,你可以使用Consul的HTTP API来进行操作。 #### 引用[.reference_title] - *1* *3* [Consul7-注销consul无效服务](https://blog.csdn.net/wj903829182/article/details/100822374)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [springCloud-19 consul 常见问题 节点和服务注销 健康检查与故障转移](https://blog.csdn.net/qq_41169544/article/details/122393155)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值