为什么你的程序总是出现各种意想不到的 bug? (一)

文章类型:行业资讯    发表2022-01-11   文章编辑:怒熊网络 · 一站式互联网+技术服务商!   阅读:260

声明:本文以下文章来源于程序员鱼皮,作者李鱼皮,如作者不愿意转载请联系网站删除。
 
 
为什么你的程序总是出现 bug?
凭什么让改 bug 占据了你大部分的时间?
看完本文,保证你能设计出更稳定的程序,摆脱 bug 的缠绕,做项目更安心
记得我在学校的时候,做的那些项目,不是为了应付课程作业,就是为了参加比赛时展示用,因此对项目的质量要求非常低。
到底有多低呢?
大部分的项目,只要基本的功能可以使用,就算完成了,完全不考虑任何的异常情况。甚至只要能成功运行一次,让我截几张图放到 PPT 或者实验报告里,足够向老师交差或者应付比赛答辩就行。
那项目出现 bug 怎么办呢?
  • 如果测试的时候发现有些功能不可用,那很简单,不管他,直接 PS 一张正常运行的图就行。

  • 如果比赛的时候发现有些功能不可用,那也很简单,把锅甩给 “现场网络不好” 就行。

但是,这些 “小技巧” 在企业中是行不通的,企业级项目必须为企业带来实际的价值,容不得半点马虎和欺骗。
我第一次进入企业实习时,还保留着自己在学校开发项目的狼性 ,只要能够完成基本功能就行,保证以最快的速度完成开发。
有一天,当我洋洋得意准备早点下班时,测试同学走过来跟我说。
“喂,你的程序有 bug,这里用户下单怎么金额是负的?”
对于我一个初入职场的小白,这是人生中第一次有人说我的代码有 bug,我有问题,我不对劲。
当时,我脑海的第一个念头竟然是怎么把这个 bug 糊弄过去,而不是怎么去更正!看来我已经养成了非常不好的习惯。
那之后几天,我又连续收到了测试提出的多个 bug,然后将他们一个个改正。如果将这样一个漏洞百出的程序发布上线,带来的损失是不可估量的,现在想想仍心有余悸。
这件事之后,我意识到,在企业中开发项目,不能只追求开发时的效率,还要注重项目的稳定性,否则带来的额外返工时间远比开发时节省的时间要长,而且会影响同事对你的看法。如果将开发时产生的 bug 遗留到线上,后果更是不堪设想!
后来,在字节跳动和腾讯这两家大公司工作后,我进一步认识到了项目稳定性有多重要,并且积累了更多只有在大公司才能学到的提升项目稳定性的经验。
我总结了 10 个开发中通常不会考虑到的风险点,以及 16 个减少风险、提升项目稳定性的方法,分享给大家~
分享这些之前,先讲个故事。

bug

达摩克利斯之剑

古希腊传说中,达摩克利斯是公元前 4 世纪意大利叙拉古的僭主(古希腊统治者独有的称号)狄奥尼修斯二世的朝臣,他非常喜欢奉承狄奥尼修斯。
他奉承道:“作为一个拥有权力和威信的伟人,狄奥尼修斯实在很幸运。”
于是狄奥尼修斯提议与他交换一天的身份,那他就可以尝试到首领的命运。
在晚上举行的宴会里,达摩克利斯非常享受成为国王的感觉。当晚餐快结束的时候,他抬头才注意到王位上方仅用一根马鬃悬挂着的利剑。他立即失去了对美食和美女的兴趣,并请求僭主放过他,他再也不想得到这样的幸运。
这个故事告诉我们什么呢?
  1. 在和平安宁之后,时刻存在着危险与不安。

  2. 当一个人获取多少荣誉和地位,他都要付出同样多的代价。

  3. 地位越高,看似越安全,实则越危险。

  4. 居安思危,对随时可能带来的严重后果,要做到谨慎。

那么这和软件开发又有什么关系呢?下面就让我来揭秘软件开发中的达摩克利斯之剑。

危机四伏

