内容简介:很多时候我们都不用特别的关心服务器时间的问题,比如后台管理系统,如果服务器时间出错顶多会在页面获取错误的时间而已,影响不大。但有些程序对时间非常敏感,不能出一丁点错误,今天要讲的是去年发生在自己身边的事:由于时间同步问题引发了部门级故障,造成非常严重的后果。因为事件发生还不到一年,并且就在自己身边,所以记忆犹新。老规矩,在讲故事之前先了解一下背景,故事中 X 系统后台的简化架构如下:它包括 Client->Connector->Heartbeat 三个模块(当然实际有很多模块,这里省去其他模块简化架构,不影
很多时候我们都不用特别的关心服务器时间的问题,比如后台管理系统,如果服务器时间出错顶多会在页面获取错误的时间而已,影响不大。但有些程序对时间非常敏感,不能出一丁点错误,今天要讲的是去年发生在自己身边的事:由于时间同步问题引发了部门级故障,造成非常严重的后果。因为事件发生还不到一年,并且就在自己身边,所以记忆犹新。老规矩,在讲故事之前先了解一下背景,故事中 X 系统后台的简化架构如下:
它包括 Client->Connector->Heartbeat 三个模块(当然实际有很多模块,这里省去其他模块简化架构,不影响问题的描述)
- Client 是用来采集数据客户端,安装在公司所有的内部机器上,我们称之为 Client 端。
- Connector 起到 Client 端与后台桥梁的作用,主要用来进行 Client 连接管理、数据透传等。
- Heartbeat 是心跳模块,用来管理 Client 上报的心跳数据。同时它兼职时间服务器:为 Client 提供统一时间服务。
经过
上图中 Client 模块机器数量 > 万台,Connector 模块机器数量 > 百台,而 Heartbeat 模块机器却只有一台,这是引发惨案的根本原因。事情经过:
- 某天早上到公司,部门内部已经乱成一团。一番了解后才知道上述 Heartbeat 机器昨晚宕机了,很多心跳数据上报不上来。这时候的问题还只是 Client 上报不了心跳数据而已,并不影响正的常数据采集。
- 运维开始在一台新机器上部署 Heartbeat 模块,并启动(能自动注册并被发现)。
- 心跳数据陆续回升,但是只到了正常数量的 60-70%。
- 各个部门开始向我们部门反映:自己部门的机器 频繁产生 Client 的 core 文件(C++ 程序意外终止时产生的现场文件),而且是大面积的。
- 开发组老大和相关人获取 core 文件开始分析,一番定位后发现是因为 Client 出现了除零错误,Client 异常退出。异常处的代码大致如下:
int g_lastReportTime = sometime; void report() { int currentTime = getCurrentTime(); if (somevalue / (currentTime - g_lastReportTime ) > threshold) { reportSomething(); g_lastReportTime = currentTime; } } 复制代码
从上面代码可以看出 currentTime - g_lastReportTime 在理论上是不会等于零的,但是由于新启的机器时间并未与 X 系统内部保持一致。导致 Client 时间回退,也就出现了 currentTime - g_lastReportTime = 0 的情况。
- 运维立马想到是新 Heartbeat 机器时间问题,由于我们的 X 系统后台都是 Linux 机器,通过 ntpdate 命令将新机器时间统一,并将该命令加入开机启动项。
- 心跳数据陆续恢复正常。
以上过程花了将近 40多分钟,造成了非常大的影响。后来部门对事故相关责任人进行了问责和全部门通报。这次事故看似偶然,其实必然,从心跳机器单点那一刻。器宕机造成的事故远不及 Client core 造成的严重,可以看出本次事故的主要原因是由于时间不同步造成的,但它也不是孤立的,是众多因素的统一作用结果。本标题服务器时间同步引发的“惨案”并不夸张,希望能引起读者这块的注意和思考。
思考
在事故发生的时候我也没有想太多,毕竟当时作为萌新也想不出来个所以然来。但是随着工作经验积累,慢慢的从这次事故中有了一些总结。这次事故主要引出以下四个问题:
1、监控问题
心跳机器宕机,心跳数据上不来居然在第二天上班的时候才被发现,整整超过 8 小时了。幸亏该系统只是一个内部系统,如果是类似淘宝、天猫这种的,那造成的经济损失和口碑影响可想而知了。听说在半夜监控已经报出异常,但是一个新来的同事定位并未发现问题,才导致了最后的蝴蝶效应。不过好像即使当场发现,如果在切换新机器时没做好时间同步也会出现 core 事故。对于系统监控我有以下几点建议:
- 不要将重要模块的监控交给新人,很多情况下新人可能并不知道某个监控的重要性
- 重要模块的监控接收人不要出现“单点”,最好将主管也加入重要模块的监控通知接收人中。
- 多维度、多地点进行监控,一般一个系统都是一个整体,一旦某个模块出现问题,其他模块也会随之出现问题。如果我们在多个维度、多个地方进行监控,即使某个地方发现不了,总有一个地方能被发现。
2、单点问题
这是一个老生常谈的话题,网上有一堆的解决方案,这里不讲普适性的只介绍下针对 X 系统的。由于该系统的特殊性,Heartbeat 模块并不能进行多机器部署。所以只能单点,那我们只能祈祷单点机器不会发生故障了吗?根据墨菲定律:如果你担心某种情况发生,那么它就更有可能发生。所以不要有侥幸心理,对于 X 系统 Heartbeat 的单点,虽然不能在短时间内重构,但我们还是可以做一些事情而不至于需要运维手动切换机器的。一种方案如下:
在服务注册中心对 Heartbeat 模块机器进行监控,如果发现服务注册中心没有了心跳模块超过一定时间。则启动备用机器上的 Heartbeat 并发出告警。这样就能及时的切换到备用 Heartbeat 不至于太匆忙忘这忘那。简单结构图如下:
3、代码质量问题
事故中的错误代码是我简化后的,一般不会出现出现这种低级错误,应该是一段逻辑处理后产生了类似的代码,只是不够直接没被发现。这种情况我们怎么保证代码质量问题?我觉得可以从三个方面考虑:
- 程序员自测:程序员在写完代码后一定要进行充分自测,虽然不能 100% 保证不出错,但起码能发现大部分的逻辑错误和低级错误。不要想着会有测试帮你,自己就可以偷懒,他们往往只在使用层面进行测试而不是在代码层面。如果不给你配备专门的测试,那就更应该自己动手、丰衣足食了。
- 代码评审(CodeReview):都说不识庐山真面目,只缘生在此山中。代码检查也是,自己检查自己的代码,很难发现一些隐藏的错误。这时就要其他人帮你检查,也就是 CodeReview。当然,所有的代码都进行 CodeReview 是一件费时费力的事,所以要有选择的进行。一些核心模块的代码,一定要一遍又一遍的自测、CodeReview。至于 CodeReview 的方案试情况而定。
- 增加专业测试人员:(1) 和 (2) 只能解决代码层面的错误,但是一些使用层面的、极端情况下的错误 (1) 和 (2) 并不一定能发现。所以需要专业的测试人员和测试平台对上线前的代码进行充分测试。
4、分布式或者集群时间同步问题
分布式或者集群的时间同步也是分布式系统下需要解决的问题之一,抛开 X 系统事故中的场景不说。有很多场景是需要保持分布式系统中各个节点(机器)上的时间一致的的,比如银行交易系统,不能让后一笔交易的时间比前一笔交易的时间早,否则会给用户造成困扰。不同的分布式系统有不同的解决方案,有的简单有的复杂。简单的像 X 系统一样直接用 ntpdate 就可以实现,复杂的参考博客园这篇文章《分布式系统----时钟同步》实现自己的同步系统。
当然,以上只是泛泛而谈,权当抛砖引玉。不知道你所维护的系统中是否也存在类似的问题呢? 早一点把已知的问题暴露出来,比藏着掖着要好的多。在出事故之前发现问题能赢得老板对你好感,说不定还能升职加薪,而如果在出事之后再来解决问题那就只能背锅咯 。
以上所述就是小编给大家介绍的《服务器时间同步引发的"惨案"》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。