惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

L
LINUX DO - 最新话题
Cyberwarzone
Cyberwarzone
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Security Archives - TechRepublic
Security Archives - TechRepublic
S
Securelist
V2EX - 技术
V2EX - 技术
www.infosecurity-magazine.com
www.infosecurity-magazine.com
P
Privacy & Cybersecurity Law Blog
Spread Privacy
Spread Privacy
N
News and Events Feed by Topic
H
Heimdal Security Blog
Hacker News - Newest:
Hacker News - Newest: "LLM"
大猫的无限游戏
大猫的无限游戏
L
LangChain Blog
爱范儿
爱范儿
阮一峰的网络日志
阮一峰的网络日志
G
GRAHAM CLULEY
L
Lohrmann on Cybersecurity
G
Google Developers Blog
Recorded Future
Recorded Future
H
Hacker News: Front Page
Application and Cybersecurity Blog
Application and Cybersecurity Blog
The GitHub Blog
The GitHub Blog
量子位
V
V2EX
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Vercel News
Vercel News
H
Help Net Security
Know Your Adversary
Know Your Adversary
Forbes - Security
Forbes - Security
T
Threatpost
S
SegmentFault 最新的问题
Hugging Face - Blog
Hugging Face - Blog
T
Threat Research - Cisco Blogs
人人都是产品经理
人人都是产品经理
Project Zero
Project Zero
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
罗磊的独立博客
C
Check Point Blog
P
Palo Alto Networks Blog
Google DeepMind News
Google DeepMind News
Last Week in AI
Last Week in AI
L
LINUX DO - 热门话题
Apple Machine Learning Research
Apple Machine Learning Research
C
Cybersecurity and Infrastructure Security Agency CISA
A
Arctic Wolf

元视角

