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

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

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

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

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

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

故障可视化管理

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

系统的架构图如下

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

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

故障管理处理流程

上报故障的处理流程

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

故障重试的处理流程

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

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

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

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

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

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

系统关键实现

故障收集任务

sql 复制代码
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,用来标记故障属于哪一个任务。

故障表

故障表结构如下

less 复制代码
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 = '故障表';
  1. taskId是故障收集任务id,task_code是故障任务的编码。
  2. context 是故障内容。业务系统自定义故障内容,重试故障时自己解析故障内容。管理后台不限制故障的格式。
  3. uniqukeKey 是故障自定义的唯一键,系统使用 taskId+uniqueKey 作为唯一键。当上报的故障重复时,系统会覆盖原有的故障内容。

注册Rpc Client 进入Spring

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

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

ini 复制代码
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());

总结

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

相关推荐
dawn191228几秒前
SpringMVC 入门案例详解
java·spring·html·mvc
极客先躯2 分钟前
高级java每日一道面试题-2024年9月16日-框架篇-Spring MVC和Struts的区别是什么?
java·spring·面试·mvc·struts2·框架篇·高级java
Counter-Strike大牛3 分钟前
MySQL迁移达梦报错,DMException: 第1 行附近出现错误: 无效的表或视图名[ACT_GE_PROPERTY]
java·数据库
我要学编程(ಥ_ಥ)1 小时前
滑动窗口算法专题(1)
java·数据结构·算法·leetcode
niceffking1 小时前
JVM 一个对象是否已经死亡?
java·jvm·算法
真的很上进2 小时前
【Git必看系列】—— Git巨好用的神器之git stash篇
java·前端·javascript·数据结构·git·react.js
科研小白_d.s2 小时前
intellij-idea创建html项目
java·html·intellij-idea
林太白2 小时前
❤Node09-用户信息token认证
数据库·后端·mysql·node.js
XXXJessie2 小时前
c++249多态
java·c++·servlet
喝旺仔la2 小时前
VSCode的使用
java·开发语言·javascript