Go技巧 - 偶现的问题怎么解?
在我们日常排查异常时,必现的问题不难解决,让开发者们头痛的问题往往是偶现的。
我将偶现的问题分为两类 - 程序类与业务类,分开进行讲解:
程序类问题 - 注重可观测性的埋点
程序类问题,包括Go
语言中的panic
、请求超时、内存泄露、死锁等。对于这类问题,我们优先会尝试各种途径去复现;如果短时间里无法复现,建议尝试如下3个思路:
- 从现象推导根因,不断缩小问题范围 :如程序崩溃了,可以通过监控图、日志等途径,逐项分析,偶现的问题也往往是有迹可循的。
- 适当添加测试:偶现类的问题往往是性能原因,可以尝试着用测试(尤其是
Benchmark
)去发现一些问题。 - 查缺补漏,为下一次异常进行准备:偶现问题往往因为缺少数据的埋点,导致无法进一步分析;那我们只能补充相关的埋点信息,在下次出现问题时快速且精确定位。
不难看到,程序类问题的核心在于 提高程序的可观测性,就是在程序的关键位置埋下数据,并利用监控图等工具去分析这些数据。这类问题一方面需要成熟的开发框架与基础平台的支撑,另一方面也依赖开发者的经验沉淀。
业务类问题 - 注重业务对象的设计
业务类问题,指的是和具体业务系统相关的故障,比如:订单系统出现了几笔金额异常的订单、用户系统偶现登录异常、支付系统有时发生超时但仍显示支付成功等。
业务偶现类的问题,往往是业务代码的兼容性、健壮性不足,导致在特殊的场景下出现异常。
但是,业务代码往往有复杂的逻辑,如果直接从现象来分析,无疑是大海捞针。为了减少复杂度,最好利用面向对象的特性 - 定位到关键问题的对象,并寻找对应的方法。对应到代码中,主要分两步:
- 定位到关键问题的对象 - 找到代码中的对象,分析其数据结构与方法
- 寻找对应的方法 - 对异常的问题,找到对象中相关的方法
上面的思路看起来很简单,但大部分用Go
语言写的业务代码都是过程性的,做不到这一点。
比如,在对订单进行增删改查,并不是通过 订单对象 去操作的,而是直接用GORM
库操作数据库来解决的。这两者有什么样的区别呢?我们以创建订单功能为例:
- 面向对象的风格:我们只需要关注订单对象中的Create方法,所有对数据库的操作都封装在方法中;
- 面向过程的风格:我们需要全局搜索数据库的操作,找到其中创建订单的SQL语句;
读懂了对象与对应的方法,再配合一定的日志,业务类问题(抛开基础组件的异常)基本可以被定位到了。将系统的复杂度集中在一个易于理解的对象中,是面向对象编程的一大价值。
注意,如果系统中有“大对象”(例如订单内部包括多种订单类型),不利于分析,最好找时间优化为多个子类。
编程思考 - 重构方案的关注点
大部分的开发者都经历过遗留系统的维护工作,整天在稳定性、开发效率、新需求迭代等工作中头痛不已。如果遗留系统具有长期价值,重构工作会被提上日程。
上一篇已经分享了重构工作的整体考量点,以下三点会聚焦于技术层面的重构方案细节:
- 核心关注:重构工作的价值 - 重构方案的价值,一般是修复某些历史问题,或者能大幅提升后续需求的效率。价值点要强调两个:价值要足够大,并且收益是确定性的(千万不要出现某个模块后续没啥需求了,还在那边谈提效)。
- 守住底线:兜底能力的保障 - 兜底能力,就是在出现问题时能快速切回到现状,千万不要因为重构引入更大的问题。在大型重构时,尽量保留原模块代码,复制出一份再进行重构,在
Go
语言中可以用package
或者目录进行隔离,稳定运行一段时间后再删除。 - 推进思路:结合已知需求,阶段性呈现效果 - 重构需要周期,但周期过长很容易导致方案被否决;而如果你能将已有的需求与重构工作结合起来,适当地延长周期(比如某个需求开发需要5天,单独的重构需要10天,而结合起来只需要10天),会更容易得到业务与技术的一致认可。
工作生活 - 聊聊当下的就业环境
近几年,程序员的就业环境一直比较恶劣,大量的裁员与为数不多的就业岗位压缩着工作机会。尽管现实比较残酷,但我通过周围的圈子关注到如下:
- 现实中的就业环境比舆论中的好不少 - 负面消息会被自媒体放大,而正面消息相对被压缩。
- 人才需求的两极化 - 目前招聘的岗位要么偏向于经验资深的人才,要么是极具性价比的优秀应届生,资历平平的基层开发者很难在市场上寻找对口的位置。
- 招聘途径更倾向于内部渠道 - 随着市场萎缩,负责招聘的HR人力也缩减不少,大部分公司更倾向于内部渠道,优势不仅在于省下成本,更是有利于岗位的精确匹配。因此,我们在要注重各圈子的人脉建设。
Github: https://github.com/Junedayday/code_reading
Blog: http://junes.tech/
Bilibili: https://space.bilibili.com/293775192
公众号: golangcoding