面试官问我:重试依然失败怎么办?

2023年 10月 12日 63.2k 0

重试依然失败怎么办?这个问题曾经一直困扰我,我制定的重试策略是重试N次,重试时间间隔2倍递增,其中最大重试次数、最小重试间隔、最大重试间隔等参数都要求可以动态配置。这个重试策略并不是完美的,我一直困扰于超过最大重试次数依然失败,该怎么办?因为最终失败时我能做的只有 打印异常日志、上报异常消息到公司故障群,稍后人工介入处理异常场景。

从我的经验来看,重试失败的频率并不高,但是每次出现都需要人工介入处理真的很烦人。处理这个问题往往很棘手,需要在线上手动执行一些命令,是比较危险的人肉运维工作。

而且大多数情况下,重试失败通常不是由我的问题引起的,而是因为我的下游系统存在缺陷,导致我的系统处理失败。重试无法解决这类问题。在这种情况下,我只能找到相关的同事,请求他们尽快修复问题。待问题修复完成后,我再手动重试来解决这个异常情况。

我曾经吃过人肉运维的亏,对人肉运维工作深恶痛绝。# 点击看我的悲惨经历 所以在无数次地重复以上工作后,我终于忍无可忍,

人肉运维一时不会出错,但难保一世不会出错。出错就只能自己背锅,出来混,要学会保护自己。我需要设计一个系统,减少人肉运维的工作量。

故障可视化管理

提供可视化页面处理故障,可以有效提高安全性,避免人肉运维工作。解决思路是系统故障发生后,统一上报到故障管理平台。然后故障管理平台提供可视化页面展示故障列表,故障详情。在可视化页面提供修复工具。一般的修复工具是继续重试,如果有需求回滚故障,也可以提供回滚工具。

系统的架构图如下
image.png

系统交互共存在三个角色 业务系统、故障管理后台、故障管理后台页面。一个故障的处理流程是这样的。

当请求一直重试失败超过最大重试次数时,业务系统会上报到故障MQ,关账管理平台消费MQ,收集故障并落库。研发同学收到故障通知,同时在故障管理后台页面可以看到故障列表、故障详情。 排查问题原因、敦促相关同事修复问题后,点击重试按钮。故障管理后台收到重试请求,会通过 Rpc SPI 调用到业务系统 重试故障,并告知管理后台成功和失败结果。

故障管理处理流程

上报故障的处理流程

sequenceDiagram
业务系统 ->> 业务系统: 重试一直失败
业务系统 ->> MQ: 上报故障
MQ ->> 故障管理后台: 收集故障,落库存储
故障管理后台->> IM: 通知到公司故障群

故障重试的处理流程

sequenceDiagram
研发 ->> 后台页面: 浏览故障列表
研发 ->> 研发:  排查问题原因、敦促相关同事修复问题
研发 ->> 后台页面: 点击重试按钮
后台页面 ->> 故障管理后台: 重试故障 Http接口
故障管理后台->> 业务系统: 调用 Rpc SPI 重试故障
业务系统 -->> 故障管理后台: 返回处理结果,成功或失败原因
故障管理后台-->> 后台页面: 返回处理结果
后台页面-->> 研发: 失败继续排查原因。成功则结束