.NET 生态下的 Agent 框架选型:从 ReAct 到原生推理 - 元视角 从「能用」到「好用」:LLM 流式响应实现方式的探索之路 - 元视角 当我用 2000 条聊天记录,让 AI 为我画一幅自画像 - 元视角 基于 Supabase 的 AI 应用开发探索 - 元视角 微博 × MCP:社交媒体新玩法解锁 - 元视角 四点钟海棠花未眠 - 元视角 Semantic Kernel × MCP:智能体的上下文增强探索 - 元视角 基于 K-Means 聚类分析实现人脸照片的快速分类 - 元视角 容器技术驱动下的代码沙箱实践与思考 - 元视角 温故而知新:后端通用查询方案的再思考 - 元视角 浅议 CancellationToken 在前后端协同取消场景中的应用 - 元视角 Semantic Kernel 视角下的 Text2SQL 实践与思考 - 元视角 关于 ChatGPT 的流式传输,你需要知道的一切 - 元视角 RAG 的是与非、Rewrite 和 Rerank - 元视角 使用 EFCore 和 PostgreSQL 实现向量存储及检索 - 元视角 基于 LLaMA 和 LangChain 实践本地 AI 知识库 - 元视角 使用 llama.cpp 在本地部署 AI 大模型的一次尝试 - 元视角 如何为 Git 配置多个 SSH Key - 元视角 C# 使用 LibUsbDotNet 实现 USB 设备检测 - 元视角 基于 C# 实现样式与数据分离的打印方案 - 元视角 基于 SVG 的图形交互方案实践 - 元视角 前端视频播放技术概览 - 元视角 温故而知新,再话 Python 动态导入 - 元视角 后 GPT 时代,NLP 不存在了? - 元视角 视频是不能 P 的系列:使用 Milvus 实现海量人脸快速检索 - 元视角 GDI+下字体大小自适应方案初探 - 元视角 小爱音箱集成 ChatGPT 的不完全教程 - 元视角 程序员视角下的三体世界随想 - 元视角 关于 Docker 容器配置信息的渐进式思考 - 元视角 在 Docker 容器内集成 Crontab 定时任务 - 元视角 为你的服务器集成 LDAP 认证 - 元视角 似花还似非花 - 元视角 视频是不能 P 的系列:使用 Dlib 实现人脸识别 - 元视角 浅议分布式链路追踪与日志的整合 - 元视角 关于 Git 大文件上传这件小事 - 元视角 .NET 进程内队列 Channel 的入门与应用 - 元视角 使用 Fody 实现 .NET 的静态编织 - 元视角 .NET Core + ELK 搭建可视化日志分析平台(下) - 元视角 聊一聊前端图片懒加载背后的故事 - 元视角 支持外部链接跳转的 Vue Router 扩展实现 - 元视角 视频是不能 P 的系列:OpenCV 和 Dlib 实现表情包 - 元视角 不得不说的 ASP.NET Core 集成测试 - 元视角 再议 DDD 视角下的 EFCore 与 领域事件 - 元视角 Vue.js 前端项目容器化部署实践极简教程 - 元视角 再见,人间四月天 - 元视角 Python 图像风格化迁移助力画家梦想 - 元视角 利用 ASP.NET Core 中的标头传播实现分布式链路追踪 - 元视角 利用 gRPC 实现文件的上传与下载 - 元视角 七种武器:延迟队列的原理和实现总结 - 元视角 gRPC 流式传输极简入门指南 - 元视角 浅议非典型 Web 应用场景下的身份认证 - 元视角 gRPC 借助 Any 类型实现接口的泛化调用 - 元视角 分布式丛林探险系列之 Redis 集群模式 - 元视角 分布式丛林探险系列之 Redis 主从复制模式 - 元视角 通过 Python 预测 2021 年双十一交易额 - 元视角 gRPC 搭配 Swagger 实现微服务文档化 - 元视角 SSL/TLS 加密传输与数字证书的前世今生 - 元视角 使用 Python 自动识别防疫健康码 - 元视角 你不可不知的容器编排进阶技巧 - 元视角 ASP.NET Core 搭载 Envoy 实现 gRPC 服务代理 - 元视角 再话 AOP,从简化缓存操作说起 - 元视角 ASP.NET Core 搭载 Envoy 实现微服务身份认证(JWT) - 元视角 ASP.NET Core 搭载 Envoy 实现微服务的监控预警 - 元视角 ASP.NET Core 搭载 Envoy 实现微服务的反向代理 - 元视角 ASP.NET Core gRPC 打通前端世界的尝试 - 元视角 EFCore 实体命名约定库:EFCore.NamingConventions - 元视角 ASP.NET Core gRPC 集成 Polly 实现优雅重试 - 元视角 ASP.NET Core gRPC 健康检查的探索与实现 - 元视角 ASP.NET Core gRPC 拦截器的使用技巧分享 - 元视角 SnowNLP 使用自定义语料进行模型训练 - 元视角 使用 HttpMessageHandler 实现 HttpClient 请求管道自定义 - 元视角 ABP vNext 的实体与服务扩展技巧分享 - 元视角 ABP vNext 对接 Ant Design Vue 实现分页查询 - 元视角 源代码探案系列之 .NET Core 跨域中间件 CORS - 元视角 源代码探案系列之 .NET Core 限流中间件 AspNetCoreRateLimit - 元视角 源代码探案系列之 .NET Core 并发限制中间件 ConcurrencyLimiter - 元视角 通过 EmbededFileProvider 实现 Blazor 的静态文件访问 - 元视角 低代码,想说爱你不容易 - 元视角 记一次失败的 ThoughtWorks 面试经历 - 元视角 从 C# 1.0 到 C# 9.0,历代 C# 语言特性一览 - 元视角 通过 Python 分析 2020 年全年微博热搜数据 - 元视角 基于 Python 和 Selenium 实现 CSDN 一键三连自动化 - 元视角 使用多线程为你的 Python 爬虫提速的 N 种姿势,你会几种? - 元视角 实现网页长截图的常见思路总结 - 元视角 温故而知新,由 ADO.NET 与 Dapper 所联想到的 - 元视角 视频是不能 P 的系列:OpenCV 人脸检测 - 元视角 作为技术宅的我,是这样追鬼滅の刃的 - 元视角 使用 Python 抽取《半泽直树》原著小说人物关系 - 元视角 厉害了!打工人用 Python 分析西安市职位信息 - 元视角 使用 dotTrace 对 .NET 应用进行性能分析与优化 - 元视角 一道 HashSet 面试题引发的蝴蝶效应 - 元视角 基于选项模式实现.NET Core 的配置热更新 - 元视角 Dapper.Contrib 在 Oracle 环境下引发 ORA-00928 异常问题的解决 - 元视角 .NET Core 中对象池(Object Pool)的使用 - 元视角 利用 MySQL 的 Binlog 实现数据同步与订阅(下):EventBus 篇 - 元视角 利用 MySQL 的 Binlog 实现数据同步与订阅(中):RabbitMQ 篇 - 元视角 利用 MySQL 的 Binlog 实现数据同步与订阅(上):基础篇 - 元视角 记一次从已损坏的 Git 仓库中找回代码的经历 - 元视角 .NET Core 原生 DI 扩展之属性注入实现 - 元视角 .NET Core 原生 DI 扩展之基于名称的注入实现 - 元视角
Envoy 集成 Jaeger 实现分布式链路追踪 - 元视角
飞鸿踏雪 · 2022-01-15 · via 元视角

