当前位置: 首页 > 科技观察

消防必备!故障排除和系统优化手册

时间:2023-03-19 20:53:29 科技观察

软件工程领域有一个共识:维护代码所花的时间远比编写代码多。在整个代码维护过程中,最惊险刺激的部分就是故障排除(Trouble-shooting)。尤其是对于那些需要7x24小时在线维护业务的一线服务器程序员来说,大大小小的问题在线排查救火已经成为家常便饭,一不小心可能会吃到自助餐——垂直进入,躺下,退出,我吃不下饭,也吃不起。本文分享笔者排查服务器端问题的一些经验,包括常见问题、排查步骤、排查工具,并结合实际项目中发生的惨痛案例。1故障排除1常见问题知己知彼:知己知彼,方能百战百胜。日常生活中遇到的大部分问题,大致可以分为以下几类:逻辑缺陷:例如NPE,无限循环,未覆盖边界条件。性能瓶颈:例如接口RT急剧增加,吞吐量没有增加。内存异常:例如GC卡顿、FGC频繁、内存泄漏、OOM并发/分布:例如竞争条件,不同步的时钟。数据问题:例如脏数据,序列化失败。安全问题:例如DDoS攻击、数据泄露。环境故障:例如主机宕机、网络故障、丢包。操作错误:例如误配置推送、删库跑路(危险动作,请勿尝试。。)。以上分类可能并不完整和严谨。我想传达的观点是:你也可以积累这样的检查表。遇到想不通的问题,耐心过一遍,说不定很快就可以查号了。2筛查过程医生:你看,小王,这个伤口的形状像漂浮的白云吗?病人:……如果你不给我包扎止血,它会变成一团火烧云。排除快速止血的第一步必须是止血,及时止损。如何快速止血?常见的方法有:发布时报错,发布前一切正常?先不管,先回滚,恢复正常后再慢慢查看。应用稳定运行了很久,突然进程开始退出?可能是内存泄漏,请静默重启大法。只有几台固定机器报告错误?尝试隔离这些机器(关闭流量入口)。单用户流量突然增加导致服务不稳定?如果不想得罪买不起的主顾,请勇敢地推一下限行规则。下游依赖宕机,导致服务雪崩?还想什么,降级计划启动。让现场止血?那么恭喜你,至少故障的影响不会扩大。把锅拿下来喘口气再说话。下一步就是根据线索找出问题的罪魁祸首。作为一个有经验的检查员,你需要有尽可能保持现场的意识,例如:隔离一两台机器:关闭这些机器的入口流量,让它们静静地等待你的检查。Dumpapplicationsnapshot:常用的快照类型一般是线程栈和堆内存映射。所有机器都回滚了,怎么办?不要慌,如果你的应用监控和操作系统足够健全,那么你还有多维度的历史数据可以回溯:应用日志、中间件日志、GC日志、内核日志、Metrics指标等,定位就OK原因,并有排除故障的线索。接下来如何定位具体原因呢?这个环节会综合考验你的技术深度、业务熟悉度、实战经验,因为原因往往千奇百怪,需要逐案跟踪分析。这里有几点排查建议:与近期变化相关:90%以上的线上问题都是由变化引起的,这也是为什么集团安全生产的重点一直放在“变化”的管控上。所以,不要急于否认(“绝对不是我刚加的那行代码的问题!”),相信统计概率,回顾最近的变化历史(由近到远)。全链路跟踪分析:在微服务和中心化盛行的当下,如果一个业务请求不是十个八个应用处理,你写Java都不好意思。所以,不要只盯着自己的应用,需要把调查的范围扩大到整个链路。还原事件时间线:把自己想象成福尔摩斯(柯南也可以),眼前是犯罪现场。你需要做的是将不同时间点的事件的所有线索串联起来,重构还原整个案发过程。相信我,时间戳不会骗人。查找根本原因:在解决许多问题后,您会发现许多疑似原因往往只是另一个更深层次原因的表面结果。作为夏洛克·福尔摩斯,你最需要找到的是幕后凶手,而不是雇来的凶手——否则TA又要雇人来干了。尝试重现问题:在努力推断出根本原因后,不要急于修复错误。如果可能,最好稳定地重现问题,这样更有说服力。这里提醒一下:不要在生产环境下这样做(除非你真的知道自己在做什么),否则可能会造成二次伤害(你:哈哈哈,你看,这把刀本来就是从这个角度刺出来的,并且轨迹完全相同。用户:...)。解决问题终于找到问题根源了,如何完美解决结局呢?几个基本原则:修复也是一种改变,需要经过完整的回归测试和灰度发布;不要急于启动bugfix,这会导致更多的bug需要修复。修复发布后,一定要做在线验证,并持续观察一段时间,确保是真的修复了。最后,如果问题已经上升到故障级别,那就请大家做故障恢复。整个处理过程肯定有改进的余地,你的经验和教训也是其他同学输入和反省的好机会:快乐总是相似的,失败也是如此。3排错工具手里只有锤子,看什么都是钉子。作为一名工程师,您需要的是一整套工具箱。故障排除实际上是一个持续观察应用程序行为的过程。为了确保不遗漏关键细节,你需要让你的应用程序更“可观察”。可以使用三种强大的工具来提高应用程序的可观察性:Logging、Metrics和Tracing。在我之前做过的项目中,这三个能力都是由SLS、Alimonitor/AliMetrics/Tsar、EagleEye提供的,这里不再赘述。另外我也推荐Arthas这个工具,非常实用方便。相信很多同学都已经用过了。二次系统优化只学故障排除是不够的(当然,技巧一定要全,狗屎总是会发生的),再熟练也只能治标不治本。要想避免问题的根源,就必须从系统本身做起:根据性能、稳定性、可维护性三个方向,不断优化你的系统实现,把问题扼杀在摇篮里,让自己每时每刻安然入睡天。老板:一定要快、稳、好。哦,工资不用着急,下个月肯定发。系统优化的三个基本方向:性能、稳定性和可维护性。三者并非完全独立,而是有着复杂的互动关系,有时甚至潮起潮落。最好的软件系统并不是要在这三个方向上做到极致,而是要根据自己实际的业务需求和场景进行合理的选择,在三者之间达到一个综合最优的动态平衡状态,让每一个都可以做得够好了。所以,优化不仅是一门科学,更是一门艺术。1性能优化问题:要跑出最快圈速,车手重要还是车重要?答:两者都很重要。没有男人不喜欢高性能跑车,也没有女人不想看李佳琦直播的时候突然愣住。性能是各行各业工程师追求的极致浪漫。性能指标(Indicators)是衡量事物好坏的科学量化手段。对于性能,一般采用以下指标进行评价:吞吐量:系统在单位时间内能够处理的工作量,例如:在线web系统——QPS/TPS,离线数据分析系统——每秒处理的数据量。响应时间:以Web请求处理为例,响应时间(RT)是请求从发送到接收的往返时间,一般由网络传输延迟、排队延迟和实际处理时间组成。可扩展性:系统通过增加机器资源(垂直/水平)来承载更多工作负载的能力;投入产出比越高(理想情况下是线性缩放),系统的可扩展性越好。另外,同一系统的吞吐率和响应时间一般有如下关系:当吞吐率小于某个临界值时,响应时间几乎不变;一旦超过这个临界值,系统就会进入过载状态(overloaded),响应时间开始线性增加。对于有稳定性要求的系统,在做性能压测和容量规划时,需要充分考虑这个临界值的大小。注意:实际上,更严格地说,性能只是指系统有多“快”;以上一些指标并不仅仅代表系统的快慢,还与速度密切相关。性能分析古人有句古话,不能衡量,就不能改进。优化一个系统的性能(比如Web请求响应时间),首先要准确的衡量和分析当前系统的性能有什么不同:是请求解析不够快,还是查询速度太慢DB?如果是后者,是扫描数据录入阶段太慢,还是返回结果集太慢?或者它只是应用程序和数据库之间的连接?网络延迟太大?任何复杂请求的处理最终都可以分解为一系列并行/串行原子操作。如果只是抓哪个,优化哪个,显然效率不会太高(除非你很幸运)。更合理的做法应该是坚持2/8原则:优先分析和优化系统瓶颈,即当前对系统性能影响最大的原子操作;它们很可能是具有最高ROI的优化点。如何具体量化和分析绩效?下面是一些工具参考:系统级:tsar、top、iostat、vmstat网络级:iftop、tcpdump、wireshark数据库级:SQLexplain、CloudDBA应用代码级:JProfiler、Arthas、jstack其中很多工具也是常用的诊断工具用于故障排除;毕竟性能分析和诊断分析的目的都是为了了解一个系统和它的环境,需要做的事情是相似的。优化原则Whatyoushoulddo:上面已经说了很多,这里再补充一点:性能优化和功能需求一样,都是为业务服务的,所以优化的时候不要忙得不可开交,一定要结合target需求和应用场景——可能你想做的优化线上根本没有;也许很难做的优化可以根据流量特性进行定制和优化。Whatyoushouldnotdo:老生常谈的Premature-optimization和Over-optimization——一般来说(不是绝对),性能优化不是免费的午餐,优化越多,可维护性也会越差。优化方法常用的性能优化方法有哪些?我这里总结了8个套路(最后一个是小霸王一体总结套路)。1)简化一些事情,你可以选择不做。业务层面:例如精简流程和简化要求。编码级别:例如减少循环内的开销操作。架构级别:例如减少不必要的抽象/分层。数据级别:例如数据清洗、提取、聚合。2)有些事情是并行的,可以找人一起做。模式:单机并行(多线程)、多机并行(分布式)。优点:充分利用机器资源(多核、集群)。缺点:同步开销、线程开销、数据倾斜。同步优化:乐观锁、细粒度锁、无锁。线程替代方案(例如协程:JavaWISP、Go例程、Kotlin协程)。数据倾斜:负载均衡(Hash/RR/dynamic)。3)有些事情是异步的,不用等待就可以放手。方法:消息队列+任务线程+通知机制。优点:提高吞吐量,解耦组件,削峰填谷。缺点:排队延迟(队列积压)。避免过多的积压:背压(反应性思维)。4)有些事情可以分批一起做。方法:多个单操作→组合成一个单批操作。案例:TCPNagel算法;DB批量读写接口。优点:避免了单次操作固有的开销,摊销后的总开销更低。缺点:latency延迟+聚合延迟。减少等待延迟:超时触发提交,控制延迟上限。5)时空交换游戏的本质:要么有闲,要么有钱。以空间换时间:避免重复计算,缩短传输距离,分流流量,减轻压力。案例:缓存、CDN、索引、只读副本(replication)。时间换空间:有时可以实现“更快”的结果(减少数据量→减少传输时间)。案例:数据压缩(HTTP/2headercompression,Bitmap)。6)数据结构和算法优化方案=数据结构+算法多了解一些“冷门”的数据结构:Skiplist、Bloomfilter、TimeWheel等一些“简单”的算法思想:递归、分治、贪心、动态编程。7)Pooling&LocalizationSharingEconomy&CommunitySupermarketPooling(Pooling):减少资源创建和销毁的开销。案例:线程池、内存池、DB连接池、Socket连接池。本地化(Localization):避免共享资源竞争开销。案例:TLB(ThreadLocalBuffer),多级缓存(local局部缓存->共享全局缓存)。8)更多优化方式升级奖励:内核、JRE、依赖库、协议。调优大师:配置、JVM、内核、网卡。SQL优化:index、SELECT*、LIMIT1.业务特性的定制和优化:例如在清晨的低营业时间进行日志轮换。混合思路(优势组合):JDKsort()实现,Weex/RN。2稳定性优化稳了,我们才能赢。——by【0杀10死】等待复活的鲁班七号,维稳是我们程序员每天都要思考和讨论的重大问题。什么样的系统被认为是稳定的?我自己写了一个小工具。我在本地运行它从来没有遇到过任何问题。它被认为是稳定的吗?几千人维护淘宝网站,双十一半夜下单经常失败,不稳定?稳定是相对的。业务规模越大,场景越复杂,越容易出现系统不稳定,影响也越严重。不同企业提供的服务类型差异很大。如何用一致的指标来衡量系统的稳定性?标准的做法是定义服务可用性(Availability):只要服务对用户“可用”,系统当前就被认为是稳定的。;否则不稳定。这样,通过采集和聚合就可以得到总的服务可用性/不可用性比率(服务时长或服务次数),从而对系统的稳定性进行监控和量化。但是,什么定义了服务当前是否可用?这确实与业务有关,但大多数同类业务都可以用类似的方式定义。例如,对于一般的网站,我们可以这样定义服务是否可用:API请求返回成功,页面总加载时间<3秒。对于阿里云提供的云产品来说,服务可用性是一个更需要关注和持续提升的指标:阿里云上的很多用户会同时使用多个云产品。被用户的用户感知和放大。因此,底层基础设施越低,可用性要求越高。关于可用性更详细的指标和概念(SLI/SLO/SLA),请参考CloudSmartSLA进一步了解。可用性测量有了以上可用性指标的定义,如何准确测量系统的可用性性能呢?一般来说,有两种方式。1)探针从客户端模拟用户的调用行为。优点:真实数据(客户端角度)缺点:数据不完整(单一客户数据)2)服务器采集从服务器端,直接分析日志和数据。优点:涵盖所有通话数据。缺点:缺少客户端链接数据。对于可用性数据要求高的系统,也可以同时使用以上两种方法。建议结合您的业务场景进行综合评估和选择。优化原则Whatyoushoulddo:关注RT的数据分布(如:p50/p99/p999分位数点),而不是均值(mean)——均值意义不大,你应该多关注你的1%,0.1%用户的准确感受。不该做的事:不要试图承诺和优化可用性到100%——一方面是不可能实现的,有太多的客观不可控因素;另一方面,这是没有意义的,客户几乎不关心0.001%的可用性差异。优化方法常用的稳定性优化方法有哪些?这里也总结了8个套路:1)避免单点父母:一个人漂泊了这么多年,是时候找个人安稳了。如何避免?集群部署数据复制多机房容灾是不够的,还需要具备故障转移能力(Failover)。接入层:DNS、VipServer、SLB。服务层:服务发现+健康检查+淘汰机制。应用层:无状态设计(Stateless),方便随时切换,快速。2)交通管制/限生、学校调整、车牌号限制、景点限制……生活处处被交通控制。类型:QPS流控,并发流控。工具:RateLimiter、Semaphore、Sentinel。粒度:全局、用户级、接口级。热点流量控制:避免意外的流量激增。3)熔断器早上买的股票烧断了,晚上家里的熔断器烧断了……静下心来,及时止损。目的:防止级联故障(雪崩效应)。工具:Hystrix、Failsafe、Resilience4j。功能:自动绕过异常服务并检测恢复状态。过程:关闭→打开→半开。4)降级没时间做饭,今天吃外卖吧……为了健康问题还是少降级为妙。触发原因:流控、熔断、高负载。常见的降级方法:关闭非核心功能:停止打印应用日志牺牲数据时效性:返回缓存中的旧数据牺牲数据准确性:降低数据采样频率5)超时/重试钉钉不返回怎么办?每10分钟Ping一次,1个多小时通话。超时:避免调用者陷入永久阻塞。超时设置:自上而下规划整个链路Timeoutvs.Deadline:最好使用绝对时间Retry:保证可重试操作的幂等性。消息去重异步重试指数退避6)资源限制双倍11如何避免女友败家?提前降低信用卡额度。目的:防止资源被异常流量耗尽。资源类型:线程、队列、DB连接限流方式:资源池、有界队列超限处理:返回ServiceUnavailable/QuotaExceeded7)资源隔离双12闺蜜还是浪子?拿到你自己的卡,别碰我的。目的:防止资源被一些异常流量耗尽;为VIP客户提供服务质量保证(QoS)。隔离方式:队列划分,独立集群;注意处理优先级和资源分配比例。8)平安送女友最后哭着要我剁手?安全第一,宁可心疼,也不愿心疼。程序动态:切换、配置、热升级。开关:类型安全;较少干扰。审计机制:代码审查、发布审批。灰度发布;批量部署;回滚计划。DUCT:自动/手动调整HSF节点权重。3可维护性优化前人栽树,后人乘凉。前人挖坑,后人爽。维护的英文是maintain,也可译为:维护,供应。那么软件维护有多重要呢?它是软件系统的呼吸机和食物管道,是维持软件生命的必要供给。系统开发完成并上线,只是“诞生”而已。软件真正能发挥多大的价值,取决于交付后持续的价值实现过程——是否为用户不断的欣欣向荣、熠熠生辉?还是慢慢退化,逐渐被用户遗忘?这不取决于当下是否足够卓越(性能)和可靠性(稳定性)取决于未来——在瞬息万变的市场环境、客户需求和人为因素中,能否始终保持卓越和足够可靠,以及会越来越好。与性能和稳定性相比,可维护性所体现的价值往往是最长久的,也是最难在短期内实现的。因此,很多软件项目在前期选择牺牲可维护性。这样的决定所带来的后果,就像架构设计一样,几乎不可能(或者需要非常高的成本)来弥补和恢复。太多的软件项目,因为越来越不可维护(代码改不了,bug修不了,功能加不上),只能慢慢退化成没人愿意碰的legacyproject。与性能和稳定性相比,可维护性确实不容易量化(艺术成分>科学成分)。这里我选取了几个指标进行有偏分析:1)复杂性:复杂性是否可控?编码:简洁性、命名一致性、代码行数等。架构:组件耦合、层次清晰、职责单一等。2)可扩展性:是否容易改变?当需要更改代码或配置时,是否简单、优雅且容易出错。3)可操作性:运维方便吗?日志和监控是否完善;是否易于部署和扩展。重要性这里给出几个观点,进一步强调可维护性的重要性。软件生命周期:维护周期>>开发周期。破窗效应,熵增定律:可维护性会越来越差。遗留系统的危害:理解困难、修改成本、变更风险;陷入了踩坑、填坑、再挖坑的循环。优化原则应该做的:遵循KISS原则、DRY原则、各种代码可读性和架构设计原则等。不应该做的:引入过多的临时、Hack代码;功能还行,欠了很多技术债(出来混总是要还的)。优化方法常用的可维护性优化方法有哪些?这里我总结了4个套路:1)编码规范无章可循,不够完善。Coding:推荐《Java 开发手册》,也推荐TheArtofReadableCode这本书。日志:无盲点,无冗余,TraceID。测试:代码覆盖率、自动回归。2)代码重构不要气馁,代码还是可以省下来的。何时重构:任何时候你在代码中闻到难闻的气味。重构节奏:小步迭代,回归验证。Refactoringvs.rewriting:需要综合考虑成本、风险、并行版本维护等因素。推荐阅读:重构:改进现有代码的设计。3)数据驱动相信数据的力量。系统数据:监控覆盖率、Metrics收集等对于理解系统和故障排除至关重要。业务数据:一致性校验、旧数据清洗等;相信数据通常比代码寿命更长。4)技术演进技术是第一生产力。站稳脚跟还是紧跟潮流?需要综合评估风险、生产力和学习成本。目前方向:微服务、容器化。三个结论Truthisundertheskin-真理总是隐藏在皮肤之下。对,就在这句话下面。欢迎各位技术同路人加入阿里云云原生应用研发平台EMAS团队。我们专注于广泛的云原生技术(BackendasaService、Serverless、DevOps、低代码平台等),致力于为企业和开发者提供一站式的应用研发管理服务,内部推荐直接:pengqun.pq#alibaba-inc.com,来信会回复。