3.3 典型监控应用场景

线上服务出现异常的原因多种多样,与之对应的监控应用场景也可分为多种类型。一部分监控应用场景具有明确的数据来源和业务类型,另一部分监控应用场景则用于满足用户特殊的监控需求。本节将梳理一些典型监控应用场景,通过不同监控应用场景来阐述监控系统在其中承担的作用和价值。

3.3.1 系统监控

系统监控负责保障底层基础设施正常工作,是相对通用且便于批量管理的一种监控应用场景。典型的系统监控,如CPU利用率、网卡流量、磁盘负载等基础指标,都属于基础监控的一部分。

CPU方面关注较多的是CPU利用率和Load 值。CPU利用率可以细分为用户态时间开销、内核态时间开销、软中断时间开销、IO等待时间开销等。不同指标体现了 CPU 在执行任务过程中的行为占比对应不同的优化解决方案。与CPU利用率的复杂性和全面性相比,Load值更简单直接。Load值细分为Load1、Load5、Load15,分别代表1分钟、5分钟、15分钟内CPU运行队列的平均堆积长度。相比CPU利用率,Load值能更直接地表现CPU当前的负载情况。无论是CPU繁忙还是IO繁忙导致的Load值过高,最终都意味着服务节点存在压力,需要优化或扩容。

CPU负载中另一个有趣的指标是上下文切换次数。业务通常会使用多线程或多进程的方式提高服务吞吐。这种方式一方面提升了业务对多核CPU的驱动能力,另一方面充分利用了任务的IO间隔,最大化发挥了CPU处理能力。但线程(进程)的数量并不是越多越好,现代操作系统通常使用抢占式任务调度,任务切换时存在上下文切换开销。显式的上下文切换开销包括硬件寄存器切换、TSS(任务状态段)内容更新等;隐式的上下文切换开销包括非全局TLB(分页缓存)失效、缓存区缓存失效等。线程(进程)数量越多,上下文切换次数也会越频繁,最终可能降低CPU处理实际业务的能力。

网卡流量方面比较关注的是流量、PPS和丢包率。网卡流量直接反映了网卡设备的繁忙程度,间接反映了服务负载情况。PPS是衡量网卡负载的一个指标,表示每秒处理的数据包数量。丢包率升高主要出现在网络质量下降或网卡负载过高的场景,同样需要重点关注。

磁盘负载方面会关注磁盘容量水位、iNode使用率、磁盘Util等,磁盘负载过高会降低业务吞吐能力,对数据库类业务的影响尤其明显。TCP连接状态也是系统监控中需要关注的一个方面,连接数量可以变相体现业务负载情况,新建和关闭连接的状态信息能准确发现各类网络攻击。另外由于连接关闭过程中生成的 Time Wait 状态会持续2MSL (Maximum Segment Lifetime),因此作为TCP连接中的客户端要注意处于Time Wait状态的连接数量,避免端口耗尽阻碍新连接建立。除此之外,内存使用情况、文件系统工作状态、系统时间等,都应该作为系统监控的一部分纳入监控运维体系。

除上述提到的硬件设备基础数据监控之外,其他一些能在系统维度获得的监控结果都可以被归类到系统监控,系统监控全面保障系统级服务质量,暴露系统能力短板。

3.3.2 应用监控

应用监控主要用于探查应用运行状态、发现应用异常和瓶颈、辅助问题定位和性能调优。实现应用监控的思路有两种:黑盒监控和白盒监控。黑盒监控是借助应用监控提供的能力,从外部探测应用服务状态,如Kafka服务提供一组脚本采集Kafka应用执行状态信息;Java应用对外提供JMX接口,暴露应用执行状态信息。白盒监控是将监控逻辑嵌入应用内部,作为应用的一部分输出应用执行状态信息。白盒监控可以分为主动推送和增强嵌入。主动推送是由业务团队预先编写监控数据采集逻辑、周期调度生成监控结果,并按照监控系统要求的数据格式推送到监控系统,监控系统的开发团队通常要提供用于数据通信的通用 SDK,用以减小业务团队接入监控系统的复杂度。增强嵌入相比主动推送对业务代码的侵入性更小、接入成本更低。例如,Java 应用基于JavaAgent启动参数,能将监控逻辑嵌入Java应用启动流程中,并通过字节码增强的方式修改Java应用加载类的字节码,注入自己的监控逻辑。