当我们的应用架构,从单体系统演变为微服务时,一个永远不可能回避的现实是,业务逻辑会被拆分到不同的服务中。因此,微服务实际就是不同服务间的互相请求和调用。更重要的是,随着容器/虚拟化技术的发展,传统的物理服务器开始淡出我们的视野,软件被大量地部署在云服务器或者虚拟资源上。在这种情况下,分布式环境中的运维和诊断变得越来越复杂。如果按照功能来划分,目前主要有 Logging、Metrics 和 Tracing 三个方向,如下图所示,可以注意到,这三个方向上彼此都有交叉、重叠的部分。在我过去的博客里,我分享过关于 ELKPrometheus 的内容,可以粗略地认为,这是对 Logging 和 Metrics 这两个方向的涉猎。所以,这篇文章我想和大家分享是 Tracing,即分布式追踪,本文会结合 Envoy、Jaeger 以及 .NET Core 来实现一个分布式链路追踪的案例,希望能带给大家一点 Amazing 的东西。

可观测性:Metrics、Tracing & Logging 可观测性:Metrics、Tracing & Logging

分布式追踪

如果要追溯分布式追踪的起源,我想,Google 的这篇名为 《Dapper, a Large-Scale Distributed Systems Tracing Infrastructure》 的论文功不可没,因为后来主流的分布式追踪系统,譬如 ZipkinJeagerSkywalkingLightStep……等等,均以这篇论文作为理论基础,它们在功能上或许存在差异,原理上则是一脉相承,一个典型的分布式追踪系统,大体上可以分为代码埋点、数据存储和查询展示三个步骤,如下图所示,Tracing 系统可以展示出服务在时序上的调用层级,这对于我们分析微服务系统中的调用关系会非常有用。

分布式追踪系统基本原理 分布式追踪系统基本原理

一个非常容易想到的思路是,我们在前端发出的请求的时候,动态生成一个唯一的 x-request-id,并保证它可以传递到与之交互的所有服务中去,那么,此时系统产生的日志中就会携带这一信息,只要以此作为关键字,就可以检索到当前请求的所有日志。这的确是个不错的方案,但它无法告诉你每个调用完成的先后顺序,以及每个调用花费了多少时间。基于这样的想法,人们在这上面传递了更多的信息(Tag),使得它可以表达层级关系、调用时长等等的特征。如图所示,这是一个由 Jaeger 产生的追踪信息,我们从中甚至可以知道请求由哪台服务器处理,以及上/下游集群信息等等:

