一次现网翻车经历与总结

1,811 阅读9分钟

0. 前言

还是和平时一样,做完需求,测试通过,愉快地上线。运营侧在大推,推了好几天。突然有一天,都来反馈说页面自己弹出dialog而且关不掉:

image

这就奇怪了,都跑了几天的项目没问题,怎么突然翻车了?

背景:一个专门做活动的git项目,仅在微信和手Q环境运行。用的是preact,项目结构是该有的都有,pages下每一个文件夹都是一个页面,也能支持第2级文件夹一个页面。每一个活动页面就是一个文件夹,互不影响,mpa。我们的ci系统很慢,部署大项目需要几分钟,然后加上各种环节最快要半小时走完流程,接下来很快将会用另一个方案替代。我活动已经上线几天,是一个运营活动,推广那些学习课程的。同事他们另一个活动后来上线,是一个年度总结活动。突发现网问题,时间非常非常紧张而且是2018最后一工作日前一天晚上,而且我们两个活动必须在元旦前发出去(运营压时间的活动准时上线的重要性大家应该都懂)

项目文件夹

pages
-- act1文件夹
---- conponents
---- index.jsx
---- index.html
-- act2文件夹
---- act4文件夹(都同上)
---- act5文件夹
-- act3文件夹

最后act1.html,act4.html,act5.html,act3.html都是不同页面

1. 开始第一次排查

都上线几天,突然出事,可以肯定的是,不是业务的bug。剩下的原因:现网资源被更改、npm包被更新。跑了一下主分支master代码,没问题。npm run dist模拟生产环境也没问题。

经过询问,原来同事刚刚发布,发完还没合并主分支。一看他的分支,果然是更新了。查了一下ci系统,也找到了记录。于是,切换他的分支跑起来项目代码,然后打断点,弹出方法的函数根本都没有进去,而且控制弹框弹出的state都不是true

image

这种情况,首先让我怀疑人生一阵,代码明明没问题而且state也没有让他弹出来。于是再对比现网的和我之前发布的正常版本,来到同一个页面,发现新代码的主逻辑js多了10行代码!!一瞬间我马上想到的是babel插件的问题。因为我正在用同事写的一个Babel插件,功能就是在react的jsx中写类似像vue那种命令。

现网弹框无解弹出关不掉 =》 所对应的state并没有为true =》 代码多了10行 =》 babel插件有问题

2. 升级基础库版本之坑

问了一下,原来他为了fix那个插件在preact中的一些坑和更新了preact-compact。为什么呢?因为他们的活动需要引入antd不然时间上有风险,引入antd在目前preact的版本并不能跑起来需要升级。升级完成后,antd大部分组件可以正常使用,但select组件是undefined无法使用,效果就是一个叫undefined的什么都没有的标签渲染在页面上,所以他在能跑大部分antd组件的基础上,再手写一个很完美的select。

但是,就是因为升级,导致这个插件有bug。时间紧急,也没有时间去管。定位到问题,马上叫测试回滚,现网我的活动正常运行。接下来的计划是,我把所有的用了指令的地方改回常规方法,然后跟着他们一起发出去。对于版本,测试稳定后,把package.json的^号去掉,把版本稳定下来。刚刚好,他们那边被leader们盯得紧,一些体验问题也要改,所以我也可以坐个顺风车。我改完了,自测没问题,先撤了。他们改完了也自测通过,已是差不多10点,部署上了预发布。

晚上的时候,看见企业微信上的他们顺利上预发布的消息,我也放心了,心里想着第二天过去验证一波就上线。

必须要用antd =》不能用只能升级基础库 =》 导致babel插件问题出现(作者也投入需求没时间理)=》 我的活动和年度总结活动一起放同一个分支上线

3. 预发布资源丢失

当我在公司,代理配置上了预发布,发现资源404和502,一些主js丢失页面崩溃。而且有的手机有缓存复现不了,连电脑修改ua跑微信环境也发现大部分图片资源失败。一开始想到的就是我们的ci系统有点坑,重新部署一次就可以。浪费半小时,结果还是一样。这时候,去检查资源,经过老司机们的同心协力,确定了原因以及解决方案:

