我们的系统大约在上线后2个月内,我们发觉我们的前端小程序即TO C端的商品目录这一块,经常会有用户在点一下“分类”进入后在显示相应的分类项时右边的具体商品列表会时不时卡一下,卡个大概1秒-2秒左右。它还经常发生。
另外就是,我们有TO B端即运营管理端,它的访问来自于通过公司内网访问。我们的系统有上游主数据也有下游相应的仓库、财务、发票等系统。由于这些系统为Legacy因此大部分-约90%为跑批而并非real time的交易。然后以触发式MQ的方式,收到一条消息就会有一条主数据下行中台或者是中台下行仓库的调用。而在这种调用时,前端小程序也会莫明卡个几十秒。
虽然它没有构成特别严重的问题,但是给人感觉特别的难受。另外在我了解下来,TO B端这一侧经常会出现time out的error日志。不过好在90%以上的TO B端操作有on error retry机制。但是当这个现象越来越多、越来越频繁时特别的烦人。
我们的系统它在我接手前你们可以认为这是一座“屎山”,一座拥有600万行代码的“屎山”。我不知道有人遇到过“屎山”,就算遇到过但是可能真的稀少有像我这样遇到的是一座喜马拉雅这么高大的“屎山”。
我一般对于生产问题会做这样的记录和整理:
BUG我平时一般很少会去细看,但是我有每天花5分钟快速扫几眼生产BUG的习惯。我对生产BUG的要求是“D+1天清零”,那么当发觉这样的BUG已经被报了三次了,它立刻引起了我的重视。
于是我开始对这个问题进行了分析。
问题的描述就像我上面所述,它具有一个很好玩的通性。TO B端操作一下就会引起TO C端的卡。或者TO C端那些模块如果不是采用的是RPC或者是spring的微服务而是HttpClient访问请求时就会发生这种现象。
要知道我拿接手的是一个己有600万行代码的项目的框子上改出来的东西,它光本身基础模块达34个,还有企业内部一堆的legacy system相关连,而API连接点更是高达上万处。
这不是一般的项目可以想像得到的也不是一个系统对接另一个系统这种单对单的“普通”对接。
它是一个网、超大的蜘蛛网!千丝万缕!
因此,这个系统注定它里面的相应的一条条技术栈非单一技术而论。
因此,在系统中会存在这么几种连接:
所以我们从总的架构图(好在“屎山”上线前我绘制有总架构图,从南北、东西、模块、系统间交互用的是什么方式都完整的绘制出来了)上对应着三次生产上发生的问题来看,找到了第3个点。
在当时,我们还没有上全链路跟踪APM,只有靠日志、GLOWROOT和Grafna来排查。而这种问题它不会表现在表面。在表面可以找到的证据,就只有可以找到elk日志里有相应的connection time out这种字眼和相应的代码位置。
那么我把这些日志挑出来,找到相应的代码再去排查,才发觉了以下这么几个好玩的现象。
各位要知道,这个庞大的项目它不是一切从0行代码开始写起的,它至少有一个框子。我们知道程序员最喜欢的就是推倒从来,可是最考验人的却是“治理屎山”。
那么我现在手上的就是这座“屎山”,特别是上面的第3点,由于这个框子的升级是经过了3年多,因此它里面用到的http client的helper类还不是spring托管的,也不是restTemplate。而是纯编码书写的http client啊。
然后呢,结合着3个好玩的现象,在系统运行时,整个架构内的http connection由如下图所示:
整个系统里随时伴随着7位数的Http连接风暴。
发生了这种问题就是改呗?但实际问题没有这么简单。
我前文在:家乐福618保卫战二-零售O2O场景中的万级并发交易情况下的极限性能调优中提到过,我们这个是生产环境,生产环境有业务在运行和严格的业务排期计划。
你要动排期计划,这可是要了业务的命了。你不动,那么最终因为当这个系统外部的请求越来越多时,早则一个月慢则三个月你还是会没命。
因此为了说服业务型领导,你给他看代码吗?他也不需要看或者他找到的相应的咨询者怀有着自己的一些目的也会给你把话题给说偏了。比如说来一句:你不用手动去关的,spring啊,spring里一切都会自动关闭。
然后这时你为了和他争论你们要开始翻spring 源码和http client源码来比对和说明这个问题。这么简单的问题马上会沦落成bla bla bla。然后业务领导就烦了,就不会听你的了,也不让你动手去改这个技术认为特别严重的问题而在业务听不懂的问题了。
真的,有时技术干活真就这么难。
这怎么办呢?现在摆在我面前有两个困难
换成一般的技术,此时“心累”了,要走了!
唉。。。说实话我确实心累,累在很难说服业务型领导。但这个事我知道是必须去做的,怎么做呢?
我采用了以下几招。
首先,我们使用grafna,抓这些出问题的功能模块上的linux里的文件打开数。我们知道http的底层归根到底还是tcp连接,而一条tcp被打开它就会创建一个linux里的file open句柄。
我们假设这个file open没有被用完后close而再开、再开、不断的开,它会发生什么?linux里的文件打开数很快就会都被搞“爆”,linux的文件打开数被搞爆后会发生什么?运维和devops人员连远程sftp管理工具都连不上。
因此,我们做了一件这样的事:
问题最严重的点在于TO B端那几个连接legacy时的操作,由于都是MQ触发式或者是XXLJOB频率跑批类的,因此这些关联操作它们涉及到的http connection的梗特别严重,而且在白天必现。而有一些呢在晚上才出现的呢?可以先按照优先级往后放。
要知道,2,000多个点有这样的问题,动到了3分之1以上的原功能模块和业务逻辑啊?这不是15天,1个月可以搞得定的事。
因此按照:
那么业务领导就会来问了:这三期,总计达近3个月,我的业务功能要delay吗?
我告诉他:不用不用,一点不用delay,一边上业务功能一边改。
领导:那么你改这个是要全回归啊?这个也需要时间,你说的话不符合logic啊?
于是我告诉他:很简单,我们开发还好人手有,因此在做业务功能开发时顺手把正在开发到的这些功能模块里的http connection乱用问题撸掉。撸掉清掉后,它不是要提测功能单吗?我们看这几次上线里程碑里的业务功能,都要用到订单、主数据、商铺、用户对吧?而这个http connecton属于底层“基建”,基建如果在改动时影响了业务功能、或者是反而改坏了,那么在测试时就会发现问题。因此这对开发叫“顺手撸掉”,因此要纠正这种问题真的不难改,而对测试来说呢正好是带着当前版本“测”掉。
所以我把功能模块就是咬合着业务功能叠代的里程碑去做的折分。
领导又问:我比较关注于第三期就是依靠开发自己发现、梳理和避免下次如何不会再发生一次这样的问题?
于是我做了以下几件事。
其实这件事在我看来是好事,就如我所说,我接手的我认为是一座“屎山”。但我真的有这个信心把它变成一座“金”山。关键不在于发现问题,而在于发现了问题后同样的问题不要再发生,这不属于天天你拿鞭子抽就可以抽得出来的结果,而在于整体团队的一个提升,这是技术管理的核心价值所在,那就是“变废为宝”。
我做了以下这样的事:
我们来看看我们的这方面的规范是什么样的吧,我这边摘录核心的3条内容(其实我们仅仅为http connection的使用上就制定了小20条规范):
以上第3点在一开始遭到了不少开发们的抵触,要知道上百人的开发团队,高手有、大部分为中级程序员,还有不少初级和刚毕业的程序员。它们就是我们嘴里的“码农”。以上这样的技术点在于大厂的P5、P6、P7来说简直和1+1=2的问题一样都不需要做任何告知,他们平时的开发习惯就是这样的,而我们要知道99%的企业真的不是大厂,还是以码农为主的开发团队。
于是为了消除这样的抵触,我着重和开发们举了一个例子,为什么很简单的我一个for循环,然后在循环里塞上一个http接口的调用不好呢?这是因为,好比你有一个目录,这个目录里有几十万个几百k大小的jpg或者是txt文件,你用鼠标“拎”着这个目录往百度网盘里一扔,你以为很快就会传完?实际用了4,50分钟。而另一种做法是:我把这个目录连同里面的文件打成一个zip包,它可能有2个G,打成zip包后我把这个zip包然后再往百度网盘里再去做备份上传,那么就算在家里面这种普通用户带宽情况下也只用了两分钟。这就是因为每一个http connection是一条Network IO。系统性能有一条很重要的点就叫:减少Network IO的调用。
看!这么一举例,所有的开发包括刚毕业的新人都领悟了和深刻记下了这件事。
这不是炫耀,上面我说的这个问题放在任何单模块、或者就2-3个系统对接时你会觉得这个改改只是一周的时间。
没错,真的全改了就一周。
你别忘了有回归测试啊。
你说:这个测试快一点也一周,最多两周。
我这样告诉你,这么多改动,你说测试只要一周,业务型领导就会觉得你已经不靠谱了。为什么呢?业务型领导只看有型资产和看得到的东西,对于这些看得到的它觉得投入是值。哪些是看得到的呢:硬件他肯投、测试他肯投因为这个关系质量哈。
关键还有,这个系统有600万行代码,小一半业务功能底层里有一部分被动到了,而且被动到的这一部分是涉及到订单、主数据、用户,那可是零售三大主流程啊!
你敢拍胸脯说:没事?
嘿!
大型团队、上百人开发合作,忌讳的就是拍胸脯。
这件事上其实有这么三个点:
因此我采用了以上的手法。
你们看这件事,它属于管理吗?看了又不太像,因为里面是有技术点的。你说它属于技术吗?这里面还有敏捷项目管理、项目折分、沟通演示技巧也不是那么“全技术”;
各位,我以此一例带领各位开始正式入门“架构-而且我传播的一定不只是纯技术架构,而是企业架构”这一理念。
这都什么问题呀,这么简单的这么烂的事。。。根本上就一开始时就可以完全避免这么LOW的事。
我的回答:
等等。。。等等。。。且打住兄弟。。。
任何程序员都喜欢摧毁式的去重构一个系统,而你实际的人生时间里有99%碰到的是接手“屎山”。接手“屎山”真心不怕,屎山完全可以变金山。也不要去怨开发,不是个个企业是大厂,在大厂的程序员都已经讨论到leet code level5, level7了。不是这样的。
超过99%的人手下的团队,都是我在一开始碰到的这一群连http close要放在finally块中也需要着重教育的“猿”们,你把他们都fire了?谁去做事?
把他们培养好、把他们带向“员”、再带向“师”,这才是真正的挑战。
everyone says: I need the good resource, but how about to train your current resource?这是乔帮主说过的话。
结束本篇篇章。