编程十年,「重启程序」如影随形

时间:2021-01-19 01:02:40

「重启程序」对我来讲,太熟悉了,我很纠结,因为它能帮我解决问题,但出于程序员对于程序健壮的强迫症,我又本能的抗拒。

编程十年,慢慢的,我才发现:

重启程序」表面看是一个技术问题,但本质上是一个管理问题

1 一路重启

2010 - 2014 年 ,我经历了一家彩票网站重构的整个过程 ,工作上我全情投入,学习技术如饥似渴,遇到了极多稀奇古怪的问题,取得了很大的进步 。

可是,公司的大小事故不断,研发人员经常使用重启来解决问题。

第一次生产环境重启,也是我的人生第一次重大 BUG ,我负责的用户中心在上线后隔一段时间变会内存溢出。我站在运维同学那里,看着他调整 tomcat jvm 参数 ,不知所措 。但就算调整了,只能是延缓溢出的时间,过了几个小时服务还是内存溢出了。运维每隔一段就主动的把应用重启了,这样业务看起来能正常运行。

后来发现我在使用 ibatis 的时候 ,使用类似的 SQLMap,前端又没有验证,数据库执行了全表查询,从而导致 JVM OOM 。

编程十年,「重启程序」如影随形

这次事故给了我很大的刺激,事故的原因只是因为我写的程序存在严重的 BUG, 程序并不能控制好内存资源,

只能通过外力暴力的「重启」来释放系统资源

随着彩票订单量的激增,系统遇到了越来越多的问题,采取重启程序方案频率也越来越高。

交易的核心服务「调度中心」重启事件让我至今记忆犹新。

某一天双色球投注截止,调度中心无法从消息队列中消费数据。消息总线处于只能发,不能收的状态下。整个技术团队都处于极度的焦虑状态,“要是出不了票,那可是几百万的损失呀,要是用户中了两个双色球?那可是千万呀”。大家急得像热锅上的蚂蚁。

这也是整个技术团队第一次遇到消费堆积的情况,大家都没有经验。

首先想到的是多部署几台调度中心服务,部署完成之后,调度中心消费了几千条消息后还是 Hang 住了。这时,架构师只能采用重启的策略。你没有看错,就是重启大法。说起来真的很惭愧,但当时真的只能采用这种方式。

调度中心重启后,消费了一两万后又 Hang 住了。只能又重启一次。来来回回持续20多次,像挤牙膏一样。而且随着出票截止时间的临近,这种思想上的紧张和恐惧感更加强烈。终于,通过1小时的手工不断重启,消息终于消费完了。

我当时正好在读毕玄老师的《分布式 Java 应用基础与实践》,猜想是不是线程阻塞了,于是我用 Jstack 命令查看堆栈情况。果然不出所料,线程都阻塞在提交数据的方法上。

编程十年,「重启程序」如影随形

我们马上和 DBA 沟通,发现 oracle 数据库执行了非常多的大事务,每次大的事务执行都需要30分钟以上,导致调度中心的调度出票线程阻塞了。

技术部后来采取了如下的方案规避堆积问题:

1、生产者发送消息的时候,将超大的消息拆分成多批次的消息,减少调度中心执行大事务的几率;

2、数据源配置参数,假如事务执行超过一定时长,自动抛异常,回滚。

调度中心做为彩票交易最核心的系统,团队愿意投入资源去优化,可是周边的系统呢 ?举两个例子:

1、团建路上,运维同学半开玩笑的说:”这一路上,几个小时,我都重启了三个系统了,要不我定时给你们重启得了"。大家都尴尬的笑着。

2、黑客打电话给我们客服,嘲笑我们:“你们的程序员没脑子,写个 JSP 都能让我来拖库“。最后勒索了公司几万块钱。这次黑客勒索事件,也让当时的很多同事倍感挫败感。

我经常去做救火队长,帮大家解决疑难杂症,绝大部分需要重启的应用是没有控制好系统资源(比如内存泄露,socket 连接泄露),严格把控好若干环节,就能避免重启的发生。

现在想来,技术团队负责人的管理还是太过于粗糙,不重视技术储备流程管理,再加上研发人员的工作负担重,基本没有精力优化代码,核心代码质量很差,发生了很多的生产事故,重启程序也成为了常态 。

2 质量意识

当我离开彩票公司,先后加入艺龙网,神州专车时,那些如影随行的重启短暂的消失了。