“在和平安宁之后,时刻存在着危险与不安。”
软件开发正是如此,表面上机器是 “死” 的,只会按照人输入的指令或编好的程序来执行,一成不变,听话得很。好像我们写好代码扔到机器上后,就可以高枕无忧。
但真的是这样么?我们真的可以信任机器和程序么?
其实,在程序世界中危机四伏,人为因素、环境因素等可能都会对我们的程序产生影响。因此,我们必须时刻坚守软件开发的不信任原则,保持 overly pessimistic(过于悲观),把和程序有关的一切请求、服务、接口、返回值、机器、框架、中间件等等都当做不可信的,步步为营、处处设防。
那为什么写个代码要这么小心翼翼,什么都不信任呢?

大项目的苦衷

“当一个人获取多少荣誉和地位,他都要付出同样多的代价。”
软件开发中,项目价值越大,需要承受的压力也越大,来听听大项目的苦衷吧。
  我是一个身价过亿的大项目,每天服务着上千万的用户,帮助他们获得知识与快乐。
我的小伙伴们只看到我身上的光环和荣耀,但是他们看不到我背负的压力和风险,今天终于有机会和大家倾诉我的苦衷了。
记得很多年前,我还是个孩子,只有几个小主人开发我,那段时间,我成长得很快。虽然只有几十个人使用我,但我感到非常轻松和快乐,偶尔偷会儿懒,也不会被人发现。
后来,我的功能越来越多,越来越强大。每天有数之不尽的新面孔来和我打招呼,并享受我提供的服务。渐渐地,更多开发者在我身上留下了印记,我感觉自己正在变得复杂,也开始感受到了压力。我再也找不到机会偷懒,因为我一旦休息,就会让我的主人们损失一笔不小的财富。
如今,我已经是一个成熟的大项目了,每天有上千万的用户依赖我,我终于拥有了更大的价值,却也增加了很多烦恼,感受到了更大的危险。

首先,同时服务千万用户,每秒钟都可能会有几十万、甚至几百万个请求需要我来处理,因此我必须每时每刻无休止地高负载工作,且不说休息,哪怕稍微慢了一点,就会遭到用户的投诉,主人们也会因此受到批评。
我的运行,必须依靠很多兄弟们的支撑,因此我必须和兄弟们好好相处,哪怕一个兄弟倒了,我都会受到影响。
在我强大的实力背后,有一颗非常脆弱的心。经历了那么多次的强化和改造,我的功能逐渐变多的同时,也因此被植入了各种框架和插件,体积像滚雪球一般越来越大,不知道什么时候就会爆炸。以至于主人们每次改动我时都要万分谨慎,我的成长也变得十分缓慢。复杂然而最让我感到恐惧的,是那些坏家伙们!
他们和正常的用户不同,有的不断制造请求,试图将我击垮。有的绕到我的背后,试图直接控制我。有的对我虎视眈眈,监视并记录我的一举一动。还有的尝试各种非法操作,想从我身上牟取暴利。
作为一个大项目真是太累了,我不知道我还能坚持多久。

真的可信么?

“地位越高,看似越安全,实则越危险。”
如今是一个软件开源和共享的时代,我们在开发项目时,或多或少会使用到网上现有的资源,比如依赖包、工具、组件、框架、接口、现成的云服务等等,这些资源能够大大提升我们的开发效率。
就拿云服务来说,几乎已经成了我们开发必备的资源,以前我们想要做一个网站,可能需要自己买一台物理服务器,然后连通网络,再把项目部署上去。而如今,直接登录大公司的云官网(像腾讯云、阿里云),然后租一台云服务器就行了,非常省事。
再说说现在主流的开发框架,以前做一个简单的网站界面可能只会使用 HTMLCSSJavaScript 这三种最基础的技术,而如今,网站的样式和交互越来越复杂,我们不得不使用一些知名的框架来提升开发效率,比如 VueReact
听起来好像没有任何问题,你也根本不会去怀疑什么,因为我们天生带着对大公司,或者说是对名气的信任
但是,你知道么,当你决定使用其他人的资源时,你就已经把项目系统的部分掌控权、甚至可能是半条命,都交出去了。
那么不妨思考一下,你使用的这些资源,真的可信么?