无论通过何种方式实现应用监控,最终的目的都是要感知应用执行状态,并尽快发现应用执行异常。对Java应用而言,常用的应用监控项有JVM、JavaMethod、Exception等。JVM用于探测应用的GC情况,包括FGC和YGC的执行次数和执行时间,JVM内存空间分配和使用情况,JVM线程使用情况等。JVM监控结果是GC调优最有力的判断依据,也是应用负载评估、服务瓶颈分析的数据支撑。Java应用的GC曲线如图3-5所示。

图3-5 Java应用的GC曲线

JavaMethod 主要用于统计 Java 应用方法的执行次数和时间开销,服务出现问题的一个典型现象是核心方法平均RT延长或错误率飙升。Exception监控用于采集 Java 应用执行过程中出现的异常,Exception 监控不仅要采集抛出异常的方法和异常出现的次数,还要采集异常堆栈信息,通过异常堆栈信息能快速定位问题出现的原因。Java应用的运维工程师要关注服务出现的各项异常,对新增异常要尤其敏感。应用发布前后,Exception监控是必须要查看的监控之一。

应用监控的覆盖范围还包含对各类中间件的监控,例如,对Tomcat源码注入能实现Servlet容器监控,对JDBC Driver注入能实现MySQL监控,还有HttpClient、Redis、Kafka、HBase、Dubbo等都能通过这种方式实现监控。鉴于此,应用监控是线上运维最有效的手段。

3.3.3 终端监控

系统监控和应用监控主要侧重服务端监控,但部分异常从服务端无法感知,如区域运营商出现异常、区域网络出现异常、CDN节点出现异常等,此时需要终端监控来发现这类问题。

终端监控主要包括移动端(Android,IOS)、PC端(浏览器),以及各类嵌入式设备。数据采集通常使用SDK嵌入数据源,周期执行获取监控结果。终端监控能采集到正常请求和异常调用,正常请求统计服务质量,异常调用能够发现问题。由于正常请求数据量大、重要性偏低,因此通常使用一定比例采样上报。异常调用可以在不同维度下形成聚合结果,用于发现区域网络异常、CDN服务异常、运营商异常、流量数据劫持等问题。

终端监控相比服务端监控存在更高的复杂性。首先,终端监控数据延迟到达比例高,这是因为不同终端设备所处的网络环境不同,所以监控数据在网络中的传输延迟有明显差别,另外部分终端监控的执行机制也可能影响监控结果的按时上报。其次,终端监控数据请求量大,且随用户使用习惯周期性波动,重要业务活动开始前要评估监控系统数据处理能力,必要时可以降低采样率或执行服务降级。

终端监控数据会延迟到达,到达率随时间持续波动的情况给监控报警造成了很大困扰,且难以兼顾报警的实时性和有效性。针对这种情况,可以尝试对终端监控数据到达率做数据拟合,对修正后的错误率结果做报警判断。另外终端监控面临的异常类型也非常复杂,基于全维度做数据划分会形成很多数据量偏小的监控曲线,这种监控曲线的比例类报警误报率很高。提高报警准确性还可以枚举可能出现的异常场景,针对不同的场景定义不同的维度组合,基于不同的维度组合决定不同的报警策略。

3.3.4 秒级监控

有别于上述监控类型中提到的不同监控应用场景,秒级监控的主要特征是监控数据采集频率高。秒级监控比分钟级监控的监控数据总量放大了数十倍,全量开启秒级监控会对监控数据处理和存储服务造成非常大的压力。常规监控出于对数据量和性价比的考虑,通常会使用分钟级采集间隔来应对各类监控需求,但在某些场景下,秒级监控有其不可替代的作用。

线上曾经出现CPU利用率的分钟级监控结果持续维持在90%的高水位,而机器 Load 负载却很低的情况。此时使用秒级监控发现应用有分钟周期的定时任务,任务执行频率和监控数据采集频率相同,任务执行时CPU利用率达到90%,任务未执行时CPU利用率却很低。分钟采集间隔采样精度低,无法完全还原真实监控数据曲线。当采集时间点和高负载时间点重合时,最终结果表现为CPU利用率持续处于高水位。