在艺龙 ,我接触到了 AOS 平台,一个集监控、部署、CMDB 于一体的平台,确实让我惊艳不已。

  • 部署竟会变得如此简单,一切都是严格按照流程进行,不再像彩票公司时那般随意的手工上线;
  • 监控体系更加完备,系统异常时响应会更加及时;

同时,正是因为有流程管控,艺龙的工程师对质量也有底线意识。CTO 曾经在开会中不止一次提到敬畏一词,注重代码质量,同时敬畏生产环境的任何操作。

在神州,架构师的水平很高,基础架构团队提供的服务也比较完善,研发同学只需要负责将业务代码写好就行。

记得一次 zookeeper 事故,zookeeper 集群重启之后,服务虽然恢复了,但架构师们还是直面问题,采用了优化 MetaQ 的写入逻辑和迁移 zk 集群两种方案彻底解决了问题。

重视质量,优化管理流程才是减少重启的手段。

3 凤凰架构

我从内心已经认识到:"技术管理者应该有质量意识,但假如程序员自身遇到不得不重启的局面,短时间解决不了时,该如何处理呢 ? "

两年前的一次性能优化,让我从架构设计层面对「重启程序」有了另外的思考 。

当时接手了一个转码服务,转码是将老师上传的文件转换成目标格式。比如课件文件 ppt 或者 word ,转换成 HTML , 或者 PDF 格式等。

转码服务部署在多台物理机上,每隔一段时间,服务的性能就下降得极快,一次转码的耗时从几十秒增长到十几分钟 。而且业务量增长很快,已经有100多万转码任务在堆积了。

通过一周的不间断的观察,发现国外的一个商业版的转码插件,存在内存泄露的问题。

可是怎么解决呢 ?看源码吗 ?软件闭源。我试图反编译,但反编译的代码可读性极差。为了让任务快速执行完成,我还是使用了非常抗拒的方案:"让运维同学写脚本,每隔一个小时重启转码服务"。效果和我预期中的一样,堆积的任务不久就执行完成了。

看起来解决了,但我深知:"这仅仅是临时的方案"。可是有什么好的解决方案吗?

当时正好在读周志明老师的《凤凰架构》,书中的一句话让我思考良久:

编程十年,「重启程序」如影随形

“如果系统中某个部分采用了由极不靠谱的人员所开发的极不靠谱的程序,哪怕存在严重的内存泄露问题,哪怕最多只能服务三分钟就会崩溃,只要整体架构设计有恰当自动化的错误熔断,服务淘汰,重试机制,从系统外部来看,架构依然表现出稳定和健壮的服务能力”。

按照这个思路,只要在架构设计层面屏蔽商用转码软件的 BUG,不就可以了吗?

编程十年,「重启程序」如影随形

将转码角色分为三部分:调度器 ,客户端,转码服务 ,转码服务是 worker ,负责执行任务即可,调度器会将不同的任务分配到不同的服务器上,客户端会上报每个转码服务的状态,当转码服务出现问题时,调度器可以通过客户端重启/关闭/暂停转码服务,在调度任务的过程中,需要保证任务消息不丢失。

我完成了心理建设:“作为程序员需要避免使用「重启程序」的方案,假如遇到迫不得已的场景,需要从架构层面做好设计(有恰当自动化的错误熔断,服务淘汰,重试机制),从系统外部来看,架构依然表现出稳定和健壮的服务能力”。

可是这样就结束了吗 ?好的架构设计确实能大大提升程序的容错率,但企业愿意投入资源推进吗?冰冻三尺,非一日之寒,转码内存问题肯定已存在许久,为什么直到我接手才把这个问题暴露出来 ?原来肯定都出现过问题,大家都不在意而已。

但个人的精力终究有限,我又陷入了怀疑。

3 自媒体九边

2021年,我无意中读到了自媒体九边的一篇文章《中国制造,比德国制造到底差在哪?》,看完之后,我恍然大悟,醍醐灌顶。

企业本身不太在意质量和管理方式的缺陷,毕竟人口红利依然存在,大不了通过加班方式补偿问题。管理简单,成本不高,自然就没有动机优化管理,质量问题频现也就很自然了”。


下文摘自《中国制造,比德国制造到底差在哪?》:

还在我大学的时候,当时院里请了一个国内著名的软件公司负责人去演讲,当时他讲到一个我当时觉得很牛逼的事。

他说,他们的软件在服务器上线后,有个内存泄露问题,非常缓慢,差不多一个礼拜能泄露完,到时候服务器就会重启。如果那个时候你正在玩他们的游戏,就会出现“服务器不在线的状态”,过一会儿服务器重启完就好了。

