双11大促期间服务可用率突然降到50%以下
线上一个服务0:00-0:20没有任何问题,0:20之后突然一个服务调用量增加(不合理接口调用量应该都是0:00开始猛地暴涨),
接口可用率降到50%以下。如果是核心交易接口,那么订单将影响一半以上,很可怕。还好,不是核心业务,是一个辅助展示
业务,并且业务本来不应该打开。
那么为什么配置被打开?为什么调用量增加?马上和下游沟通,以及查找问题,如有情况对系统做降级处理,返回通用数据,
因为下游程序发生了bug,在配置服务时候导致配置异常,不该调用我们接口配置被打开,调用我们接口量暴涨。
为什么调用量暴涨之后接口可用率下降?一种情况是调用量暴涨导致,超过了服务可相应极限导致服务可用率降低,经定位
发现不是流量暴涨导致,因为服务各种cpu、内存、网络资源使用率均正常,服务可承受流量为当时十倍也不会有问题,故排除
这种情况。
排除流量超过之后,那就是逻辑存在bug,下游请求参数错误或我们程序本身有bug在大流量下有用户触发到该bug,有了这
两种假定。继续日志定位是线上服务有null值导致,具体什么原因呢无法定位,因为线上打日志方式是log.error(e.getMessage())
没有打印详细信息,关键时刻掉了链子,无奈只好马上将预发布加上log.error(e.getMessage(),e)打印详细错误栈信息辅助快速定
位问题。
经修改后预发布进行定位发现问题是:使用for循环遍历集合,另外一个集合与他进行判断数据不再集合中则移除数据如下样
例所示:
for(String str : stringList){
if(!otherList.contains(str)){
stringList.remove(str);
}
}
因为在遍历集合时对集合进行移除操作,这种操作会导致计数器异常,最终导致程序异常。
修改方式一种采用倒叙方式遍历:
for(int i=stringList.size()-1;i>-1;i--){
String str = stringList.get(i);
if(!otherList.contains(str)){
stringList.remove(i);
}
}
这种通过倒叙遍历方式避免移除数据,不会导致集合遍历下标错误,程序完全正确,是一种程序遍历方式。
再有一种修改方式:
for(Iterator<String> it = stringList.iterator();it.hasNext();){通过支持删除迭代器遍历方式遍历集合方式删除数据也是没有问题的。也是一种正确数据删除方式。 还有其他比如通过另一个
集合记录待删除数据,通过removeAll()一次删除符合删除要求数据等多个方式。
回过头来总结一下,线上问题出现根本原因,一是基础不扎实,二是测试不到位,不该犯得错误犯了,再有就是编码不规范,
如果异常日志打印详细合理,也能节省时间,很快就能定位线上服务问题,节省定位问题时间。
基础是及其重要一件事,也是一件“很小”的事,但却往往决定我们技术架构、设计成败。不能好高骛远,要脚踏实地。勿在
沙台筑高楼--侯杰。上边程序bug如是在中间件程序或线上支付流程大家可以想象下,中间件情况下是线上全部服务受影响,一种
是影响线上订单服务,按交易额是50亿计算就是影响25亿以上,想想就后怕,做事要认真做好每一件“小事”与大家共勉。
微信搜索:debugme123
扫描二维码关注: