上线流程规范

2015-12-20

突然想说一说这个话题,是因为最近被我搞出个事故来。周末的时候发现竞价系统,线上的投放表现不正常,追本逆源,是依赖的数据出错了。竞价那边会需要加载一些数据,比如算法数据,广告位信息,媒体分类信息什么的。我们的竞价进程部署在好多台机器上,要把数据传过去,还有周期性的更新,文件推送是之前我写的一个基础性的服务,这个系统的功能就是可靠地将数据文件推送过去。

其实是很弱智一个bug,代码是类似这样子的:

func randJobID() string {
    rand.Seed(time.Now().Unix())
    h := md5.New()
    io.WriteString(h, "生成一串随机的string用作job的ID")
    binary.Write(h, binary.BigEndian, rand.Int63())
    binary.Write(h, binary.BigEndian, rand.Int63())
    binary.Write(h, binary.BigEndian, rand.Int63())
    return fmt.Sprintf("%x", h.Sum(nil))
}

我会为每次推送任务都生成一个随机的ID,结果几个任务同时到来时,生成了相同的ID。相同的任务ID导致算法调度完全乱了,数据最终得不到保证。

这个地方不应该每次调用rand.Seed的,当然冲突也没有考虑。

这不是我想说的重点,谁写代码bug都有可能存在。其实bug只是很微小的细节,影响却很严重。在线下环境自己测试时并没有暴露出来,上线跑了一段时间才暴露。关键的还是以其它形式暴露的:业务的表现不正常了,一路排查到这里,整个过程当然是浪费时间浪费人力的事情。

紧急修复,又暴露更多的不规范:实际运行的程序比master代码版本落后了比较多,都不敢确定master代码直接丢到线上跑是完全没问题的。master代码又落后最新的开发分支很多,那边有一些功能的更新以及一些不疼不痒的fix。既没有回滚方案,也没升级方案。最后是硬着头皮上了没在线上测试过的代码,祈祷不出问题。

一方面深深感到,越是基础的功能,责任就越是重大。另一方面这类事故在流程规范是可以避免的,至少可以把出事故后影响降到最低,还有应急响应也是可以做到更迅速的。

总结一些经验,让以后做到更规范。

一个是分支管理需要更严格。代码方面,我们把master分支作为上线分支。开发在其它分支上面,添加新功能和bug修复。一般是本地测试或者内网测试通过之后,给同事review,然后就合并到master了。但是执行不够严格,还有一个问题是有时候master积累太多,没及时上线。另外,这份代码只是线下测试,还没线上测试过。可以考虑加一个发布分支,本地分支测过合master,小范围上线,观察没问题再合到发布分支,全面的正式上线都使用发布分支的代码。

线上一定要灰度发布。有时候一些问题确实在线上才暴露出来的,比如说这里我在内网环境测试时就没覆盖到。以后要做到在线上小范围的先跑一段时间,确认没问题才整个更新。即使发现了bug,也可以将影响程度尽量的降低。

回滚方案。新加功能的同时,可能会引入新的bug。那么要做好回滚的预案。这个场景有个问题是,代码是可以回滚,推过去的数据能回滚么?本身这不是一个幂等操作。不过根据业务做一些措施是必要的,比如重新推一遍数据,业务层那边做更新。代码会慢慢稳定下来,能够回滚到某个稳定的版本是必需的。