这个问题也不是解决不了,不过需要很大的人力排查代码,成本非常高。后来他们的技术骨干想了一个办法,说是每隔六天凌晨主动重启一次服务器,这样既不影响业务(一般凌晨还在线的用户比较少),还可以给公司省一笔排查代码的钱

当时大家非常感慨,这么一目了然的解决方案,为啥自己没想到,大神就是大神。

很快地,我毕业进了一家公司,我们这家公司在业界已经基本没了对手,无论是海外还是国内。工作了一些时间,我才意识到大学听到的那个故事毒性有多大,到底是多么傻逼的人,才会想出来这样的办法,只有低级作坊才会干这么缺心眼的事。稍微高级点的作坊会怎么做呢?

只有一个办法就是把这个问题彻底解决了,不惜代价,不计成本,把这个问题解决了,并且把经验推广,避免今后再出现这类问题

很简单的道理,你做任何一个产品,其实整个开发过程,其实就是一个个“设计-开发-发现问题-解决问题”的过程。

现代的产品还有个更重要的特性,就是需要不断的迭代,本来只开发了一个简单功能的产品,后来客户有了新需求,又得加,后来新需求越来越多,过几年变得几乎看不出来原本的产品到底长啥样。就好像手机最早只能打电话,后来发短信,再后来功能越来越多,现在啥都干,唯独打电话非常少了。

再深入一点,那种复杂的产品,比如手机或者火箭,每个部件都需要打到恐怖的精度,因为每个部件精度差一点点,集合在一起就成一堆太空垃圾了,根本没法用。国内长期以来饱受这类毒鸡汤困扰,把各种偷奸耍滑当成本事

还有另一层次,最关键的是流程。即使每个人都兢兢业业,也难免会偶尔出错,就需要测试、闭环流程追踪测试出来的问题,防止哪天有谁懈怠了,照样可以保证产品质量,这就是流程驱动。

第三层次就是需要经验丰富的大神对系统改进,留有更多升级迭代的空间余量。

如果能够与国外本行业顶尖公司使用同一套开发流程和标准,把质量提高,产品还便宜,自然会活得好。

我们要想打出局面,必须扎扎实实一步一步走,以客户为中心,不抖机灵,把流程思维融入企业运行中。这个逻辑就是“勤勤恳恳做技术,不断迭代,用流程来保证质量”,但是需要时间。

某种意义上讲,我国很多企业之前过得太舒服,因为以前我国的人力实在是太便宜,打得国外低端产品根本没有任何还手之力。大部分企业也不太在意质量和管理方式的缺陷,毕竟完全可以通过堆人力来解决,就比如我上文提到的那事,很多企业对质量兴趣不大,怎么省钱怎么来,也是建立在一种观念上:企业主觉得就算出问题,将来也可以通过廉价劳动力不断加班来补偿问题

欧美学者有个断言,说是为啥欧洲有工业革命而中国没有?因为中国当时人太多,有啥事砸人力就可以了,根本没必要发展机械,任何机械和新发明都会因为找不到用的地方最后归于湮灭。人口下降当然有坏处,但是也预示着一个更加尊重劳动者的时代,而不是那种“你不干就滚,有的是人干”,这才是更加健康更加长久的道路。

人口太充裕,是中国崛起的重要力量,也造成了大量企业对人力的滥用,只有资源不足的时候,大家才会通过复杂工具和流程来仔细控制成本

我国接下来肯定是两条腿走路,既要修复之前被“毒鸡汤”毒害的心灵,把质量意识灌输到一代人脑子里,也要鼓励突破给年轻人和梦想家机会,除此之外也没啥好的选择。

4 总结

最近 ChatGPT 大火,我也尝试让他回答“如何看待重启程序” 这个问题。

编程十年,「重启程序」如影随形

ChatGPT 真的是一个耿直 Boy , 他的回答我是认同的。

重启程序是一个简单而有效的解决问题的方法,但是,如果程序频繁出现问题并需要经常重启,那么可能需要深入调查和解决程序的根本问题,以确保程序的稳定性和可靠性。

作为一个程序员,我们应该质量意识,通过好的架构设计提升系统的容错性。

同时,我们也需要认识到:"工程师红利依然存在,很多企业把滥用人力和耍小聪明当做理所应当" 。

最后,我想说:"所有的改变都需要时间,勤勤恳恳做技术/产品,不断迭代,用流程来保证质量才是王道"。