记录一次生产发布事件——(简单的非空验证也能引发大问题)
欢乐小插曲
时间定格在周四——常规发布日下午三点,研发任务早已完成、测试也无大碍。这时,办公室外走廊里传来了一阵急促的报警声——“此大楼发生紧急情况,请各单位抓紧撤离”。像往常一样刷刷博客园,坐等发布生产的我闻听此声,虎躯一震正准备要跑路,转头看向周围的同事。大都很淡定的坐在工位上各自撸码。于是收回我那一只已经迈向过道的腿,佯装淡定的坐下,悄悄的问了问隔壁的老大哥:老哥,怎么没人跑路?“兄弟,习惯就好,八成又是大楼搞消防演习”老哥扶了扶眼镜,继续撸码。果不其然,半个小时后收到了公司的马后炮邮件——“近期大楼消防演习,请大家听到警报声后无需惊讶,各自工作即可......”
事件经过
下午四点,发布生产g环境(生产环境m为正式环境,g为内测环境)。这时测试有人提出“服务器忙”。听到这里我赶紧翻了翻内测日志,发现了最熟悉的老朋友——未将对象引用设置到对象的实例。问题出现在如下代码上。(我下面附上了伪代码,大家可以看看下面的代码有没有问题,当时我看了很久才发现问题所在)
//获取账户信息 var accountInfo=GetAccountInfo(); if(accountInfo==null){ return "xxx"; } //判断账户信息中是否包含xx——问题点就出现在下面这行代码(未将对象引用设置到对象的实例) //其中Test字段为此次发布新增字段——线上版本的AccountInfo中并不存在此字段 bool isTest=accountInfo.Test.Contains("A");
//获取账户信息方法 public AccountInfo GetAccountInfo(){ //先读取Cache AccountInfo info=cache.GetCache(); if(info!=null) { return info; } info=SOAService.GetInfo(); if(info!=null){ cache.SetCache(info); return info; } return null; }
//账户信息类 public class SOAService() { //从远程服务获取账户信息 public static AccountInfo GetInfo() { //获取一个服务Client SOAClient client=SOAClient.GetClient(); //获取账户信息 Account account= client.GetAccountInfo(); if(account!=null){ AccountInfo info=new AccountInfo(); info.xx=account.xx; //Test为null处理 info.Test=account.Test??string.empty; return info; } return null; } } //账户信息实体类 public class AccountInfo{ private string name; //...此处省略n个字段 //此处新增字段test private string test; //属性Name public string Name{ get { return this.name; } set { this.name=value; } } //...此处省略n个属性 //属性Test public string Test{ get { return this.test; } set { this.test=value; } } }
在代码中观察许久仍没有发现问题。这时测试一句话提醒了我,“我看m环境没有问题”。灵光一闪,原来测试先从m环境登录,浏览了一圈页面后,已经缓存了AccountInfo,但是m环境此时是没有新增字段Test的,此时切换到g环境(我们的m环境,g环境对应缓存数据都是一样的,区别仅仅是应用服务器不同),获取账户信息时会直接从Cache中读出来,然后accountInfo.Test在用之前并没有判空,所以...未将对象引用设置到对象的实例。于是乎得意的跟测试说,你登录后别再m环境操作,直接切到g环境,就可以了,等发m不会有问题的。果不其然,测试按我说的做了不再报错。
如果你以为事情就这么结束,那就错了。请原谅我那猪油蒙了心的傻叉操作。不久,g环境验证无误,开始往m环境发布。起初未见异常,当发了集群大概三分之一节点的时候,大量异常突然袭来,瞬间监控开始报警。一看日志满屏的老朋友。紧急关头得亏脑子反应快,紧急回滚代码。静下心来脑子一想,生产用户本身处于登录状态,有使用缓存。刚刚出现问题没去处理,真是悔之晚矣!于是紧急修复。增加使用前判空,问题终于解决。
事件教训
- 对于程序中大量使用缓存的系统,开发时一定要考虑好缓存。(这个系统一些不合理的缓存设计坑的我苦不堪言)
- 对于测试中的每一个问题都要认真对待
- 纸上得来终觉浅——背的滚瓜烂熟的,缓存穿透、雪崩,缓存更新、程序非空验证。实战起来还是不够用
- 敬畏每一次生产环境的发布
- 图片验证码-header-session的一次问题记录
- 记录一次JDK版本问题,引发的思考
- 记录一次id循环输出后,引发的问题
- 定位问题解决问题-记录一次事件
- 问题记录-如何让Listview子控件在点击之后失去点击事件
- 记录一次简单的STM32串口通信使用printf发送数据到屏幕,并测试SHA512算法。
- 最近想写一个邮箱自动验证功能,在网上看了很多,写到自己上面出了很多问题,记录下来给后面的人一个参考
- 关于jquery全选和取消全选遇到的只能选一次问题记录
- 记录一次给recyclerview item点击添加水波纹反馈效果没起作用的问题。
- [Oracle]记一次由sequence引发的enq sv-contention等待事件
- 由一次mycat+mysql水平拆分集群问题引发的思考
- 一次Mysql数据库服务器磁盘空间满引发的写入和同步问题
- Android中关于EditText点击事件的响应问题记录
- 简单记录一次REDO文件损坏报错 ORA-00333重做日志读取块出错
- 记一次VNC远程连接Linux问题解决记录(5900端口测试、KDE桌面安装)
- 【troubleshooting】记一次Kafka集群重启导致消息重复消费问题处理记录
- 记录一次eclipse右键弹不出来的问题
- 记录问题:ajax局部刷新页面,导致js事件失效
- 记录错误使用eventbus引发的问题
- 记录一次使用_RecordsetPtr去访问已有表的新增字段时,出现的怪异问题!