工作中的事故心得

Featured image

工作中的第一次错误

事故描述
写充值结果查询代码时,应该是else if逻辑,结果丢失了else;详细情况如下:

//当时的错误逻辑如下
if(电信){
}
if(联通){
}
if(移动){
}else{
  return "充值失败"
}
//正确逻辑应该是
if(电信){
}else if(联通){
}else if(移动){
}else{
  return "查询失败"
}

在错误的逻辑下,对于移动的订单,如果充值成功,是可以正常查询并且返回正确的结果;上线之前恰好也是使用移动的单子进行的测试,根据以前的经验,对下游来说无需关注运营商,所以通常都只测试一个运营商,不会三个运营商的单子都测一遍;

正常上线之后的一小段时间,恰好也是开了移动的通道,所以观察并没有问题;之后临近下班时,开通了联通的通道(电信也是相同的错误),但并没有观察,这时候便执行到了错误的逻辑;也就是最后一个else “充值失败”;最终的结果是导致一部分订单重复充值(这一个通道充值失败后会尝试其他上游通道);

工作中的不足之处
1、技术的专业程度不够,导致代码写错;
2、没有足够小心的去review自己的代码;没有做到对于任何一个改动都应该保持敬畏;
实际上当时在写完代码第一遍的时候自己review过,当时的逻辑也确实没有问题,在别人提出修改建议之后,按照建议修改,因为觉得改动不大,所以就没有再次review
3、业务不太熟练,这是参加工作的第二个月左右,当时并没有意识到业务知识与业务认知对于工作的重要性,实际上这里面除了少了两个else逻辑错误之外,最后一个else的返回是有问题的;在错误逻辑下,当时就不会去上游查询,这时候应该返回查询失败,而不是充值结果失败;如果不犯这个错误,即便少些两个else也不会造成直接损失。在之后的工作中便高度重视了所有充值失败的逻辑,只有确保100%是充值失败,才会将订单状态置为充值失败。
4、测试不严谨;如果当时测一笔联通/电信的单子,这个错误必现

心得
1、增加技术专业度,主要是代码逻辑严谨度;
2、对代码保持敬畏,认真审视每一个细节,每一处改动;
3、增强对业务的认知
4、制定严谨的测试用例

其他
好在公司的监控与异常保障措施相对比较完善,在失败率达到一定的值后自动关闭了充值通道;最终导致的损失不太大,一共100多笔订单,大约损失了1万多

zk配置错误导致实例初始化失败

事故描述:
一个SDK使用zk存储配置数据,修改zk配置时,数据配置错误,新的配置未加载成功,按照业务逻辑继续使用旧的配置。过了几个星期,重启服务,配置加载不成功,创建SDK中的java对象时抛出异常;SDK调用方没有cache住相应异常,不停的的尝试创建实例对象,直接导致调用方的整体服务挂掉;

工作不足之处:
SDK配置失败没能及时发现、调用方没有处理好异常、调用方定位问题耗时很长

修复:
增加配置监控,每次修改zk配置确保正确生效;调用方处理好异常;调用方业务架构不合理,调整架构(实际上SDK提供的服务出问题之后,暂停SDK的服务而不应该影响整体服务)

心得:
业务方服务很多,涉及到kafka、hbase、zk、hdfs以及自身的许多RPC服务,平时告警比较多,经常忽略告警;保证监控告警的有效性与业务平时的质量很重要。 这次出问题时,最先观察到的是跟hbase服务相关的问题,而且hbase和kafka也是经常比较容易出问题的地方,所以他们先联系运维排查hbase,kafka等相关问题,就耽搁了很多时间。对于高并发业务,合理完善的服务架构,能够快速的定位问题很关键,预防问题>快速定位问题>尽可能保证业务可用性>查找问题原因>解决问题 对于高并发的一些问题,出问题时的一些情况状态很难完全记录,事后也很难复现问题,导致查找原因以及解决起来非常的耗时。

zk频繁连接、断开导致频繁执行onChange逻辑,大量消耗机器资源

事故描述
一个相对比较复杂,涉及到的功能和组件比较多的系统崩溃,不可用。根据现象,结合之前的经验来看应该是kafka队列堆积,或者是上游错误导致。总之花了很长时间排查,之后定位到到是由于业务本身的某个逻辑频繁受到zk断连的影响而频繁执行一个相对较重的逻辑,大量消耗了机器资源,导致业务的其他功能不正常。

工作不足之处:

修复:
zk频繁断连的具体原因只有一些可能的分析,但是没能复现,最终的root cause也没有定论。作为业务方,每次触发onchange事件时首先检测数据是否真的发生了改变,然后再做处理。

加强监控。

长期来看,调整系统架构。

压力增大导致api服务可用性不达标

事故描述
客户端短时间覆盖了大量用户,由于redis连接池配置不合理(太多的健康检测)以及使用不当(应该先缓存整体,再处理,结果是处理完之后分成了6部分缓存,只要一部分失效,就得重新请求),导致缓存穿透,同时数据库连接池配置不合理(实际是配置不生效,使用默认配置)导致对mysql服务器造成了很大压力;

问题原因:
redis配置不合理,redis使用不合理,mysql配置未生效;缺少压测环节;

当时的难点: 难点一:只有在高峰时段才发生这个错误,而且也不是所有请求都会出错,所以给排查带来一定难度;
难点二:问题的主要原因在于redis,但是redis测并没有监控到压力,而直接暴露问题的是MySQL,导致前期排查方向不太对;即便是修正了MySQL的配置之后,依然是在MySQL的逻辑出现大量错误;

访问MYSQL服务未指定走读服务器,导致时而取到时而取不到数据

问题描述
staging环境测试正常,上线之后发现有时候正常,有时候读取不到数据。

不足之处
当时第一反应是在服务内部,多次尝试读取减少异常请求的发生。然而这实在是下下策。在高并发的情况下这样做,这种做法往往会让事情变得更糟,因此临时下线,只保留一台机器线上测试!

问题原因
项目中连接的数据库使用了主从配置,并且主服务器只负责写数据不允许读数据。但是在写DAO层取数据接口的时候,并没有指定走读服务器,导致时而走读服务器时可以取到数据,走主服务器时取不到数据。

修复
设置走服务器即可

SDK内部使用线程池,导致异常不能被捕获

https://www.toutiao.com/i6771768308048855556/