上报故障和重试故障是两个核心流程比较关键的点包括

  • 上报故障使用 MQ 的方式,可以实现业务系统和故障后台的解耦、隔离。不妨碍业务流程。

  • 业务系统需要实现故障重试SPI。业务系统负责重试的业务逻辑

  • 故障管理后台是通用平台,通过故障类型区分各种故障。通过故障类型关联业务系统。重试故障时,根据故障类型查找到对应的业务系统 Rpc Client,并调用SPI方法 重试故障。

  • 需要有前端页面实现故障列表页检索、故障详情页展示、故障重试按钮等功能。这是重中之重的环节,没有可视化页面,故障管理后台就没有存在的意义了。

  • 系统关键实现

    故障收集任务

    CREATE TABLE `fault_collect_task` (
      `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增ID',
      `misId` bigint(20) NOT NULL DEFAULT '0' COMMENT '创建人 ',
       `taskCode` varchar(256) NOT NULL DEFAULT '' COMMENT '故障类型code码,上报时需指定,全局唯一',
      `taskName` varchar(128) NOT NULL DEFAULT '' COMMENT '任务名',
      `taskComment` text comment '任务备注',
      `appkey` varchar(1024) DEFAULT '' COMMENT '业务系统微服务唯一键',
      `port`  int(11) NOT NULL DEFAULT '0' COMMENT '业务系统spi port',
      `status` int(11) NOT NULL DEFAULT '0' COMMENT '状态',
      `ext_config` text COMMENT '扩展字段',
      `op_log` text COMMENT '操作记录',
      `ctime` int(11) NOT NULL DEFAULT '0' COMMENT '创建时间',
      `utime` int(11) NOT NULL DEFAULT '0' COMMENT '更新时间',
      PRIMARY KEY (`id`),
      UNIQUE KEY `uni_idx_id` (`taskId`, `identityId`,`key` )
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='故障收集任务表'
    

    故障收集任务是某一类故障的集合,任务会关联业务系统,上报故障时候,需要指定taskCode,用来标记故障属于哪一个任务。

    故障表

    故障表结构如下

    CREATE TABLE `fault_item` (
     `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增ID',
     `user_id` bigint(20) NOT NULL DEFAULT 0 COMMENT 'userId',
     `biz_id` bigint(20) NOT NULL DEFAULT 0 COMMENT 'userId',
     `unique_key` varchar(256) NOT NULL DEFAULT '' COMMENT '业务定义的唯一键',
     `task_id` bigint(20) NOT NULL DEFAULT 0 COMMENT '故障收集任务id',
     `task_code` varchar(256) NOT NULL DEFAULT '' COMMENT '故障收集任务code码,上报时需指定,全局唯一',
     `context` text COMMENT '故障内容',
     `status` int(11) NOT NULL DEFAULT 0 COMMENT '状态 0 初始化,1. 已重试成功,2 上次重试失败,3 忽略 ',
     `ext_config` text COMMENT '扩展字段, 被索引字段也放在里面',
     `comment` text COMMENT '备注',
    `retry_result` text COMMENT '重试结果',
    `version` int(11) NOT NULL DEFAULT 0 COMMENT 'version',
     `ctime` int(11) NOT NULL DEFAULT 0 COMMENT '创建时间',
     `utime` int(11) NOT NULL DEFAULT 0 COMMENT '更新时间',
    PRIMARY KEY (`id`),
    UNIQUE KEY `uni_idx_taskid_id` (`task_id`, `unique_key`),
    KEY `task_code_ctime_idx` (`task_code`, `ctime`)
    ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT = '故障表';
    
  • taskId是故障收集任务id,task_code是故障任务的编码。
  • context 是故障内容。业务系统自定义故障内容,重试故障时自己解析故障内容。管理后台不限制故障的格式。
  • uniqukeKey 是故障自定义的唯一键,系统使用 taskId+uniqueKey 作为唯一键。当上报的故障重复时,系统会覆盖原有的故障内容。
  • 注册Rpc Client 进入Spring

    业务系统需要实现故障重试的SPI,故障管理后台需要创建Rpc Client的Bean,注册进Spring管理。

    首先构建BeanDefinitionBuilder,需要声明该bean的类型,声明bean的重要参数。 将Spring ApplicationContext 转化为 BeanFactory。调用 registerBeanDefinition 注册bean进上下文。

    ConfigurableApplicationContext configurableApplicationContext = ((ConfigurableApplicationContext) applicationContext);
    DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) configurableApplicationContext.getBeanFactory();
    
    BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(ThriftProxy.class);
    beanDefinitionBuilder.addPropertyReference("mtThriftPoolConfig", "thriftPoolConfig");
    beanDefinitionBuilder.addPropertyValue("timeout", );
    beanDefinitionBuilder.addPropertyValue("appKey", "");
    beanDefinitionBuilder.addPropertyValue("port", );
    
    beanFactory.registerBeanDefinition(config.getBeanName(), beanDefinitionBuilder.getRawBeanDefinition());
    

    总结

    借助于故障管理平台可以避免很多人肉运维工作,可以作为公司通用的平台,提供最大的价值。虽然系统问题出现的频率往往很少,但是修复问题的成本是比较高的。提高修复问题的效率,提高运维的安全性是故障收集平台最大的价值!

    相关文章

    JavaScript2024新功能:Object.groupBy、正则表达式v标志
    PHP trim 函数对多字节字符的使用和限制
    新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
    使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
    为React 19做准备:WordPress 6.6用户指南
    如何删除WordPress中的所有评论

    发布评论