下面 10 个问题,可能改变你对开发的认知。
1. 开发工具可信么?
我们通常是在大而全的开发工具中编写代码,比如 JetBrains IDEA 或者 Vscode。很多刚开始写代码的同学、甚至是一些经验丰富的老手,都对开发工具保持绝对的信任。
比如你在键盘上敲击 a,那编辑器界面上显示的一定是 a
但是,由于内存不足等种种原因,开发工具其实也会抽风
比如你想要调用某个函数,通常敲击函数名前几个字母后,开发工具就会自动给你提示完整的函数名,但如果开发工具没有给你提示,你首先怀疑的是这个函数不存在,而不是编辑器没有按预期给出提示。遇到这种情况,可以稍等编辑器一下,或者进一步确认函数是否真的不存在,而不是立刻创建一个新的函数。
又或是项目无法运行,怎么排查都觉得没问题,这时不妨重新启动下开发工具,或者清理一下缓存,说不定项目就能正常运行了!
还有很多非常有意思的情况,比如编辑器一片大红,各种提示错误,但是项目依然能成功运行。
因此,不要绝对相信开发工具。

2. 开源项目可信么?

现在是一个软件开源的时代,在 GitHub 等开源项目平台上能够找到大量优秀的开源项目,好的开源项目甚至可以得到 10 万多个关注,那这些知名的开源项目可信么?
不完全可信!从每个开源项目的 Issues 就能看出这点,而且通常越大的项目,被发现的问题越多,比如 Vue 项目,累积提出并关闭了 8000 多个问题。
我记得自己有一次使用知名的开源服务器 Tomcat,就遇到了 bug,每次接受到特定的请求都会报错。刚开始我根本没有怀疑是 Tomcat 的问题,而是绞尽脑汁地想自己的代码哪里写错了。后来经过反复的排查和搜索,终于确认了就是 Tomcat 本身的 bug!
虽然开源项目并不完全可信,但是相对于私有项目而言,所有对项目感兴趣的同学可以共同发现项目中的问题,并加以解决,在一定程度上还是能够提高项目的可靠性的。

3. 依赖库可信么?

我们在开发项目时,通常会用到大量的依赖库。直接在官方依赖源(比如 Mavennpm)搜索依赖库,然后使用包管理器,用一行命令或者编写配置文件就能够让其自动安装依赖,非常方便。
但是,这些发布到官方源的依赖库,就可信么?
且不说基本每个开发者都有机会发布依赖库到官方,就算是互联网大公司的依赖库,也未必可信。
给我印象最深刻的就是阿里巴巴的 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. 缓存服务可信么?

缓存是开发高性能程序必备的技术,通过将数据库等查询较慢的数据存放在内存中,直接从内存中读取数据,以提升查询性能。有了缓存之后,项目不仅能够支持更多人同时查询数据,还能够保护数据库。
目前比较主流的缓存技术有 RedisMemcached 等,可以自己在服务器搭建,也可以直接租用大公司提供的云缓存服务。
那么缓存服务是否可信呢?
项目的并发量不是特别大的话,一般的缓存技术就足以支持了,但是如果项目的量级很大,可能缓存也无法承受住压力,严重时就会宕机。而一旦缓存挂掉,大量的查询命令会直接请求数据库,于是数据库也会在瞬间挂掉,严重时还会导致整个项目瘫痪!
因此,在使用缓存时,需要对并发量进行评估,通过搭建集群和数据同步保证高可用性。此外,还要预防缓存雪崩、缓存穿透、缓存击穿等问题,简单解释一下。
缓存雪崩:指大量缓存在同一时间过期,请求都访问不到缓存,全部打到数据库上,导致数据库挂掉。
缓存穿透:持续访问缓存中不存在的 key 导致请求直接打到数据库上,导致数据库挂掉。
缓存击穿:一个被大量请求高频访问的热点 key 突然过期,导致请求瞬间全部打到数据库上,导致数据库挂掉。
如果不预防这三个问题,即使是租用大公司的缓存服务,也一样吹弹可破。

8. 对象存储可信么?

