文章采集调用(微服务中较流行的两款开源分布式tracing系统(图))

优采云 发布时间: 2022-04-05 02:18

  文章采集调用(微服务中较流行的两款开源分布式tracing系统(图))

  1.背景说明

  由于我们的项目是微服务方向的,中间和后台服务有10多种服务模块,各个服务/模块之间的调用关系比较复杂,一些服务之间还有一些代理服务和服务(许多服务实时部署的实施)。这些现象导致在开发、调试和问题跟踪中逐渐出现问题。因此,前段时间,分别研究了微服务中两个流行的开源分布式追踪系统:Zipkin 和 Jaeger。

  在微服务分布式架构的系统中,可能存在复杂而深入的逐层服务调用关系,如下图所示。

  微服务架构中各个服务的调用关系是不是和程序中各个函数的调用关系很相似?

  上图很容易理解。当用户的浏览器发起请求时,会先到应用程序A;A 将呼叫 B 和 C;B会调用F……这样一层一层的调用到最后一层,最后完成一个客户的请求。处理(可能是读请求或写请求)。

  试想一下,在这个过程中会出现哪些可能的问题?

  对于第一种情况,如果客户端请求是写请求,调用链中每一步都有写操作,第一步执行成功,第三步失败,那么分布式系统中需要采用分布式事务。回滚前几步的写操作的机制!

  对于第二种情况,可能会因为应用F的异常而拖累整个服务环节,甚至出现雪崩现象,严重影响系统的可用性。本文不会讨论如何处理这种场景,而是先提一下,这种问题在分布式系统中使用了熔断机制和降级机制,以确保系统在出现此类异常时具有高可用性!

  那么我们来看看1、2、3的场景中可能涉及的问题。在一个复杂的调用链中,假设有一个响应慢或处理异常的调用链。如何定位这个问题?有问题的服务呢?大多数开发者的第一反应可能是查看日志,依次分析调用链路上各个系统的日志文件,然后定位出问题的服务。在海量日志中定位问题真的很痛苦!

  2.跟踪链

  首先简单解释一下什么是跟踪链。有相关基础的可以跳过,也可以快速通读本段。基本上,任何谈论跟踪链的 文章 都会发布这张图片:

  图 1. 一个简单的追踪调用流程

  图 1 是来自 Google 的 Dapper 论文的一个示例:图中,五个节点 A~E 代表五个服务。用户向A发起请求RequestX。同时,由于该请求依赖于服务B和C,所以A分别向B和C发送RPC请求,B处理完请求后直接返回给A,但是服务C也依赖于服务 D 和 E。因此,必须分别向 D 和 E 发起两个 RPC 请求。D和E处理完后返回给C,C继续回复A,最后A会回复用户ReplyX。对于这样的请求,分布式跟踪的一个简单实用的实现是为服务器上的每个发送和接收操作采集跟踪标识符和时间戳。

  在 Dapper 的论文中,Trace 和 Span 是两个非常重要的术语。我们用Trace来表示一个请求的完整调用链的trace,将上面的服务A和服务B这两个服务的请求/响应过程称为一个Span,通过span来反映trace。一句话概括,我们可以把一条trace看成span的一个有向图,这个有向图的边就是span。为了更好地理解这两个术语,我们可以看一下下面的调用图。

  [跨度A] ←←←(根跨度)

  |

  +------+------+

  | |

  [Span B] [Span C] ←←←(Span C 是 `ChildOf` Span A)

  | |

  [跨度 D] +---+-----+

  | |

  [跨度 E] [跨度 F] >>> [跨度 G] >>> [跨度 H]

  ↑

  ↑

  ↑

  (Span G`FollowsFrom`Span F) 上图收录8个span信息

  图2.一个tracing过程的Spans关系图

  ––|–––––––|–––––––|–––––––|––––––––––––––|––––––– –|––––––––|–> 时间

  [跨度A········································································································································································...

  [跨度B················································ ····················································································································································]

  [跨度D················································ ········································································································································································································································································································]

  [跨度C················································ ]

  [跨度 E···] [跨度 F··] [跨度 G··] [跨度 H··]

  图3. Spans的时间线图

  分布式跟踪系统需要做的是记录每个发送和接收动作的标识符和时间戳,并连接一个请求中涉及的所有服务。只有这样才能理解一个请求的完整调用链。

  3. 开放跟踪

  在详细介绍Jaeger之前,还有一个关键词需要提一下:OpenTracing。

  一句话总结,OpenTracing 是一套标准,通过提供平台无关和厂商无关的 API,开发者可以轻松添加(或替换)跟踪系统的实现(我们在测试中基本上使用两行代码) . 更改为在 Zipkin 和 Jaeger 之间切换)。OpenTracing 为操作支持系统和特定平台提供辅助库。程序库的具体信息请参考详细规范。OpenTracing 已进入 CNCF(Cloud Native Computing Foundation,著名的 Kubernetes、gRPC 和 Prometheus 孵化的地方),正在为全球分布式追踪提供统一的概念和数据标准。

  当然,本文前面提到的 Zipkin 和 Jaeger 都支持 OpenTracing 标准。

  3.1 引用关系

  目前 OpenTracing 标准中定义了两种类型的引用:

  3.1.1 ChildOf 参考

  一个跨度 A 可以是另一个跨度 B 的 ChildOf。以下所有内容构成了 ChildOf 关系:

  [-父跨度---------]

  [-孩子跨度----]

  [-父跨度-------------]

  [-孩子跨度A----]

  [-儿童跨度B----]

  [-Child Span C----]

  [-Child Span D--------------]

  [-Child Span E----]

  图 4. 的 ChildOf 引用 Span

  3.1.2 FollowsFrom 引用

  一些父 Span 不以任何方式依赖其子 Span 的结果。在这些情况下,我们只说子跨度是从因果意义上的父跨度派生的。

  4. 积家介绍

  细心的话,通过上面的CNCF地址,我们可以发现Jaeger现在已经成为CNCF的一个孵化项目。

  为了深入了解jaeger的工作原理,首先我们来看一下Jaeger的架构设计图:

  图5. Jaeger 架构设计图

  上图中,我们看到的是Jaeger系统:*敏*感*词*部分是我们的应用代码;红色部分代表 Instrument 操作,即加载我们的应用和 jaeger-client ,从而开始应用和 Jaeger 之间的数据交互操作。放大这部分,我们可以参考下图来了解详细的数据交互方式:

  图 6. Jaeger 的插桩流程

  在图 5 中,我们可以观察到 Jaeger 的完整概览设计。从中我们会发现Jaeger有5个模块元素,如下所列,接下来我们将解释这5个模块的作用:

  1.Jaeger 客户端

  2.代理

  3.采集器

  4.数据存储

  5.用户界面

  Jaeger-client(客户端库)

  client就是client lib,方便不同语言的项目介入Jaeger。当我们的应用程序被加载时,客户端将负责采集数据并将其发送给代理。目前 Jaeger SDK 支持如下:

  - 官方的

  1.去吧

  2.Java

  3.节点

  4.Python

  5.C++

  - 非官方

  1.PHP

  3.其他

  客户端支持 OpenTracing 标准。如上所述,Zipkin 还支持 OpenTracing 标准,这意味着我们的应用程序可以在嵌入 Jaeger-client 的任何时候替换为 Zipkin,这对业务是完全透明的。

  代理(客户端代理)

  jaeger 的代理是一个网络守护进程,它在 UDP 端口上侦听接收跨度数据。与大多数分布式系统都有一个 Agent 一样,Jaeger 的 Agent 具有以下特点:

  代理采集这些跨度信息并将其汇总到采集器;

  代理被设计为基本组件,旨在作为基础架构组件部署到所有主机;

  代理将客户端库和采集器解耦,将客户端库与路由和发现采集器的细节屏蔽掉;

  采集器(数据采集处理)

  采集器,顾名思义,从代理采集跟踪,通过处理管道处理它们,并将它们写入后端。

  当前采集器的工作是管理跟踪、构建索引、执行相关转换并最终存储它们。

  采样逻辑运行在 Collector 中,它按照我们设置的采样方式对数据进行采集和处理。

  数据存储

  Jaeger 的数据存储是组件的方式。

  目前支持 Cassandra 和 ElasticSearch(当然也支持纯内存模式,但不适合生产环境)。

  Query & UI(数据查询和前端界面展示)

  查询是一种从存储中检索跟踪并提供显示它们的 UI 的服务。上图是一个trace的数据流,可以在Jaeger UI上展示为一个系统函数的数据传播/执行图。

  5. 部署方式

  由于解决方案不同,Jaeger 的部署依赖于不同的服务。这些第三方基础服务的部署和安装不再属于本文讨论范围,如docker、Elasticsearch、Cassandra等。

  5.1 多合一

  为了方便您快速使用,Jaeger 直接提供了一个 All in one docker 镜像。通过 All in one 镜像,我们可以直接启动一个完整的 Jaeger 追踪系统,使用如下命令:

  $ 码头工人运行 -d -e \

  COLLECTOR_ZIPKIN_HTTP_PORT=9411 \

  -p 5775:5775/udp\

  -p 6831:6831/udp\

  -p 6832:6832/udp\

  -p 5778:5778\

  -p 16686:16686 \

  -p 14268:14268\

  -p 9411:9411\

  jaegertracing/all-in-one:最新

  启动成功后,可以去:16686看到Jaeger UI如下图。

  图 7. Jaeger UI 主页

  注意:在 All in one 模式下,Data Store 使用内存,因此如果您重新启动docker 容器,您将无法看到之前的数据。因此,该模式只能用于早期演示或验证,不能部署在生产环境中。

  5.2 独立部署

  当然,更推荐的方式是独立部署。独立部署也可以分为docker镜像模式和二进制模式。官网对docker镜像模式启动命令有详细的介绍,这里就不贴复制了。

  对于二进制部署,请参阅 github 上 Jaeger 的二进制包。该地址提供mac、linux、windows三大操作系统的二进制包。以linux为例,解压后可以找到如下bin包,明显对应我们前面提到的模块:

  drwxrwxr-x 3 2000 2000 4.0K 5 月 28 日 23:29 jaeger-ui-build

  -rwxrwxr-x 1 2000 2000 27M 5 月 28 日 23:29 jaeger-standalone

  -rwxrwxr-x 1 2000 2000 22M 5 月 28 日 23:29 jaeger-query

  -rwxrwxr-x 1 2000 2000 25M 5 月 28 日 23:29 jaeger-collector

  -rwxrwxr-x 1 2000 2000 16M 5 月 28 日 23:29 jaeger-agent

  注意:Jaeger 还为 Kubernetes 和 OpenShift 提供模板。详细介绍请参考github地址

  5.3 端口说明

  通过上面的 All in one 启动方式,我们直接发现 Jaeger 在启动时占用了大量的端口。当然,并非所有端口都是必需的。以下是这些端口的简要说明:

  端口协议所属模块的功能

  5775UDPagent通过兼容的Thrift协议接收Zipkin thrift类型的数据

  6831UDPagent通过兼容的Thrift协议接收Jaeger thrift类型的数据

  6832UDPagent通过二进制Thrift协议接收Jaeger thrift类型数据

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线