1 生产故障分级规范概要
1.1 开篇
计算机是当今世界的命脉,你能看到的任何地方,计算机都在改变着世界,不论是传统的制造生产,医疗通讯。还是新兴的虚拟现实,无人驾驶汽车 等领域。过去的几十年,计算机硬件飞速发展,软件领域也是不断刷新着人们的想象。可以说,计算机学科复杂而且庞大,甚至有人说,不亚于物理学。
今天我们讨论的话题,线上事故分析,只是其中的一部分,一小部分,为了说清楚这一点,我们简单了解一下软件工程学科。 玛格丽特·希菲尔德·汉密尔顿(英语:Margaret Heafield Hamilton,1936年8月17日-[1])被认为发明了软件工程一词。
她帮助了NASA在阿波罗计划中避免了严重问题。她曾这样说:"有点像牙根管治疗:你总是拖到最后才做,但有些事可以预先做好,有点像预防性体验,只不过是预防软件出错
我们不妨简单总结如何开发一个大型的软件项目下:
1-基于面向对象的编程语言
2-容易使用的集成开发环境
3-文档和注释(程序员最讨厌的两件事,写注释给别人读,阅读别人没有写注释的代码)
4-构建管理和版本控制
5-质量保证测试(QUALITY ASSURANCE-QA)
每个软件从业者从写下第一行代码开始,就一刻不停地 和软件中的错误做斗争 开发和维护(修复缺陷、确保资源充 足等保障软件运行的活动)是一对伴随软件运行而产生的双 热爱从零到一开发软件是开发者的天性 看着自己编写的 软件完美运行,为其他 提供服务,一直是驱动开发者前进的动力。为了更快更好地开发软件,我们不断改进开发方法和软件架构,但是开发者在使用新的方法和更复杂的架构时,往往也会低估潜在的风险。
1.2 事故等级定义
业务和产品的线上质量、是研发团队的生命线,也是支撑业务快速是错,小步跑的前置条件。
1.2.1 故障等级定义
故障等级定义,不同的业务形态,不同的公司团队,有不一样的划分标准,下表列举了一般常见的维度 和标准
1.2.2 故障响应处理机制
如图所示,大体描述了一般生产故障的处理机制
故障发现:
1-客户投诉或者业务反馈,研发自我发现
2-监控报警:技术监控,业务监控
1.2.3 思考
- 为什么会发生这个问题?
- 为什么测试阶段没有发现?
- 为什么系统不能容错?
- 能不能更早发现问题?
- 解决过程能不能更快?
- 怎样防止类似的事情发现?
2 生产故障原因和分类
2.1 故障分类
-
代码bug:上线代码逻辑有问题,遇到特殊情况下,导致故障的情况
-
操作不当:线上配置或资源配置错误,操作不当,比如启动顺序不合理,初始化脚本不对,语法生 产数据隔离混用等
-
系统级别软件bug:技术架构中使用到的任何OS,第三方类库,软件在特殊场景下,bug被处罚导 致故障的情况。
-
突发流量:热点或突发事件,引发的瞬时流量超过日常峰值或成倍增长,造成性能下降或功能不可 用问题
-
资源使用不均:整体产品线利用率不达标,但有些业务冗余度不足,导致资源不能正确合理使用
-
容量预估不足:对个别业务核心池预估不足
-
网络类:公网拥堵、丢包、专线、网络设备故障、ip被攻击(包括DNS被攻击)、IP被封、域名被 封、网络软件BUG、业务部门使用不当、未及时扩容等
-
安全类:被攻击,漏洞被利用等问题,均归为此类
-
局方故障:ISP,根域名服务,电力,空调,光缆等外部单位故障导致的问题。
-
硬件故障:任何硬件非人为原因损坏导致的故障均归为此类。故障分类
-
第三方合作公司或接口故障:项目依赖的第三方公司或接口故障
2.2 bug是可以完全避免的么?
-
日常项目里中,站在开发者的角度:
-
大多数程序员会花费70%-80%时间调试,而不是在写代码,虽然现在有很好的工具能极大帮助程 序员防止和解决错误
-
另一个重要部分 给代码写文档
-
由于现在的需求越来越复杂,依赖的外部条件,技术组件也是越来越多,很多bug在所难免。但也 有一些bug是不能被原谅的,所以养成良好的开发习惯,很重要。
3 生产故障定位和解决流程
3.1 影响服务质量的因素
3.1.1我们常常面对如下的业务场景
•大量无用业务逻辑
•sleep
•循环里写查询
•磁盘满了、影响到有磁盘写操作的接口
•慢查询的SQL
•Redis中存在大key,带宽打满不知是哪些接口影响与被影响
•队列堆积却不知哪些接口影响
•流量放大系数评估全靠经验和看代码
3.1.2 资源依赖
•接口依赖(内部服务、外部服务)
-
无法整体查看服务、接口、资源之间的依赖关系
-
没有很好的外部服务监控机制、无法统计依赖的服务质量
•数据资源依赖(DB、Redis、Memcached、队列)
-
无法整体查看服务、接口、资源之间的依赖关系
-
容量评估及扩容标准全靠经验
-
出问题后仅能凭经验逐一排查、无法迅速定位到代码层面
-
完全依赖dba帮忙排查
•服务器资源依赖 (负载、内存、网络 ······)
- 资源指标上升后、无法判断数值是常规上升还是资源异常
3.1.3 流量
•大流量来袭时、无法整体感知流量状况
-
流量多大算大?
-
核心业务是否收到影响?
-
单一接口流量增加却未达到整体的告警线、导致无法感知
-
可视化是否实时
•无法直观统计流量分布、无法查看具体每个接口的流量情况
•容量评估时、流量放大系数全靠人为判断、对服务整体的放大系数无感知
3.2 故障发现
一个高可用的业务系统除了技术架构支撑之外,监控系统作为事中决策支撑和事后决策止损是必不可少 的一部分
工具:Prometheus 、Zabbix 、Nagios 、Open-Falcon 、Pageduty等
3.2.1 监控体系汇总
监控体系分类:一般可分为:业务监控 ,系统级别监控 ,网络监控 ,程序代码监控 ,日志监控 ,用户行为分析监控,其他种类监控等
系统级别监控:
主要跟操作系统相关的基本监控项
物理监控(对物理机或者容器的监控):存活、内存、cpu、load、硬盘(速率、使用率)、网络
活性检测:进程、端口
应用服务监控:jvm、gc、线程数
服务监控:rpc和http接口的qps、rt、错误码等
业务监控:
包含用户访问的QPS,DAU,访问状态(http状态码),业务接口(登录,注册,聊天,送礼,留言,下单,支付,播放按钮点击率,搜索次数,好评差评数等等),产品转化率,充值额度,客诉等很宏观的概念
网络监控
IDC,对网络状态的监控(交换机、路由器、防火墙,vpn)对于一个互联网公司必不可少,但是有很多时候又被忽略:例如:内网之间(物理内网,逻辑内网),外网,丢包率,网络重传,延迟等等
链路监控
中间件监控:
异常监控:
当前排除故障的流程
【项目背景】目前公司线上业务,经常遇到以下几种问题:
- 由于环境的隔离,导致无法从本地开发环境访问线上环境,故而遇到问题,无法使用IDE远程调试应用程序。
- 为了线上业务的正常运作,所以不能在生产中调试,防止因调试导致所有线程不可用,从而影响线上业务。
- 不同环境,参数不同,重现问题难。
3.3 线上排除故障
3.3.1故障排除方法
总结如下:
-
回滚到稳定版本。
-
在日志和代码中搜索错误和异常。
-
添加log日志,创建新的修补程序版本。
-
提交测试,等待测试和发布(数天)。
-
部署预发,获取详细日志,验证问题。
-
发布新版本到线上。
-
重启服务
3.3.2 故障排除一般需要掌握的技能-工具篇
1-一般情况下,需要了解业务场景,千万不要第一时间着急解决bug,应先评断是否可以回滚,影响面大小,是否为上层提供服务,是否有数据异常。
2-应熟悉项目依赖的监控,学会看监控。
3-应熟练开发环境的断点排查与诊断,熟悉git,maven,idea的使用,分析如何复现线上故障。
4-线上排查故障的工具链
3.3.2.1 IDEA 断点高阶
1.window快捷键:
所在行处: Ctrl+F8
断点属性编辑: Ctrl+Shift+F8
2.断点类型与设置
行断点(line breakpoints)
字段断点(field breakpoints)
鼠标右键点击该断点图标 ,弹出该断点配置,会有Field access和Field modification选项,此选项是字段类型断点特有的,分别对应访问该字段或修改该字段触发断点,两项同时选中,则访问与修改该字段都会触发断点。
3.断点控制
断点删除
1-所有类型断点:断点设置中移除对应的即可。
2-快捷移除:行位置鼠标左键单击即可移除(异常断点除外)
建议:为了避免意外删除断点并丢失其参数,通常选择点击鼠标中键来删除断点。要做到这一点,请进
入 "设置/首选项"|"构建、执行、部署"|"调试器",选择 "拖到编辑器 "或用鼠标中键点击。点击一个断点
将启用或禁用它。
断点静音
如果某些时候不需要在断点处停留一段时间,可以将断点静音。这样就可以在不离开调试器会话的情况下恢复正常的程序操作。之后,可以解除中断点的静音,继续调试。断点静音会静音所有断点
启用/禁用断点
正常 通过 断电删除 的断点,会连同当时断点的内部所有配置一并删除。如果想暂时关闭一个断点,后
续可能还会使用,这是一个很好的选择
同断点删除操作
断点移动/复制
断点移动:鼠标左键拖动
断点复制:按住ctrl,鼠标左键拖动
4.断点属性配置
鼠标操作:右键断点:More(Ctrl+Shift+F8)
快捷键:Ctrl+Shift+F8
快捷键:光标所在行 Alt+Enter
断点有许多属性配置,如下图所示,下面将会对各个属性的作用以及使用进行说明
Enabled
- 表示是否启用该断点,选中表示启用,取消选中表示不启用。
Supend
-
当断点的 Suppend属性被勾选,触发该断点时,会触发程序挂起,当该属性未选中时,程序触发该断点时,程序不会挂起,常用于输出一些表达式结果日志。
-
当断点的 All 属性被勾选,触发该断点时,会挂起所有线程
-
当断点的 Thead 属性被勾选,触发该断点时,只会挂起触发该断点的那个线程,不影响其他线程
-
当需要在不暂停程序的情况下记录一些表达式时(例如,需要知道一个方法被调用了多少次时),或者需要创建一个主断点,在击中后启用附属断点时,非暂停性断点是非常有用的。
-
实际生产实践中,可用于调试多线程并发的问题
Condition
- 可以输入一段能获得true或false的表达式,程序运行到断点处,且表达式条件为true才会触发断点
Log
下面三个属性选项经常配合 Suppend 属性一起使用,用于在不挂起的情况下,输出一些想要的日志信
息Breakpint hit message :控制台输出触发端点的日志信息,类似如: Breakpoint reached at ocean.Whale.main(Whale.java:5)
Stack trace:输出触发断点时的堆栈信息
Evaluate and log:计算表达式结果并输出表达式结果到控制台,表达式的计算基于断点所在行的 上下文,表达式的语句可以是字符串字面量,如 "我是字符串" ,也可以是方法调用,如
users.size(),也可以是多行语句块,表达式的结果取自return语句,如果没有return语句,会取 表达式中的最后一行语句。
Remove once hit
- 是否在断点触发后移除该断点,后续不在触发
Disable until hitting the following breakpoint
-
指定在另一个断点触发后,该断点才启用,若该断点启用后,并且被触发,
-
场景:当只需要在某些条件下或某些操作后暂停程序时,这个选项很有用。在这种情况下,触发断 点通常不需要停止程序的执行,而是做成非暂停状态。
Filters
前边说的大都数属性,都只针对方法程序运行上下文。此属性更多关注通过过滤掉类、实例和调用者方法来微调断点操作,只在需要时暂停程序。,有如下几种过滤方式:
-
Catch class filters :此选项只对异常类型的断点可用,可以让程序只在指定类和子类中抛出的异常才会触发断点或者不在指定的类和子类中触发断点(即排除一些类,排除通常以 - 开始,例如 -pacakge.ClassName),
-
Instance filters :只有指定实例id号可以触发断点,多个实例id号以逗号隔开,实例id号可以在Variables和Memory面板中查看
-
Class filters :可以让程序只在指定类和子类中才会触发断点或者不在指定的类和子类中触发断 点(即排除一些类)。
-
Caller filters :根据调用者来进行过滤,需指定方法的全限定名(包含方法签名),例如mypackage.MyObject.addString(Ljava/lang/String;)
5.生产力建议
使用断点进行 "printf "调试
使用非暂停的日志断点,而不是在代码中插入打印语句。这为处理调试日志信息提供了一种更灵活 和集中的方式。
场景: 所有需要打印的地方,生产上禁止 System.out.print();
调试无响应的应用程序如果你的应用程序挂起,暂停会话,让调试器获得关于其当前状态的信息。然后你可以检查程序的状态并找出问题的原因。
场景: 项目启动卡死等处理
测试你的程序是否有并发性问题发现多线程程序在并发方面是否健壮的一个好方法是使用断点,在碰到时只暂停一个线程。停止一 个线程可能会揭示出应用程序设计中的问题,否则这些问题就不会显现出来。
计算保留的大小对于每个类的实例,你可以计算它的保留大小。保留大小是指对象本身和它所引用的所有对象以及 没有被其他对象引用的对象所占据的内存量。
这在估算重型单体或从磁盘上读取的数据(例如,复杂的JSON)的内存占用时,可能很有用。另 外,在决定使用哪种数据结构时(例如,ArrayList与LinkedList),这也很有用。
在运行应用程序之前,确保在设置/首选项|构建、执行、部署|调试器中启用附加内存代理选项。
在查看类的实例时,右键单击一个实例并单击计算保留大小。