项目中,经常会有用户上传图片或文件的功能,这类数据通常较大,用数据库存储不太方便。虽然我们可以将文件直接存到服务器上,但更好的做法是使用专门的对象存储服务。
可以简单地把对象存储当做一个大的文件夹,我们可以通过它直接上传和下载文件。大的云服务商也都提供了专业的对象存储服务,而无需自己搭建,那么对象存储可信么?
一般情况下,上传到对象存储的文件是不会缺失或丢失的,而且还可以将已上传的数据进行跨园区同步,起到备份的作用。
但是,记得有一次,上传到对象存储上的文件和源文件竟然不一致,大小足足少了 1M。起初我以为是文件上传到对象存储时,会自动被压缩,但是将对象存储中的文件下载到本地后,发现的确和源文件不一致!虽然出现这种情况的概率极其小,但从那一刻起,我再也不相信对象存储了。
再用自己的真实经历来聊聊对象存储的跨园区同步。因为个人负责的业务比较重要,万一单个机房整体挂掉,可能分分钟是几十万元的损失!因此我为对象存储配置了自动跨园区同步,将文件先上传至广州机房,然后数据会自动同步到上海机房,且运维同学承诺自动同步的延迟不超过 15 分钟。
我相信大部分开发者配置数据同步后也就不管了,相信它一定会自动同步的。结果后面我编写程序去做同步监控、对比数据时,发现经常出现数据未同步的情况,比例高达 10%!
因此,不能完全相信对象存储,虽然大部分情况下大公司的对象存储服务很可靠,但不能确保万无一失。尤其是同步备份的场景下,是否真的同步成功了,又有多少同学关心过呢?不妨写个程序去验证和保障。

9. API 接口可信么?

在开发中,我们经常会调用其他系统提供的 API 接口来轻松实现某种功能。比如查询某地的天气,可以直接调用其他人提供的天气查询接口,而无需自己编写。我们也可以提供 API 接口给其他人使用,尤其是在微服务架构中,各服务之间都是以接口调用的形式实现交互协作的。
几乎所有的 API 接口提供者都会说自己的接口有多安全、请放心使用,那么 API 接口真的可信么?
其实,API 接口是最不可信的资源!
首先,API 接口的提供方可以是任何开发者,很难通过他们的一面之词来确定接口的稳定性和安全性。
即使这个接口性能很高、也很安全,但是你并不了解有多少人和你在同时使用这个接口,也许只有你,又也许是 100 万个其他的开发者呢?在这个竞争条件下,接口的 qps(query per second 每秒查询数)还能达到预期么?接口返回时长真的不会超时么?
更有甚者,偷偷地把 API 接口改动了,却没有给调用者发送通知,这样接口的调用方全部都会调用失败,严重影响项目的运行!
因此,我们在调用第三方 API 接口时,一定要慎重、慎重、再慎重!
此外,如果我们是 API 接口的提供者,也要注意保护好自己的 API 接口,避免同时被太多的开发者调用,导致接口挂掉。

10. Serverless 可信么?

如果说服务器不可信,那我们干脆就不租服务器了,直接租用大公司提供的 Serverless 服务来作为项目的后台不就行了?
Serverless 指无服务器架构,并不是真的不需要服务器,而是将项目接口的部署、运维等需要对服务器的操作交给服务商去做,让开发者无需关心服务器,专心写代码就好。
听起来非常爽,那 Serverless可信么?
使用 Serverless,虽然能够大大提升开发和运维效率,但是其相对服务器等资源而言,更不可信!
首先,Serverless 本身就是部署在服务器上的,难免会受到服务器的影响。
其次,Serverless 服务不会长期保持应用的状态,而是随着请求的到来而启动,存在冷启动时期,虽然也有很多相关的优化和解决方案,但仍无法精确地保证接口的性能,尤其是在高并发场景下,性能往往达不到预期。
最重要的是,当你选择使用 Serverless 服务时,你就和某云服务提供商绑定了,后续想要迁移是非常困难的!试想一下,你项目的所有功能都交给别人来维护,真的是好事么?一旦云服务提供商改造了架构或接口,你的代码也要随之改动,而这种改动却不是由自己控制的!
当然,Serverless 具有非常多的优点,也是云计算技术发展的必然趋势,只是希望大家在使用前,考虑到那些可能的风险,并做好应对措施。
总结:正是因为我们太过信任那些名气大、看似安全的资源,所以其背后的危险才更难以被察觉,带来的后果往往也更致命!
<!--未完 接下文 https://www.nuxiong.com/news_show_6545.html -->