通过 Jaeger 收集 gRPC 请求信息 通过 Jaeger 收集 gRPC 请求信息

目前,为了统一不同 Tracing 系统在 API、数据格式等方面上的差异,社区主导并产生了 OpenTracing 规范,在这个 规范 中,一个 Trace,即调用链,是由多个 Span 组成的有向无环图,而每个 Span 则可以含有多个键值对组成的 Tag。如图所示,下面是 OpenTracing 规范的一个简单示意图,此时,图中一共有 8 个 Span,其中 Span A 是根节点,Span CSpan A 的子节点, Span GSpan F 之间没有通过任何一个子节点连接,称为 FollowsFrom

        [Span A]  ←←←(the root span)
            |
     +------+------+
     |             |
 [Span B]      [Span C] ←←←(Span C is a `ChildOf` Span A)
     |             |
 [Span D]      +---+-------+
               |           |
           [Span E]    [Span F] >>> [Span G] >>> [Span H]
                                       ↑
                                       ↑
                                       ↑
                         (Span G `FollowsFrom` Span F)

事实上,我们上面提到的 ZipkinJeager 都兼容这一规范,这使得我们可以更加灵活和自由地更换 Tracing 系统。除了 OpenTracing 规范,目前,OpenTelemetry 在考虑统一 Logging、Metrics 和 Tracing,即我们通常所说的 APM,如果大家对这个感兴趣,可以做更进一步的了解。

Envoy & Jaeger

目前,主流的服务网格平台如 Istio,选择 Envoy 作为其数据平面的核心组件。通俗地来讲,Envoy 主要是作为代理层来调节服务网格中所有服务的进/出站流量,它可以实现诸如负载均衡、服务发现、流量转移、速率限制、可观测性等等的功能。考虑到不同的服务都可以通过 Gateway 或者 Sidecar 来互相访问,我们更希望通过 Envoy 这个代理层来实现分布式追踪,而不是在每个应用内都去集成 SDK,这正是服务网格区别于传统微服务的地方,即微服务治理需要的各种能力,逐步下沉到基础设施层。如果你接触过微软的 Dapr,大概就能体会到我这里描述的这种变化。

Envoy 在 Istio 中扮演着重要角色 Envoy 在 Istio 中扮演着重要角色

事实上,Envoy 提供了入口来接入不同的 Tracing 系统,以 Zipkin 或者 Jeager 为例,除了前面提到的 x-request-id,它可以帮我们生成类似 x-b3-traceidx-b3-spanid 等等的请求头。参照 官方文档,它大体上提供了下面 3 种策略来支撑系统范围内的追踪:

  • 生成 UUID :Envoy 会在需要的时候生成 UUID,并操作名为 x-request-id 的 HTTP 头部,应用可以转发这个 HTTP 头部用于统一的记录和追踪。
  • 集成外部追踪服务:Envoy 支持可插拔的外部追踪可视化服务,例如 LightStep、Zipkin 或者 Zipkin 兼容的后端(比如说 Jaeger)等等。
  • 客户端追踪 ID 连接:x-client-trace-id 这个 HTTP 头部可以用来把不信任的请求 ID 连接到受信的 x-request-id HTTP 头部上。

这意味着,我们可以从客户端或者由 Envoy 来产生一个 x-request-id,只要应用转发这个 x-request-id 或者 外部追踪系统需要的 HTTP 头部,Envoy 就可以帮我们完成把这些追踪信息告诉这些外部追踪系统,甚至在 Sidecar 模式下这一切都是自动完成的。我在写这篇博客时发现,官方还是比较推崇 Sidecar 模式,即一个服务就是一个 Pod,每个 Pod 里自带一个 Envoy 作为代理,对于 Sidecar 模式而言,它的分布式追踪呈现出下面这样的结构,如果你认真阅读过官方的文档和示例,就会发现其 示例 基本都是这种结构:

