为什么你的程序总是出现各种意想不到的 bug? (一)
文章类型:行业资讯 发表2022-01-11 文章编辑:怒熊网 · 一站式互联网+技术服务商! 阅读:122次
声明:本文以下文章来源于程序员鱼皮,作者李鱼皮,如作者不愿意转载请联系网站删除。
为什么你的程序总是出现 bug?
看完本文,保证你能设计出更稳定的程序,摆脱 bug 的缠绕,做项目更安心 记得我在学校的时候,做的那些项目,不是为了应付课程作业,就是为了参加比赛时展示用,因此对项目的质量要求非常低。大部分的项目,只要基本的功能可以使用,就算完成了,完全不考虑任何的异常情况。甚至只要能成功运行一次,让我截几张图放到 PPT 或者实验报告里,足够向老师交差或者应付比赛答辩就行。
-
如果测试的时候发现有些功能不可用,那很简单,不管他,直接 PS 一张正常运行的图就行。
-
如果比赛的时候发现有些功能不可用,那也很简单,把锅甩给 “现场网络不好” 就行。
但是,这些 “小技巧” 在企业中是行不通的,企业级项目必须为企业带来实际的价值,容不得半点马虎和欺骗。我第一次进入企业实习时,还保留着自己在学校开发项目的狼性 ,只要能够完成基本功能就行,保证以最快的速度完成开发。有一天,当我洋洋得意准备早点下班时,测试同学走过来跟我说。“喂,你的程序有 bug,这里用户下单怎么金额是负的?”对于我一个初入职场的小白,这是人生中第一次有人说我的代码有 bug,我有问题,我不对劲。当时,我脑海的第一个念头竟然是怎么把这个 bug 糊弄过去,而不是怎么去更正!看来我已经养成了非常不好的习惯。那之后几天,我又连续收到了测试提出的多个 bug,然后将他们一个个改正。如果将这样一个漏洞百出的程序发布上线,带来的损失是不可估量的,现在想想仍心有余悸。这件事之后,我意识到,在企业中开发项目,不能只追求开发时的效率,还要注重项目的稳定性,否则带来的额外返工时间远比开发时节省的时间要长,而且会影响同事对你的看法。如果将开发时产生的 bug 遗留到线上,后果更是不堪设想!后来,在字节跳动和腾讯这两家大公司工作后,我进一步认识到了项目稳定性有多重要,并且积累了更多只有在大公司才能学到的提升项目稳定性的经验。我总结了 10 个开发中通常不会考虑到的风险点,以及 16 个减少风险、提升项目稳定性的方法,分享给大家~在分享这些之前,先讲个故事。
达摩克利斯之剑
古希腊传说中,达摩克利斯是公元前 4 世纪意大利叙拉古的僭主(古希腊统治者独有的称号)狄奥尼修斯二世的朝臣,他非常喜欢奉承狄奥尼修斯。他奉承道:“作为一个拥有权力和威信的伟人,狄奥尼修斯实在很幸运。”于是狄奥尼修斯提议与他交换一天的身份,那他就可以尝试到首领的命运。在晚上举行的宴会里,达摩克利斯非常享受成为国王的感觉。当晚餐快结束的时候,他抬头才注意到王位上方仅用一根马鬃悬挂着的利剑。他立即失去了对美食和美女的兴趣,并请求僭主放过他,他再也不想得到这样的幸运。
-
当一个人获取多少荣誉和地位,他都要付出同样多的代价。
那么这和软件开发又有什么关系呢?下面就让我来揭秘软件开发中的达摩克利斯之剑。
危机四伏
软件开发正是如此,表面上机器是 “死” 的,只会按照人输入的指令或编好的程序来执行,一成不变,听话得很。好像我们写好代码扔到机器上后,就可以高枕无忧。其实,在程序世界中危机四伏,人为因素、环境因素等可能都会对我们的程序产生影响。因此,我们必须时刻坚守软件开发的不信任原则,保持 overly pessimistic
(过于悲观),把和程序有关的一切请求、服务、接口、返回值、机器、框架、中间件等等都当做不可信的,步步为营、处处设防。
大项目的苦衷
“当一个人获取多少荣誉和地位,他都要付出同样多的代价。”软件开发中,项目价值越大,需要承受的压力也越大,来听听大项目的苦衷吧。 我是一个身价过亿的大项目,每天服务着上千万的用户,帮助他们获得知识与快乐。我的小伙伴们只看到我身上的光环和荣耀,但是他们看不到我背负的压力和风险,今天终于有机会和大家倾诉我的苦衷了。记得很多年前,我还是个孩子,只有几个小主人开发我,那段时间,我成长得很快。虽然只有几十个人使用我,但我感到非常轻松和快乐,偶尔偷会儿懒,也不会被人发现。后来,我的功能越来越多,越来越强大。每天有数之不尽的新面孔来和我打招呼,并享受我提供的服务。渐渐地,更多开发者在我身上留下了印记,我感觉自己正在变得复杂,也开始感受到了压力。我再也找不到机会偷懒,因为我一旦休息,就会让我的主人们损失一笔不小的财富。如今,我已经是一个成熟的大项目了,每天有上千万的用户依赖我,我终于拥有了更大的价值,却也增加了很多烦恼,感受到了更大的危险。
首先,同时服务千万用户,每秒钟都可能会有几十万、甚至几百万个请求需要我来处理,因此我必须每时每刻无休止地高负载工作,且不说休息,哪怕稍微慢了一点,就会遭到用户的投诉,主人们也会因此受到批评。我的运行,必须依靠很多兄弟们的支撑,因此我必须和兄弟们好好相处,哪怕一个兄弟倒了,我都会受到影响。在我强大的实力背后,有一颗非常脆弱的心。经历了那么多次的强化和改造,我的功能逐渐变多的同时,也因此被植入了各种框架和插件,体积像滚雪球一般越来越大,不知道什么时候就会爆炸。以至于主人们每次改动我时都要万分谨慎,我的成长也变得十分缓慢。复杂然而最让我感到恐惧的,是那些坏家伙们!他们和正常的用户不同,有的不断制造请求,试图将我击垮。有的绕到我的背后,试图直接控制我。有的对我虎视眈眈,监视并记录我的一举一动。还有的尝试各种非法操作,想从我身上牟取暴利。作为一个大项目真是太累了,我不知道我还能坚持多久。
真的可信么?
如今是一个软件开源和共享的时代,我们在开发项目时,或多或少会使用到网上现有的资源,比如依赖包、工具、组件、框架、接口、现成的云服务等等,这些资源能够大大提升我们的开发效率。就拿云服务来说,几乎已经成了我们开发必备的资源,以前我们想要做一个网站,可能需要自己买一台物理服务器,然后连通网络,再把项目部署上去。而如今,直接登录大公司的云官网(像腾讯云、阿里云),然后租一台云服务器就行了,非常省事。再说说现在主流的开发框架,以前做一个简单的网站界面可能只会使用 HTML
、CSS
、JavaScript
这三种最基础的技术,而如今,网站的样式和交互越来越复杂,我们不得不使用一些知名的框架来提升开发效率,比如 Vue
和 React
。听起来好像没有任何问题,你也根本不会去怀疑什么,因为我们天生带着对大公司,或者说是对名气的信任。但是,你知道么,当你决定使用其他人的资源时,你就已经把项目系统的部分掌控权、甚至可能是半条命,都交出去了。
我们通常是在大而全的开发工具中编写代码,比如 JetBrains IDEA
或者 Vscode
。很多刚开始写代码的同学、甚至是一些经验丰富的老手,都对开发工具保持绝对的信任。比如你在键盘上敲击 a
,那编辑器界面上显示的一定是 a
。但是,由于内存不足等种种原因,开发工具其实也会抽风。比如你想要调用某个函数,通常敲击函数名前几个字母后,开发工具就会自动给你提示完整的函数名,但如果开发工具没有给你提示,你首先怀疑的是这个函数不存在,而不是编辑器没有按预期给出提示。遇到这种情况,可以稍等编辑器一下,或者进一步确认函数是否真的不存在,而不是立刻创建一个新的函数。又或是项目无法运行,怎么排查都觉得没问题,这时不妨重新启动下开发工具,或者清理一下缓存,说不定项目就能正常运行了!还有很多非常有意思的情况,比如编辑器一片大红,各种提示错误,但是项目依然能成功运行。
2. 开源项目可信么?
现在是一个软件开源的时代,在 GitHub
等开源项目平台上能够找到大量优秀的开源项目,好的开源项目甚至可以得到 10 万多个关注,那这些知名的开源项目可信么?不完全可信!从每个开源项目的 Issues 就能看出这点,而且通常越大的项目,被发现的问题越多,比如 Vue
项目,累积提出并关闭了 8000 多个问题。我记得自己有一次使用知名的开源服务器 Tomcat
,就遇到了 bug,每次接受到特定的请求都会报错。刚开始我根本没有怀疑是 Tomcat
的问题,而是绞尽脑汁地想自己的代码哪里写错了。后来经过反复的排查和搜索,终于确认了就是 Tomcat
本身的 bug!虽然开源项目并不完全可信,但是相对于私有项目而言,所有对项目感兴趣的同学可以共同发现项目中的问题,并加以解决,在一定程度上还是能够提高项目的可靠性的。
3. 依赖库可信么?
我们在开发项目时,通常会用到大量的依赖库。直接在官方依赖源(比如 Maven
和 npm
)搜索依赖库,然后使用包管理器,用一行命令或者编写配置文件就能够让其自动安装依赖,非常方便。且不说基本每个开发者都有机会发布依赖库到官方,就算是互联网大公司的依赖库,也未必可信。给我印象最深刻的就是阿里巴巴的 JSON
序列化类库 fastjson
,几乎无人不知、无人不晓,因为其极快的解析速度广受好评。但是,这个库被多次曝光存在高危漏洞,可以让攻击者远程执行命令!一般的开发者根本不会发现这点,从而给项目带来了极大的危害。因此,在选用依赖库的时候,要做好充分的调研,尽量确认依赖库的安全,并且保证不要和已有的依赖冲突。
4. 编程语言可信么?
Java
是一种强类型语言,具有健壮性。这句话我相信所有学过 Java
的同学都再熟悉不过了。但是,强类型编程语言就一定可信么?这里可能有同学就要表示怀疑了,如果我们一直使用的最基础最底层的编程语言都存在 bug,那我们怎么去相信建立在这些编程语言上的框架呢?然而真相是,所有的编程语言都有 bug!而且基本每次编程语言发布新版本时都会对一些历史 bug 进行修正。就 Java
而言,甚至还有一个专门记录 bug 的数据库!但是,对于大多数开发者来说,我相信即使在程序中偶然触发了编程语言本身的 bug,也没有足够的自信去质疑,而是直接修改代码来绕过。确实,质疑编程语言需要一定的基础和知识储备,但是一旦发现了程序中莫名其妙的问题,建议大家不要直接忽略,可以花一些时间去探索研究,说不定你就成功地发现了一个重大的 bug,也能够加深对这门编程语言的理解。
5. 服务器可信么?
服务器是项目赖以生存的宿主,服务器的性能和稳定性将直接影响到项目进程。无论是个人开发者还是企业,通常都会直接租用大公司提供的云服务器来部署项目,省去了自己搭建和维护的麻烦。不完全可信!即使现在的云服务器提供商都承诺自己的服务 SLA(服务级别协议)可以达到 5 个 9(99.999% 一年约宕机 5 分钟),甚至 6 个 9(99.9999% 一年约宕机 30 秒),但是仍然存在一定的风险。有一个非常有名的案例,在 2013 年,中国最大的社交通讯软件出现大规模的故障,多达几亿用户受到影响。原因竟然是,市政道路建设的一个不注意,把网络光缆挖断了,就导致该软件所在服务器的无法访问。除了可用性的不可信之外,可能还有一些安全隐私方面的问题。当然云服务商通常是不会获取用户的数据的,但也没有办法绝对相信他们。毕竟数据的隐私对企业至关重要,这也是为什么大的公司都会搭建属于自己的服务器机房和网络。
6. 数据库可信么?
企业中的大多数业务数据都是存放在数据库中的,通过项目后端程序来操作和查询数据库中的数据。和服务器一样,我们可以使用软件自己搭建数据库,比如 MySQL
,也可以直接租用大公司的云数据库,那么数据库可信么?其实在企业后端项目中,数据库通常是性能瓶颈,相对比较脆弱,当访问并发量大一点时,数据库的查询性能就会下降,严重时可能整个宕机!即使是大公司提供的云数据库服务,遇到慢查询(需要较长时间的查询)时,可能也无从应对。数据库中的数据其实也未必可信,有时管理员的一个误操作,不小心删除数据或添加了一条错误数据,可能就会影响用户,造成损失。更有甚者,竟然删库跑路,不讲码德!因此,不要过于信任数据库,应当使用缓存之类的技术帮助数据库分担压力,并定期备份。否则一旦数据库宕机或数据丢失,带来的损失是不可估量的!
7. 缓存服务可信么?
缓存是开发高性能程序必备的技术,通过将数据库等查询较慢的数据存放在内存中,直接从内存中读取数据,以提升查询性能。有了缓存之后,项目不仅能够支持更多人同时查询数据,还能够保护数据库。目前比较主流的缓存技术有 Redis
、Memcached
等,可以自己在服务器搭建,也可以直接租用大公司提供的云缓存服务。项目的并发量不是特别大的话,一般的缓存技术就足以支持了,但是如果项目的量级很大,可能缓存也无法承受住压力,严重时就会宕机。而一旦缓存挂掉,大量的查询命令会直接请求数据库,于是数据库也会在瞬间挂掉,严重时还会导致整个项目瘫痪!因此,在使用缓存时,需要对并发量进行评估,通过搭建集群和数据同步保证高可用性。此外,还要预防缓存雪崩、缓存穿透、缓存击穿等问题,简单解释一下。缓存雪崩:指大量缓存在同一时间过期,请求都访问不到缓存,全部打到数据库上,导致数据库挂掉。缓存穿透:持续访问缓存中不存在的 key 导致请求直接打到数据库上,导致数据库挂掉。缓存击穿:一个被大量请求高频访问的热点 key 突然过期,导致请求瞬间全部打到数据库上,导致数据库挂掉。如果不预防这三个问题,即使是租用大公司的缓存服务,也一样吹弹可破。
8. 对象存储可信么?
项目中,经常会有用户上传图片或文件的功能,这类数据通常较大,用数据库存储不太方便。虽然我们可以将文件直接存到服务器上,但更好的做法是使用专门的对象存储服务。可以简单地把对象存储当做一个大的文件夹,我们可以通过它直接上传和下载文件。大的云服务商也都提供了专业的对象存储服务,而无需自己搭建,那么对象存储可信么?一般情况下,上传到对象存储的文件是不会缺失或丢失的,而且还可以将已上传的数据进行跨园区同步,起到备份的作用。但是,记得有一次,上传到对象存储上的文件和源文件竟然不一致,大小足足少了 1M。起初我以为是文件上传到对象存储时,会自动被压缩,但是将对象存储中的文件下载到本地后,发现的确和源文件不一致!虽然出现这种情况的概率极其小,但从那一刻起,我再也不相信对象存储了。再用自己的真实经历来聊聊对象存储的跨园区同步。因为个人负责的业务比较重要,万一单个机房整体挂掉,可能分分钟是几十万元的损失!因此我为对象存储配置了自动跨园区同步,将文件先上传至广州机房,然后数据会自动同步到上海机房,且运维同学承诺自动同步的延迟不超过 15 分钟。我相信大部分开发者配置数据同步后也就不管了,相信它一定会自动同步的。结果后面我编写程序去做同步监控、对比数据时,发现经常出现数据未同步的情况,比例高达 10%! 因此,不能完全相信对象存储,虽然大部分情况下大公司的对象存储服务很可靠,但不能确保万无一失。尤其是同步备份的场景下,是否真的同步成功了,又有多少同学关心过呢?不妨写个程序去验证和保障。
9. API 接口可信么?
在开发中,我们经常会调用其他系统提供的 API 接口来轻松实现某种功能。比如查询某地的天气,可以直接调用其他人提供的天气查询接口,而无需自己编写。我们也可以提供 API 接口给其他人使用,尤其是在微服务架构中,各服务之间都是以接口调用的形式实现交互协作的。几乎所有的 API 接口提供者都会说自己的接口有多安全、请放心使用,那么 API 接口真的可信么?首先,API 接口的提供方可以是任何开发者,很难通过他们的一面之词来确定接口的稳定性和安全性。即使这个接口性能很高、也很安全,但是你并不了解有多少人和你在同时使用这个接口,也许只有你,又也许是 100 万个其他的开发者呢?在这个竞争条件下,接口的 qps
(query per second 每秒查询数)还能达到预期么?接口返回时长真的不会超时么?更有甚者,偷偷地把 API 接口改动了,却没有给调用者发送通知,这样接口的调用方全部都会调用失败,严重影响项目的运行!因此,我们在调用第三方 API 接口时,一定要慎重、慎重、再慎重!此外,如果我们是 API 接口的提供者,也要注意保护好自己的 API 接口,避免同时被太多的开发者调用,导致接口挂掉。
10. Serverless 可信么?
如果说服务器不可信,那我们干脆就不租服务器了,直接租用大公司提供的 Serverless 服务来作为项目的后台不就行了?Serverless 指无服务器架构,并不是真的不需要服务器,而是将项目接口的部署、运维等需要对服务器的操作交给服务商去做,让开发者无需关心服务器,专心写代码就好。使用 Serverless,虽然能够大大提升开发和运维效率,但是其相对服务器等资源而言,更不可信!首先,Serverless 本身就是部署在服务器上的,难免会受到服务器的影响。其次,Serverless 服务不会长期保持应用的状态,而是随着请求的到来而启动,存在冷启动时期,虽然也有很多相关的优化和解决方案,但仍无法精确地保证接口的性能,尤其是在高并发场景下,性能往往达不到预期。最重要的是,当你选择使用 Serverless 服务时,你就和某云服务提供商绑定了,后续想要迁移是非常困难的!试想一下,你项目的所有功能都交给别人来维护,真的是好事么?一旦云服务提供商改造了架构或接口,你的代码也要随之改动,而这种改动却不是由自己控制的!当然,Serverless 具有非常多的优点,也是云计算技术发展的必然趋势,只是希望大家在使用前,考虑到那些可能的风险,并做好应对措施。总结:正是因为我们太过信任那些名气大、看似安全的资源,所以其背后的危险才更难以被察觉,带来的后果往往也更致命!<!--未完 接下文 https://www.nuxiong.com/news_show_6545.html -->