暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

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

老吕架构 2021-04-28
953

背景

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

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

解决思路

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

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

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

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

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

架构图

原理

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);
        }


        文章转载自老吕架构,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

        评论