k8s环境中dubbo故障容器自动隔离功能的实现

本文介绍了企业云服务中遇到的dubbo线程池爆满问题,该问题可能导致数据库性能抖动和容器CPU过载。解决方案包括启用dubbo重试机制和设置线程池监控阈值,当检测到异常时,采取服务隔离、邮件告警和日志记录等措施。此外,还提供了服务隔离的实现代码,包括隔离与恢复事件的处理。

背景

          企业云服务,全是企业客户、代账中介客户,生产环境偶发个别容器dubbo线程池爆满,通过研究发现问题发生时会同时伴随数据库性能抖动、容器CPU爆满,通过和业务系统的开发人员了解到,系统中存在重接口和批量接口的情况,这些接口也往往和客户的数据量大小有关,这些存在隐患的接口并没有得到合理的控制,它们和轻量级接口位于同一个微服务中,就可能相互影响。这些重接口可能造成的问题 1、影响数据库的稳定性;2、影响cpu和内存的稳定性;一旦产生影响,往往伴随着轻量级接口不能及时得到响应,dubbo线程池会被逐渐占满,进而报错。

           可能有同学会说,dubbo不是会自动隔离故障节点吗?实际上对于彻底死掉没有心跳的节点确实会自动隔离,但是对于半死不活,心跳还在,但压力很大的节点并不会得到隔离,这就需要我们自己来解决这类刺手问题。

解决思路

           一方面我们启用了dubbo的重试机制,在发生线程池满错误的情况下能自动容错到其它容器,保证用户侧无感知。

           另一方面我们要防患于未然,把危机扼杀在摇篮里,我们对dubbo的线程池活跃线程(active)数量进行了监控,设置了临界值,健康检查线程每间隔30秒进行一次检测,一旦检测到异常,就会触发一系列的防护措施:

1、dubbo服务隔离可以及时引流消费者流量到其它正常的提供者

2、邮件和短信告警可以让架构师和运维人员及时关注问题容器情况,如果无法自愈,需要运维干预下重启容器。

3、cpu、内存和线程栈信息记录日志,有助于业务开发人员解决接口存在的问题

架构图

b5bb2b023ed68d0e8cd985dc03d4b025.png

原理

1、根据dubbo-admin的原理,注册override配置信息到注册中心;

2、消费者通过注册中心收到事件通知,根据规则进行 override  url与provider  url以及 consumer url 合并过滤操作;

服务隔离部分的实现代码

1、隔离与恢复事件

@Autowired
private HealthCheckConfig checkConfig;
@Autowired
private RegistryService registryService;
@Autowired
private ProtocolConfig protocolConfig;


@EventListener
public void startIsolate(DubboThreadPoolHighEvent event) {
    //开启隔离
    log.error("故障节点隔离开始:" + event.getActiveCount());
    disableProvider(true);
    log.error("故障节点隔离成功");
}


@EventListener
public void stopIsolate(DubboThreadPoolNormalEvent event) {
    //关闭隔离
    log.error("故障节点取消隔离开始" + event.getActiveCount());
    disableProvider(false);
    log.error("故障节点取消隔离成功");
}


private void disableProvider(boolean disable) {
    try {
        if (!checkConfig.getHealthCheckIsolateEnable()) {
            log.error("未启用健康检查隔离服务");
            return;
        }
        Map<String, Object> beansWithAnnotation = SpringUtil.getApplicationContext().getBeansWithAnnotation(Service.class);
        //override://192.168.2.100:23881/a.b.c.XxxService?
        // category=configurators&disabled=true&dynamic=false&enabled=true
        String ip = NetUtils.getLocalHost();
        Integer port = protocolConfig.getPort();
        //---
        for (Object value : beansWithAnnotation.values()) {
            Class<?>[] itfs = value.getClass().getInterfaces();
            Class<?> itf = itfs[0];
            URL url = new URL(Constants.OVERRIDE_PROTOCOL, ip, port, itf.getCanonicalName());
            url = url.addParameter("category", "configurators");
            url = url.addParameter("disabled", true);
            url = url.addParameter("dynamic", false);
            url = url.addParameter("enabled", true);
            if (disable) {
                registryService.register(url);
                log.error("register:" + url.toString());
            } else {
                registryService.unregister(url);
                log.error("unregister:" + url.toString());
            }
        }
    }catch (Exception e){
        log.error("isolate error ",e);
    }


}

2、优雅关机事件

@EventListener
public void clearIsolate(AppCloseEvent event) {
    if (event.getIsolateStatus()) {
        log.error("关闭之前取消隔离" + event.getIsolateStatus());
        disableProvider(false);
        log.error("关闭之前取消隔离成功");
        try {
            TimeUnit.SECONDS.sleep(checkConfig.getHealthCheckShutdownHookWaitTime());
        }catch (Exception e){


        }
    }else{
        log.error("--isolate status:false--");
    }
}

3、暴力关机情况的处理

@EventListener
public void initIsolate(AppOpenEvent event){
    log.error("--init isolate status--");
    disableProvider(false);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吕哥架构

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值