在一个单体的应用程序中,我们只用简单的将日志记录到日志文件里面就能够方便的查看服务的运行状态。但是在由众多微服务构成的系统中,服务之间的依赖调用是比较常见的。
图1 微服务调用链
如上图所示,如果一个用户调用了一个服务 A ,服务 A 同时调用了服务 B 和服务 C ,服务 B 又调用了服务 D 和服务 E,在这种情况下,如果我们还用原来的日志记录方法来跟踪日志,我们就要分别去服务 A、B、C、D、E 五个服务所在的服务器上去分别查看日志。更加复杂的是,如果其中的一些服务时多个相同的服务实例形成一个小型的服务集群来处理调用的请求,或者运行的实例运行在容器中,并且由 Kubernetes 这样的调度系统来动态调度的,那么我们很难直观的了解是是哪个服务实例在这次调用中处理的调用请求,也不知道服务究竟是运行在集群的那些机器上。所以也就很难直接找到日志输出的地方去进行日志的查看。
而且即使是在单体的应用中,多个用户同时进行调用的时候,多个会话的日志混杂在一起,我们也很难将一次请求的日志给分离出来。所以,我们需要一个更合理和更高效的日志记录和跟踪体系来解决这个问题。
Google 发布的分布式系统跟踪的论文《Dapper, a Large-Scale Distributed Systems Tracing Infrastructure》 来处理这种情况。具体来说就是讲上图中前端用户发起一个根请求,这个根请求形成的一个完整的调用链,把这个完整的调用链定义为为一个跟踪树(Trace tree),每次请求,即图中自上而下的箭头都定义为一个调用(span)。除了用户直接发起的 span 没有父 span 外,每个 span 都有一个上级 span的引用。这样就形成了一个环环相扣的跟踪链条。图1 的调用链用上述的概念来描述,就形成了如图2 所示的的跟踪树调用结构图。
图2 跟踪树调用结构图
为了记录一次调用(span)的详细情况,我们在服务调用的过程中定义了一系列的事件并用日志记录下来。其中有四种系统定义的事件 Client Send ( CS ),代表调用者向被调用者发起了服务的调用请求, Client Receive( CR ) ,代表调用者接收到服务被调用者的回应,Server Receive ( SR ),代表服务被调用者接收到服务调用者的请求, Server Send ( SS ),表示服务被调用者向服务调用者发送回应。还有用户自定义的事件,用来记录服务在一次调用运行过程中的一些重要状态信息。这个用户自定义事件就是我们平时打印的日志的信息。一次跨服务的调用视图如图3 所示。
图3 跨服务调用视图
我们在将服务调用过程中的事件数据收集起来,通过服务调用跟踪链和服务调用事件的调用过程数据,我们就可以我们能够清晰的将一次调用中的所有事件按照树形方式详细的显示出来。而且,根据CS、CR 、SS、SR的时间戳,我们还能分析服务调用过程中在各个网络等待和实际运行的耗时。
当然了,这里只是将一个理论基础,实际的过程中,还有很多问题需要解决。例如 traceId 和 spanId 的生成和传递是一个需要解决的问题。这个我们在后面再继续说。
作者:阿土伯已经不是我
链接:https://www.jianshu.com/p/5476602b6e25
共同学习,写下你的评论
评论加载中...
作者其他优质文章