我们的部署是增量部署,也就是类似于电脑复制粘贴,有冲突的覆盖无冲突的不变。我前几天上线,今天也改了新代码但是一些静态资源和一些页面完全没有变化,cdn映射也不变,资源名字还是资源+原哈希。问题的根源:这周周一的时候,机器上的文件被清了一次,现在才知情。于是手动把资源拷贝到服务器上,顺利跑起来预发布环境。此时,炸出了这边资源匹配规则不合理的问题,应该改进一下。

预发布资源404和502 =》 机器被清理过 =》 手动补回资源

4. 兼容性

我这边验证没问题,因为之前遭遇过测试低端机比较晚,发布前很赶而且风险大。刚刚好,他们就遇到了,而且是,部门大老板用安卓机体验到一些不好的地方,他们那边继续修改。除了一些体验问题,然后最后定位到一个fastick的库的问题,是为了解决点透问题的,只是兼容性上出了一些问题。后来有一个操作,有一个同事发现来到一个页面的弹框怎么都关不掉。问题来了,几个同事跑的是不同结果。这很明显是node_modules的问题,根本上还是package.json的版本。经过对比,我们package.json就取掉了^符号而已。

此时,我们的package.json经过几个过程:我发布时是旧的react和preact =》 他们有一个环节需要antd升级了compact并删除了lock.json =》 我的活动发现babel插件问题,修改代码自测通过,一个正则把 package.json的所有^ 去掉保证版本稳定 =》 他们几个人不同的node_modules

为什么还有版本坑?于是我们开始了rm -rf掉node_modules重新安装,后来大家都是一样的效果了。在我们寻找解决方案的时候,突然才想到,去掉package.json的所有^ 并不能解决问题,这不能阻止依赖中的依赖的版本,说锁版本最靠谱的还是lock.json。又一番修改,确定最终package-lock.json,终于上线了!

package.json多次被修改 =》 自测稳定并锁住最终版本

5. 最后

事情并没有结束,有一个同事把工具库里面的一个函数名字写错,现网再出bug,很快解决。后来工具库的作者说这个是废弃的方法,于是又一波讨论下次版本怎么废弃没用的函数。

经过反思并总结:

  • 线上资源和预发布资源不一致 =》 脚本上的增强(当机器上的文件被清除,原本的增量部署就会有问题)
  • 部署环境坑 =》 接下来会用新的构建和ci,git push或者打tag会自动部署
  • 版本升级 =》 升级的时候需要全面测试、照顾历史代码、知会
  • 老逻辑 =》 新需求需要问清楚、主动推进整个流程、特殊照顾新人

为什么用preact?因为小,而且我也喜欢上一些宽松语法:style可以写字符串、不用在map元素中写key属性、无需担心对被卸载的组件setstate的坑(另一篇文章有讲到这个问题)。但是用了preact意味着不能用react新特性,反正目前是这样,以后应该可以支持。活动页面性能要求高,而且兼容性要做到极致,但preact在我们项目暂时跑不了antd。用不用antd不是问题,但是用不了antd就是一个问题,早晚会踩坑的。

对于我们这个项目,结构就是多页面,每一个页面独立。但是每一次发布也意味着所有的活动页面也会被重新发一次。如果修改基础库,所有的活动都被影响。我们的方案是:删掉之前的页面,发布的时候利用增量发布的特性直接发某个需求,但是旧页面需要找到旧分支才能修改,有点麻烦;或者是稳定版本库,长期不用动它;或者是先用着,等到要改的时候顺便改,旧页面先在分支中删掉。路过的大虾们,对于后续维护上如果有更好的方法可以提一下。

“我只是想安安静静写代码”,这是多美好的期望啊,可是事实上并不能,你要照顾整个团队。大家都是一个team,无论遇到什么都要一起面对,大家都要聚在一起,谁慢了谁快了都要照顾一下,永远别以为你自己跑起来项目就完事了。这次说背锅,貌似谁也没有特别明显的背锅嫌疑。说大家都没错,也是有道理,谁都是做好了自己本分而且正常上线,面对任何难题都解决。说大家都错了,也是说得通,大家都是只看自己的一亩三分地,没照顾整个团队,没照顾整个项目。

最后一个工作日,虽然跌跌撞撞,但是获得了成长,给2018职业生涯画上句号