Sidecar 模式下的分布式追踪示意图 Sidecar 模式下的分布式追踪示意图

考虑到,2022 年还有没有用上 K8S 的人,以及 Catcher Wong 大佬反映 Sidecar 模式比较浪费资源,这里我们还是用 Gateway 模式来实现,譬如我们有两个服务,订单服务(OrderSevice) 和 支付服务(PaymentService),它们都由同一个 Envoy 来代理,当我们在订单服务中调用支付服务时,就会产生一条调用链。对于大多数的微服务而言,从它被拆分地那一刻起,就不可避免地走向了像蜘蛛网一般错综复杂的结局,此时,它的分布式追踪呈现出下面的结构:

Gateway 模式下的分布式追踪示意图 Gateway 模式下的分布式追踪示意图

如果从代码侵入角度来审视这个问题,Sidecar 模式,每个服务都由 Envoy 去生成或者是设置一系列相关的请求头;而如果采取 Gateway 模式,当你在订单服务里调用支付服务时,无论你使用 HttpClient 还是 gRPC,你都需要确保这一系列的请求头能传递下去,这意味着我们要写一点无关紧要的代码,这样看起来前者更好一点,不是吗?可惜,合适和正确,就像鱼和熊掌一样,永远不可兼得。

Span 模型示意图 Span 模型示意图

关于 Jeager,这是一个由 Uber 开发的、受 Dapper 和 Zipkin 启发的分布式追踪系统,它主要适用于:分布式追踪信息传递、分布式事务监控、问题分析、服务依赖性分析、性能优化这些场景,因为它兼容 OpenTracing 标准,所以 Span 这个术语对它来说依然使用,什么是 Span 呢?它是一个追踪的最小逻辑单位,可以记录操作名,操作开始时间 和 操作耗时,下面是 Jaeger 的架构示意图,大家可以混个眼熟:

Jaeger 的架构示意图 Jaeger 的架构示意图

第一个实例

OK,现在来分享本文的第一个示例,如前文所述,我们要实现的是一个 Gateway 模式下的请求追踪。为此,我们准备了两个 ASP.NET Core 项目,分别来模拟订单服务(OrderService) 和 支付服务(PaymentService),当我们通过 Envoy 访问 OrderService 的时候,会在其内部访问 PaymentService,以此来验证 Envoy 能否帮我们找到这条调用链。首先,我们来编写 OrderService,代码非常简单,从 HTTP 请求头中拿到 Jeager 需要的字段,并在调用 OrderService 的时候传递这些字段:

[HttpPost]
public async Task<IActionResult> Post([FromBody] OrderInfo orderInfo)
{
  var paymentInfo = new PaymentInfo()
  {
    OrderId = orderInfo.OrderId,
    PaymentId = Guid.NewGuid().ToString("N"),
    Remark = orderInfo.Remark,
  };

  // 设置请求头
  _httpClient.DefaultRequestHeaders.Add("x-request-id", Request.Headers["x-request-id"].ToString());
  _httpClient.DefaultRequestHeaders.Add("x-b3-traceid", Request.Headers["x-b3-traceid"].ToString());
  _httpClient.DefaultRequestHeaders.Add("x-b3-spanid", Request.Headers["x-b3-spanid"].ToString());
  _httpClient.DefaultRequestHeaders.Add("x-b3-parentspanid", Request.Headers["x-b3-parentspanid"].ToString());
  _httpClient.DefaultRequestHeaders.Add("x-b3-sampled", Request.Headers["x-b3-sampled"].ToString());
  _httpClient.DefaultRequestHeaders.Add("x-b3-flags", Request.Headers["x-b3-flags"].ToString());
  _httpClient.DefaultRequestHeaders.Add("x-ot-span-context", Request.Headers["x-ot-span-context"].ToString());

  // 调用/Payment接口
  var content = new StringContent(JsonConvert.SerializeObject(paymentInfo), Encoding.UTF8, "application/json");
  var response = await _httpClient.PostAsync("/Payment", content);

  var result = response.IsSuccessStatusCode ? "成功" : "失败";
  return new JsonResult(new { Msg = $"订单创建{result}" });
}

