分布式环境快速定位问题

时间:2023-02-13 11:03:14

1 分布式问题定位障碍

生产环境,代码在线上运行业务,不能debug,可查看当前异常日志。

案例

分布式的应用系统,启动4个子服务:服务A、B、C、D,服务依赖关系A->B->C->D,部署在不同机器。

RPC调用中,若服务端业务逻辑异常,就会把异常抛给调用端,若现在这调用链中有个服务异常,如何定位问题?

分布式环境快速定位问题

第一反应打印日志,那就打日志。

假如这时发现A异常,这异常其实可能因B或C或D异常抛回来的。

怎么确定在整个应用系统中,哪个调用步骤问题,这步骤中的哪台机器出现问题?该在哪台机器上打印日志?

为排查问题,若要打日志,就得修改代码,就得重新对服务进行上线。若这几个服务又是跨团队,就完了。

所以分布式环境下定位问题的难点就在于,各子应用、子服务间有着复杂的依赖关系,难确定哪个服务的哪个环节问题。简单通过日志排查问题,就要对每个子应用、子服务逐一进行排查,很难一步到位。

2 快速定位问题

2.1 借助合理封装的异常信息

想办法通过日志,定位到是哪个子应用的子服务出现问题就行。

RPC框架打印的异常信息中,包括定位异常所需要的异常信息,如哪个类异常引起的问题(如序列化或网络超时问题),是调用端还是服务端异常,调用端与服务端的IP,及服务接口与服务分组。

具体如下,链路调用异常:

分布式环境快速定位问题

A->B->C->D过程中,很快定位C服务问题,服务接口com.demo.CSerivce,调用端IP192.168.1.2,服务端IP是192.168.1.3,原因业务线程池满。

好的RPC框架要对异常详细封装,对各类异常分类,每类异常有明确异常标识码,并整理成一份简明文档。使用方可快速通过异常标识码在文档中查阅,快速定位问题,找到原因。

异常信息中要包含排查问题时所需要的重要信息,比如服务接口名、服务分组、调用端与服务端的IP,以及产生异常的原因。总之要让使用方在复杂的分布式应用系统中,根据异常信息快速地定位到问题。

以上是对RPC框架本身的异常来说,如序列化异常、响应超时异常、连接异常等。

那服务端业务逻辑异常呢?服务提供方提供的服务的业务逻辑也要封装自己的业务异常信息,从而让服务调用方可通过异常信息快速定位问题。

2.2 分布式链路跟踪

无论RPC框架本身,还是服务提供方提供的服务,只要对异常信息合理封装,就可让分布式定位问题更容易。

假设这4个服务分别由来自不同部门的4个同事,在A调用B的时候,维护服务A的同事可能是不知道存在服务C和服务D的,对于服务A来说,它的下游服务只有B服务,那这时如果服务C或服务D出现异常,最终在整个链路中将异常抛给A了呢?

在这种情况下,维护服务A的同事该如何定位问题?

因为对于A,它可能不知道下游存在服务C、D,所以维护服务A的同事会直接联系维护服务B的同事,之后维护服务B的同事会继续联系下游服务的服务提供方,直到找到问题。成本很高。只要知道整个请求的调用链路。服务A调用下游服务B,服务B又调用B依赖下游服务,如果维护服务A的同事能清楚地知道整个调用链路,并且能准确地发现在整个调用链路中是哪个环节问题。

要知道服务调用的整个链路,我们可以用“分布式链路跟踪”,将一次分布式请求还原为一个完整的调用链路,我们可以在整个调用链路中跟踪到这一次分布式请求的每一个环节的调用情况,如调用是否成功,返回什么异常,调用的哪个服务节点以及请求耗时等。

哪怕多部门合作,也可一步到位。

3 RPC框架中如何整合分布式链路跟踪

分布式链路跟踪有:

  • Trace,整个链路,每次分布式都会产生一个Trace,每个Trace都有唯一标识TraceId,在分布式链路跟踪系统中,通过TraceId区分每个Trace
  • Span,整个链路中的一段链路,Trace由多个Span组成。一个Trace下,每个Span也都有唯一标识SpanId,而Span存在父子关系。A->B->C->D的整个调用链,有3个Span,分别是Span1(A->B)、Span2(B->C)、Span3(C->D),这时Span3的父Span就是Span2,而Span2的父Span就是Span1

Trace与Span的关系:

分布式环境快速定位问题

4 RPC框架中如何利用这两概念整合分布式链路跟踪

RPC在整合分布式链路跟踪要做的就是:

  • 埋点
  • 传递

埋点

分布式链路跟踪系统要想获得一次分布式调用的完整的链路信息,须对这次分布式调用进行数据采集,而采集这些数据的方法就是通过RPC框架对分布式链路跟踪进行埋点。

RPC调用端访问服务端时,在发请求消息前,触发分布式跟踪埋点,在接收到服务端响应时,也触发分布式跟踪埋点,并在服务端也有类似埋点。这些埋点最终可记录一个完整Span,而这链路的源头会记录一个完整Trace,最终Trace信息会被上报给分布式链路跟踪系统。

传递

上游调用端将Trace信息与父Span信息传递给下游服务的服务端,由下游触发埋点,对这些信息进行处理,在分布式链路跟踪系统中,每个子Span都存有父Span的相关信息以及Trace的相关信息。

5 总结

分布式系统有着较为复杂的依赖关系,我们很难判断出是哪个环节出现的问题,而且在大型的分布式系统中,往往会有跨部门、跨团队合作的情况,在排查问题的时候会面临高沟通成本。

为了在分布式环境下能够快速地定位问题,RPC框架应该对框架自身的异常进行详细地封装,每类异常都要有明确的异常标识码,并将其整理成一份简明的文档,异常信息中要尽量包含服务接口名、服务分组、调用端与服务端的IP,以及产生异常的原因等信息,这样对于使用方来说就非常便捷了。

服务提供方在提供服务时也要对异常进行封装,以方便上游排查问题。

在分布式环境下,我们可以通过分布式链路跟踪来快速定位问题,尤其是在多个部门的合作中,这样做可以一步到位,减少排查问题的时间,降低沟通成本,以最高的效率解决实际问题。

FAQ

在分布式环境下,你还知道哪些快速定位问题的方法?

1.tracid在碰到线程池异步时如何传递id? 2.mq消息是否也该带上tracid?

通过上下文或agent。

定位问题的一些经验和方法

  • 确认清楚问题的现象或本质
  • 如果时间允许可以复现下问题,对问题理解更直观
  • 查看日志,确认报错的异常信息(日志这步很关键,物理机时代通常做法是上机器grep,如果有多台机器这是比较麻烦的点,所以一般会有日志中心。容器时代的话屏蔽了Iaas层,日志都存在在日志中心,例如ELK,根据关键字就可以查了)
  • 查看代码确认业务逻辑
  • 根据日志和代码业务逻辑基本就可以确认报错的点了