在测试或线上变更时,使用秒级监控能快速验证功能、调整测试策略。对部分线上有损变更的场景,使用秒级监控能快速感知线上变更状态,推动变更流程继续推进,有效减少变更总时间。

鉴于此,通常建议用户主动开启部分数据源的秒级监控,持续一段时间后自动关闭。对于需要执行测试或变更的服务,可以提前开启秒级监控,在操作过程中使用秒级监控;对于线上存在异常需要秒级监控做进一步分析的服务,可以开启秒级监控,辅助排查问题。但上述方式存在缺陷,当面对突发事件时,监控系统和运维工程师无法未卜先知地开启秒级监控,从而缺失异常发生时的秒级监控结果。针对这种情况,部分核心服务可以考虑永久开启秒级监控以应对突发场景。

总而言之,秒级监控相较分钟级监控会有明显的性能开销,不仅体现在监控系统的数据处理和存储方面,还体现在业务自身的数据采集和上报压力方面。是否使用秒级监控取决于业务重要性和开启秒级监控对业务自身的影响。

3.3.5 监控大盘

监控大盘是一种特殊的监控应用场景,主要目的是收集离散的核心数据,并按照业务相关性组织整理。一种监控大盘展示的是重要商业数据,如交易额、订单量、PV/UV 等,这种监控大盘有助于产品相关人员直观感知用户使用情况和产品业绩水平;另一种监控大盘展示的是重要业务指标,通常会按照不同业务线做区分,这种监控大盘主要服务于运维工程师和开发工程师,监控大盘中的监控视图通常能直接表现业务线某个技术方向的运行状态。

监控大盘能够从全局视角判断服务健康状态,并在一定程度上快速确定问题的大致方向,通常不承担实际问题定位的功能。随着产品复杂度逐渐提高,逐条业务线精细运维的成本也持续放大,当线上出现重大故障时,由于服务相互影响,运维工程师的感觉往往是“四处起火”。如何从众多报警消息中判断出问题根源,或者是在问题出现时过滤掉边缘异常从而缩小问题排查范围,这些都是运维工程师在日常工作中应该思考的内容。

监控大盘提供了定位问题的大致方向,但这依赖运维工程师对业务结构和依赖关系的梳理,并将梳理结果输出给监控大盘,形成监控大盘上关联的监控表项,最终才能让监控大盘在出现故障时发挥更大作用。

3.3.6 链路监控

随着业务复杂度的提高和开发团队规模的扩大,很多业务线都会选择用微服务的方式实现功能解耦,提升开发和运维效率。随着服务化拆分逐渐深入,单次请求的响应链路不断延长,请求响应网络也愈发复杂。原有基于监控节点的故障排查效率显著降低,链路监控的需求应运而生。链路监控为运维工程师提供了追踪请求流转细节的能力,并隐含业务依赖关系,是问题定位和性能优化的有效工具。

链路监控通常在一次请求到来时生成唯一的Traceid,请求在各节点流转的过程中,使用 Traceid 打印日志到本地,再由数据收集服务从不同节点收集日志,集中处理生成的链路调用信息。Java应用的调用日志生成策略可以采用字节码增强的方式,能够实现应用内方法调用链路统计、跨应用 HTTP调用链路统计、Dubbo RPC调用链路统计等,还能统计大多开源中间件的调用链路情况,如MySQL、Kafka、Redis等。部分不能由字节码增强覆盖的场景,用户可以调用API输出自定义链路日志。

链路监控的衍生功能是服务调用拓扑关系,服务调用拓扑关系展示了服务全局调用关系。链路拓扑配合链路异常监控,能够直观展现服务整体状况。但理想的拓扑关系并不完全由自动化工具生成,由于调用链路种类多、链路复杂,因此把整个产品的调用关系和盘托出形成的调用拓扑会像一团乱码,不具有运维使用价值。

合理的组织方式是由运维工程师和开发工程师介入调用拓扑关系的组织过程,借助链路监控已经收集到的链路信息,通过半自动的方式将业务方对调用关系的理解及调用节点的重要性和层级体现在调用拓扑关系中。理想的调用拓扑关系可以分为多个层次,像文件系统的多层目录一样将复杂的调用关系组织成不同层级的调用依赖,同时自下而上传递异常,最终形成树状调用链拓扑关系。树状调用链拓扑关系如图3-6所示。

图3-6 树状调用链拓扑关系