接下来,PaymentService 就会变得非常简单,因为我们不会真的去对接一个支付系统,所以,就简单意思一下好啦!

 [HttpPost]
public IActionResult Post([FromBody] PaymentInfo paymentInfo)
{
  var requestId = Request.Headers["x-request-id"].ToString();
  return new JsonResult(new { Msg = $"支付成功, 流水号:{requestId}" });
}

服务编写好以后,按照惯例,我们使用 docker-compose.yaml 文件来进行编排,除了 OrderServicePaymentService,我们还需要 EnvoyJeager,即至少需要四个服务:

version: '3'
services:
  envoy_gateway:
    build: Envoy/
    ports:
      - "9090:9090"
      - "9091:9091"
    volumes:
      - "./Envoy/envoy.yaml:/etc/envoy/envoy.yaml"
      - "./Envoy/logs/:/etc/envoy/logs/"
  order_service:
    build: OrderService/
    ports:
      - "8081:80"
    environment:
      ASPNETCORE_URLS: "http://+"
  payment_service:
    build: PaymentService/
    ports:
      - "8082:80"
    environment:
      ASPNETCORE_URLS: "http://+"
  jaeger:
    image: jaegertracing/all-in-one
    environment:
    - COLLECTOR_ZIPKIN_HOST_PORT=9411
    ports:
    - "9411:9411"
    - "16686:16686"

此时,重头戏终于来了,Envoy 是如何连接外部追踪系统的呢?我们可以设置 HttpConnectionManager 这个过滤器下的 tracing 字段,这里我们选择 ZipkinConfig 这个类型,因为 Jaeger 完全兼容 Zipkin,所以,我们可以直接使用这个 Provider。

    filter_chains:
    - filters:
      - name: envoy.filters.network.http_connection_manager
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
          generate_request_id: true
          tracing:
            provider:
              name: envoy.tracers.zipkin
              typed_config:
                "@type": type.googleapis.com/envoy.config.trace.v3.ZipkinConfig
                collector_cluster: jaeger
                collector_endpoint: "/api/v2/spans"
                collector_endpoint_version: HTTP_JSO

基本上,只要是官方支持的 Provider,我们都可以照猫画虎接入进来,当然每一种 Provider 的配置项可能会不一样,这里我们唯一要注意的是 collector_cluster, 它表示的是指向 Jeager 服务器的一个 Cluster,这意味着我们要为它单独定义一个 Cluster :

  clusters:
  - name: jaeger
    type: STRICT_DNS
    connect_timeout: 0.25s
    lb_policy: ROUND_ROBIN
    load_assignment:
      cluster_name: jaeger
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address:
                address: jaeger
                port_value: 9411

还记得 Envoy 支撑系统内的分布式追踪的三个支撑策略是什么吗?显然,我们可以通过 generate_request_id 字段来控制 Envoy 生成作用于 x-request-idUUID,我们希望用户从 前端 或者 cURL 中发送的请求,都能自动地带上 x-request-id 请求头,所以,我们这里将其设为 true,这意味着,从现在开始,我们的请求有了这样一个 x-request-id, 其实,如果不考虑 Jeager 的话,我们请求已经可以实现追踪了,只要后续的请求都像我这里一样传递 x-request-id 即可。原因我们已经在前面说过,此时,这些请求没有一个上下文的概念,更不要说要理清楚其中的调用层级,所以,接下来,我们还要做一点微不足道的工作:

            virtual_hosts:
            - name: backend
              domains:
              - "*"
              routes:
              - match:
                  prefix: "/Payment"
                route:
                  auto_host_rewrite: true
                  prefix_rewrite: /api/Payment
                  cluster: payment_service
                decorator:
                  operation: PaymentService
              - match:
                  prefix: "/Order"
                route:
                  auto_host_rewrite: true
                  prefix_rewrite: /api/Order
                  cluster: order_service
                decorator:
                  operation: OrderService

