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

程序员如何优雅地解决线上问题?_0

时间:2023-03-17 19:09:56 科技观察

作为程序员,遇到线上问题是家常便饭。如果你看到一群同事深夜围在一起,他们是不是在讨论一些哲学问题?不,他们一定遇到了在线错误。只要线上出现问题,影响到核心业务流程,就是意外,所以一旦发生意外,无论你是约会,周末打游戏,甚至是睡觉,只要接到公司电话,你只能快速连接到公司网络加班。在线问题复杂多变。我们一般把bug分为系统级bug和业务级bug。1.系统级bug业务部署运行在整个系统上。一旦出现系统级的bug,业务就会受到严重拖累。比如CPU爆满,服务不可用,甚至服务器宕机等等,都是系统级的bug。如果是100%CPU,是哪个线程,哪个类,甚至是哪个方法引起的?如果业务流程正常,但部分服务性能滞后,如何快速定位问题?因为是网上发生的,重点是如何快速解决。以下是我最常用的一些故障排除工具。Linux定位工具Linux定位工具perf是Linux的性能分析工具。它的核心功能之一就是查看热门功能的分布情况。用它来生成火焰图来查看函数的资源使用情况。函数的调用堆栈越深,火焰就越高。所以你可以一眼看出异常的功能。如上图所示,通过调用栈可以看到Monitor监听器在反复调用enter和wait。在这种情况下,可以判断程序已经死锁,存在性能问题。假设有大量线程请求这段代码,CPU资源会很快被占满!在著名的“713B站事故”中,技术团队在事故发生时使用当前工具生成了火焰图,并迅速分析出事故根源,就是lua热点功能导致CPU占用100%。怀疑某个进程异常,想快速了解其状态?ps命令:我们项目部署的服务器中运行着很多进程,比如java进程、nginx进程、redis、消息队列进程等等。例如,假设在一个流量高峰期,系统监测到整体服务性能下降了5倍,业务受到严重拖累。如果没有业务层面的bug,大概率是服务性能已经到了瓶颈。如何判断瓶颈在哪里?在大多数情况下,您可以通过系统警报了解大概的问题。如果出现消息堆积,我们应该怀疑消息生产者和消费者的状态。这时候我们就需要详细查看消息队列的流程了。可以使用一些轻量级的linux命令,比如ps:[root@linuxfancy~]#ps-ef|grepqueuejobroot130310Apr17?00:00:00/usr/sbin/queuejobroot326030870Apr17?00:00:00/usr/bin/queuejob/bin/sh-cexec-l/bin/bash-c"envGNOME_SHELL_SESSION_MODE=classicgnome-session--sessiongnome-classic"root2417419508011:39pts/000:00:00grep--color=autossh[root@linux265~]#ps-aux|grepqueueAroot13030.00.0824681204?SsApr170:00/usr/sbin/queueAroot32600.00.052864572?SsApr170:00/usr/bin/queueA/bin/sh-cexec-lroot241880.00.0112652956pts/0S+11:390:00grep--color=autossh这个命令也可以用来排序进程资源使用:[root@linuxfancy~]#psaux|排序-nk3[root@linuxfancy~]#psaux|sort-rnk4我想知道内存和磁盘使用情况?vmstat命令:vmstat是VirtualMeomoryStatistics(虚拟内存统计)的缩写。它是一个监控内存和磁盘使用情况的工具,但也可以用来查看CPU的一些指标,比如中断次数等。用它来查看内存使用和磁盘读写的详细信息。上面头域的说明如下:Procs(进程):r:运行队列中的进程数b:等待IO的进程数Memory(内存):swpd:已用虚拟内存大小free:可用内存大小buff:用于缓冲的内存大小cache:用作缓存的内存大小swap(exchange):si:每秒从swap区写入内存的大小so:写入swap区的内存大小persecondIO:(目前Linux版本的blocksize为1024bytes)bi:每秒读取的块数bo:每秒写入的块数System(系统):in:每秒的中断数,包括时钟中断cs:每秒上下文切换次数CPU(以百分比表示)us:用户进程执行时间(用户时间)sy:系统进程执行时间(系统时间)id:空闲时间(包括IO等待时间),CPU空闲时间wa:IOwaitingtime从上面的命令可以清楚的看出serverpe各方面的表现。此外,在故障排除或调优中还可以使用以下命令:2、如何定位业务级的bug到业务bug?如果有业务bug,那纯粹是开发或者测试锅。确认bug后,第一步肯定是先读log。只要你写需求的时候日志是完整的,有问题的时候会第一时间推送日志或者告警。通过日志,我们可以定位代码对应的bug位置,但这只是第一步,因为你只知道问题出在哪里,并不知道代码哪里出了问题(除非你能一目了然)。所以下一步就是看数据了。数据是业务应用的核心。如果通过日志和页面性能,你的主进程没有问题,那么下一步就是判断表中的数据是否有问题。数据中的BUG表现会表现在各个方面,可能是用户反馈,也可能是工艺错误,这要看你手表的设计。记住!!在线数据是最重要的。当你决定修复数据时,你必须在处理之前做好备份,这样至少可以保证事情不会变得更糟。一般情况下,需要写SQL修改线上数据,经过领导批准后,再交给DBA操作。千万不能删库跑路。假设你的数据被验证是OK的,那么问题很可能是在代码层面。当代程序员最悲哀的时刻无非是一个非常紧急的线上bug需要你去解决,而摆在你面前的却是一堆狗屎山代码!!修改业务bug最重要的是修改bug点,保证其他业务依然可以正常运行。这是牵一发而动全身的事情,否则虫子只会越来越多。所以平时应该预见到这些风险,做好代码设计。总结定位业务bug的正确步骤:3.代码设计一般公司都有自己的代码设计规范。比如从外到内打包代码,每个方法都要有相应的职责,一个方法不要超过100行,一个类不要超过1000行代码。清晰的结构可以让您和其他人更好地审查代码,而不会感到困惑。业务逻辑的写法有两种,一种是简洁明了的线性逻辑,另一种是通过封装代码来降低代码耦合,提高内聚性,也就是我们所说的使用设计模式。两种方式各有优缺点,但我们工作多年写出的代码不能直截了当,总得有点“文艺”吧?推荐一个我经常用但不是特别复杂的设计模式。设计模式工厂模式这是最常用的设计模式之一。工厂模式分为简单工厂模式、工厂方法模式和抽象工厂模式。我们在这里讲解简单工厂模式,因为后两者都是在它的基础上进行改进的。它的结构是这样的:通过定义一个创建对象的接口,让子类决定实例化哪个类。所以本质上就是一个工厂类根据传入的参数动态决定创建产品类(这些产品类继承自父类或接口)的实例。它包括以下角色:工厂(Creator)角色:工厂类中创建产品类的方法可以被外界直接调用,创建需要的产品对象。抽象产品(Product)角色:负责描述所有实例通用的公共接口。具体产品(ConcreteProduct)角色:创建一个目标,所有创建的对象都是充当这个角色的具体类的实例。当遇到需要根据某个前提创建不同的类实现时,可以使用工厂模式。装饰器模式是在不改变原有类结构和继承体系的情况下,动态扩展对象的功能。通过创建一个包装器对象来实现功能扩展,动态地为一个对象添加一些额外的职责。所以装饰者模式分为主体和装饰者。包含的角色如下:Main:业务主要逻辑、字段等。Main组件具体实现类(MainComponent):主要具体实现类。装饰器(Decorator):要做的装饰扩展了逻辑接口。装饰器具体实现类(DecoratorComponent):扩展逻辑的具体实现类。以上两种设计模式都具有“高扩展性”的特点。我们应该根据业务灵活设计接口,避免需求迭代造成一堆又臭又长的代码。但是设计模式不应该用来炫耀技巧。一些比较冷门或者复杂的设计模式是不推荐的,否则当一套代码只能自己维护的时候会很痛苦。.当然,这也能体现出你在公司的不可替代性!4.架构设计系统高性能&高可用使用缓存:缓存的作用是为了系统的可读性。将经常访问的数据丢到缓存中,可以有效提高访问速度,减轻数据库的压力。服务降级&限流:如果短时间内流量激增影响了服务器的性能,可以考虑对边缘业务进行降级,以保证核心业务的可用性和性能。分布式系统&服务拆分:将整个系统拆分成不同的业务模块,然后部署到相应的服务器上。服务通过中间件进行通信,可以有效避免和减少单个服务故障对整个系统的影响。高可用架构:重要性不言而喻。同城多活、异地多活的架构部署,保证单个机房宕机时,流量可以快速切换到其他机房,不影响核心业务。可谓是防止系统宕机的必备良药!做好事故审查会说,小事故伤身,大事故背水一战。.发生事故后写事故单是不可避免的。除了详细描述事故发生的过程、责任人、解决方案外,事故的后续跟进也是一系列程序,可能需要数周时间才能跟进。事故的发生往往对团队的技术发展和形成起到积极的推动作用,所以事故对于每一个团队来说都是不可避免的。每次发生事故,我们都要思考如何完善系统,打破技术壁垒。而且遇到事情也不要惊慌,如果出了大问题,那领导肯定是第一个被责备的!事实上,一般公司都喜欢能快速解决问题的员工,即使这些问题可能是你造成的。