如上所示,如果我们希望 Envoy 能记录我们的请求,那么,我们的请求必须要从它这里经过。这听起来像一句废话,可是在我调用 PaymentService已经确保我的请求是从 /Payment 这个路由上发起。默认情况下,在生成 Span 的时候,Envoy 会使用 --service-cluster 这个参数来作为 Span 的名称,这个参数通常写在 Envoy 的启动命令里,在这个示例中,它的取值是 reverse-proxy。仔细一想,会觉得哪里不太对,这样一来,所以的 Span 不就是同一个名字了吗?事实上,一开始我做实验的时候,确实是这个结果。解决方是设置一个 operation。此时,如果我们通过 Postman 访问订单接口 /Order,不出意外的话,我们会收到订单创建成功的结果,在浏览器里输入http://localhost:16686,我们来看看 Jeager 都收集到了哪些信息:

JeagerUI 数据查询 JeagerUI 数据查询

从图中我们可以非常容易地识别出 Service 和 Operation 在 Envoy 中分别对应着什么,我们注意到这里检索到了三个 Span,因为博主后来又加了一个 EchoService,从这里我们能看到它整个过程从何时开始,经过多长时间以后结束。如果我们点击它,会看到更加详细的说明,如下图所示:

JeagerUI 数据展示 JeagerUI 数据展示

显然,这个调用关系是符合我们预期的,即客户端调用了OrderServiceOrderService调用了PaymentService,对于每一次调用,我们均可以从 Span 的 Tag 中获得更多信息,文章中的第三张图,实际上就是出自这里,有了这些信息以后,我们排查或者分析微服务中的问题,是不是感觉容易了很多呢?结合 ELK,你可以知道要去找哪里的日志,而这些正是分布式追踪的意义所在!

通过 Jaeger 收集 gRPC 请求信息 通过 Jaeger 收集 gRPC 请求信息

好了,到这里为止,关于 Envoy 在分布式追踪上的探索,终于可以告一段落,完整的项目文件我已经放在 Github 上供大家参考,谢谢大家!

本文小结

可观测性(Logging、Metrics & Tracing) 是当下微服务中重要的一个组成部分,从 ELk 收集日志,到 Prometheus 监控指标, 再到 Jeager 追踪调用链,我们看到了一种完全不同于单体系统中打断点、单步调试的诊断思路,这是否说明,微服务的治理永远是一个绕不过去的话题。在这篇文章里,我们简单介绍了分布式追踪系统,比如最常见的 ZipkinJeagerSkywalkingLightStep…等等,其基本思想是生成一个 x-request-id,并在不同的服务或者应用中传递这个信息。在此基础上,我们介绍了 OpenTracing 规范,即 一个调用链(Trace),是由多个 Span 组成的有向无环图,而每个 Span 则可以含有多个键值对组成的 Tag。目前,Envoy 官方主推的是 Sidecar 模式,即每个服务分配一个 Envoy 作为代理,考虑到博主目前使用 Gateway 模式更多一点,故结合 ASP.NET Core 和 Jeager 实现了一个简单的示例,这个示例唯一的不足在于,服务或者应用必须显式地传递这些请求头,如果直接集成 SDK,效果应该会比现在好很多,可这样的话,就显得不那么云原生了,如果大家有更好的做法,欢迎在评论区留言和交流。大家可以稍微注意一下 OpenTelemetry 这个项目,如果你需要更完备的可观测性信息收集。好了,以上就是这篇博客的全部内容,晚安,世界。