2025年1月

这是小卷对分布式系统架构学习的第6篇文章,关于链路追踪,之前写过traceId的相关内容:
https://juejin.cn/post/7135611432808218661,不过之前写的太浅了,且不成系统,只是简单的理解,今天来捋一下链路追踪的理论

1.为什么需要链路追踪

在复杂的分布式系统中,系统通常由多个独立的服务组成,几乎每一个前端请求都会形成一个复杂的分布式服务调用链路。

这种复杂的系统会带来一系列问题:

1.如何快速定位问题,判断故障影响范围?

2.如何梳理服务间的依赖关系?

链路追踪的用途就是为了知道请求在系统中的流转路径,定位性能瓶颈,诊断故障等。

2.追踪与跨度

要理解链路追踪的原理,先理解
Trace
追踪 和
Span
跨度两个概念。

  • Trace(追踪)
    :一个完整的用户请求流程,从用户发起请求开始,到请求结束。一个追踪包含多个 Span。
  • Span(跨度)
    :一种表示工作单元的结构,通常对应着请求经过的某个服务或者操作,每个Span包含以下信息:
    • Span ID:唯一标识当前Span
    • Trace ID:标识属于同一个Trace的所有Span
    • 父Span ID:如果当前Span由另一个Span引发,则会记录父Span ID
    • 时间戳、标签和日志

每一次Trace是由若干个有顺序、有层级关系的Span组成的一棵追踪树结构,图片来源
Dapper论文

3.链路追踪的概念


广义
上,分布式链路追踪系统可以分为三个部分:
数据收集、数据存储、数据展示


狭义
上,指链路追踪的
数据收集
部分

比如:Spring Cloud Sleuth就属于狭义的追踪系统,通常会搭配 Zipkin 作为数据展示,搭配 Elasticsearch 作为数据存储来组合使用。

这里从Dapper论文的内容总结下链路追踪的设计目标如下:

  • 低开销
    :追踪系统对正在运行的服务应该具备很小的性能影响
  • 应用层透明性
    :开发人员无需关注追踪系统,作为业务组件,尽可能减少对业务系统的代码侵入性。使用时透明,减少开发负担。如果需要依赖开发者配合才能使追踪系统生效,这样是无法满足追踪系统“无所不在的部署”这个需求
  • 可扩展性
    :支持分布式部署,具备良好的扩展性,能支持的组件越多越好,至少在接下来几年内能处理服务和集群的规模
  • 数据的快速分析
    :追踪数据生成后的数据分析要快,分析维度尽可能多,理想情况下是一分钟内,数据的新鲜度能快速对生产异常做出反应。

4.功能模块

生产环境的链路追踪系统,主要分为4个大模块:

4.1 埋点与生成日志

分客户端埋点、服务端埋点、以及客户端和服务端双向埋点,埋点日志通常包含了traceId、spanId、调用的开始时间,协议类型、调用方ip和端口,请求的服务名、调用耗时,调用结果,异常信息等

需要写log,高并发服务中,性能影响越重,通常使用采样+异步log的方式解决

4.2 收集和存储日志

特点是需支持分布式日志采集方案,一般还会用MQ作为缓冲

每个机器上有个daemon,这里的daemon指的后台服务进程,专门用于日志收集和Trace转发;

多级collector,类似pub/sub架构,可以负载均衡;

聚合数据进行实时分析和离线存储;

离线分析 需将同一条调用链的日志汇总在一起;

4.3 分析和统计调用链数据

调用链跟踪分析
:把同一TraceID的Span收集起来,按时间排序就是timeline。
把ParentID串起来就是调用栈

4.4数据展现以及决策支持

5.数据收集的三种实现方式

不论是狭义还是广义的链路追踪系统,都必须包含数据收集的工作,介绍三种主流的数据收集方式:

5.1基于日志的追踪Log-based Tracing

思路是将 Trace、Span 等信息直接输出到应用日志中,然后将日志归集过程汇聚到一起,再从全局日志信息中反推出完整的调用链拓扑关系;

日志追踪对网络消息完全没有侵入性,对应用程序只有很少量的侵入性,对性能的影响也非常低

缺点:

  • 依赖日志归集过程,日志不求决对的一致和连续,精准性较低。
  • 业务服务的调度和日志归集不是由同一个进程同时完成的,存在日志延迟或丢失的问题,从而产生追踪失真的情况

5.2基于服务的追踪

目前最常见的追踪实现方式,如Zipkin、SkyWalking、Pinpoint 等主流追踪系统都采用这种方式,其实现思路是:
通过某些手段给目标应用注入追踪探针(Probe)
,比如针对 Java 应用,一般就是通过 Java Agent 注入的。

探针可以看作是目标服务身上的小型微服务系统,有服务注册、心跳检测等功能,有专门的数据收集协议,可以把从目标系统收集的服务调用信息,通过HTTP 或者 RPC 请求,发送给追踪系统

该方式具备追踪的精确性和稳定性,缺点是消耗的资源更多,具备更强的侵入性

下图是pinpoint的调用栈示例:

Pinpoint 本身就是比较重负载的系统(运行它必须先维护一套 HBase),服务追踪这方面国产开源的Skywalking更加轻量化

5.3基于边车代理的追踪

·基于边车代理的追踪是服务网格的专属方案,也是最理想的分布式追踪模型,对应用完全透明,无论是日志还是服务本身,都不会有任何变化;

边车代理本身对应用透明的工作原理,决定了它只能实现服务调用层面的追踪,像前面 Pinpoint 截图那样的本地方法调用级别的追踪诊断,边车代理是做不到的。

6.链路追踪协议

链路追踪协议的发展历史,2016 年 11 月,CNCF 技术委员会接受了 OpenTracing 作为基金会的第三个项目。OpenTracing 是一套与平台无关、与厂商无关、与语言无关的追踪协议规范。

但是,Google 却在这个时候出来并提出了与 OpenTracing 目标类似的 OpenCensus 规范,且得到了巨头 Microsoft 的支持,这样就形成了OpenTracing 和 OpenCensus两大可观测性的阵营。

2019 年,OpenTracing 和 OpenCensus 宣布握手言和,共同发布了可观测性的终极解决方案OpenTelemetry,并宣布会各自冻结 OpenTracing 和 OpenCensus 的发展。

6.1 OpenTracing

概述
:OpenTracing是一个开放的API规范,旨在通过提供一套统一的接口,帮助开发人员能够在其应用中实现分布式追踪

和一般的规范标准不同,Opentracing 不是传输协议,消息格式层面上的规范标准,而是一种语言层面上的
API 标准
。以 Go 语言为例,只要某链路追踪系统实现了 Opentracing 规定的接口(interface),符合Opentracing 定义的表现行为,那么就可以说该应用符合 Opentracing 标准。

官网:
https://opentracing.io/

6.2 OpenCensus

OpenCensus为微服务和单体应用提供可观测性,通过追踪请求在服务之间传播并捕获关键的时间序列指标。其核心功能是从应用程序中收集追踪和指标,能够在本地显示并将其发送到任何分析工具(也称为“后端”)

官网:
https://opencensus.io/

6.3 OpenTelemetry

官网:
https://opentelemetry.io/

OpenTelemetry 可以用于从应用程序收集数据。它是一组工具、API 和 SDK 集合,我们可以使用它们来检测、生成、收集和导出遥测数据(指标、日志和追踪),以帮助分析应用的性能和行为。具体的解释为:

  • 一个可观测性框架和工具包,旨在创建和管理遥测数据,如追踪、指标和日志。
  • 与供应商和工具无关,这意味着它可以与各种可观测性后端一起使用,包括开源工具如Jaeger和Prometheus,以及商业产品。
  • 不是像Jaeger、Prometheus或其他商业供应商那样的可观测性后端。
  • 专注于遥测的生成、收集、管理和导出。OpenTelemetry的一个主要目标是能够轻松地在应用程序或系统中插桩,无论它们使用何种语言、基础设施或运行时环境。遥测的数据存储和可视化故意留给其他工具。

篇幅问题就不继续详细介绍这三个协议了,感兴趣的小伙伴们可以自行去官方了解。

总结:今天讲了链路追踪的理论知识,包括:追踪与跨度的概念,一个追踪系统的模块划分,数据收集的3种方式,以及链路追踪协议的发展。了解这些概念后再更容易去理解开源的链路追踪框架。

文件包含

文件包含漏洞(File Inclusion Vulnerability)是一种常见的网络安全漏洞,主要出现在应用程序中不安全地处理文件路径时。攻击者可以利用此漏洞执行恶意文件,或者访问不该被访问的文件

1. low

有3个页面

随便点击一个,可以在url处发现传参点

访问:
http://127.0.0.1/DVWA/vulnerabilities/fi/?page=file4.php
,可以看到下面的提示

远程文件包含

本地文件包含

源码审计

<?php

// The page we wish to display
$file = $_GET[ 'page' ];

?>

可以看到没有做任何过滤

2. medium

访问
file4.php,
依然可以访问

访问
?page=../../phpinfo.php
发现没有回显,可以确定是被过滤了

尝试双写绕过,成功绕过

..././ <=> ../

源码审计

<? php
// 显示的页面
$file = $_GET['page'];

// 输入验证
$file = str_replace(array("http://", "https://"), "", $file);
$file = str_replace(array("../", "..\\"), "", $file);
?>


http://; https://; ../ ; ..\
替换为了
' '
,可以利用双写绕过过滤

3. high

访问
1.php
看看报错信息

直接文件读取

源码审计

<?php
// 显示的页面
$file = $_GET['page'];

// 输入验证
if (!fnmatch("file*", $file) && $file != "include.php") {
    // 这不是我们想要的页面!
    echo "错误:文件未找到!";
    exit;
}  
?>

要求了参数必须以file开头,即
?page=file..........
,否则就会报错

4. impossible

源码审计

<?php
  // 我们希望显示的页面
$file = $_GET['page'];

// 只允许 include.php 或 file{1..3}.php
if ($file != "include.php" && $file != "file1.php" && $file != "file2.php" && $file != "file3.php") {
    // 这不是我们想要的页面!
    echo "错误:文件未找到!";
    exit;
}
?>

只允许访问
include.php 或 file{1..3}.php
页面,其他页面全部过滤

本文介绍在
高德开放平台
中,申请、获取地图
API

Key
的方法;同时通过简单的
Python
代码,调取
API
信息,对所得
Key
的可用性加以验证。

首先,我们进入
高德开放平台

官方网站
。如果大家是第一次使用高德地图开放平台,那么需要点击右上角注册一个开发者账号。

注册完毕后,登录这一账号,并点击如下图紫色框内所示的“
控制台
”。

image

随后,点击左侧“
应用管理
”中的“
我的应用
”,并到右上角点击“
创建新应用
”;如下图所示。

随后,在弹出的窗口中,依据我们的需求,输入应用的名称及其类型;如下图所示。其中,
名称
就按照我们实际需求来命名,
类型
则按照我们的应用场景来选择即可。

接下来,在界面右上角,点击“
添加Key
”;如下图所示。

随后,为这个
Key
命名,并选择所需的服务平台;如下图所示。我这里后续打算基于
Python
语言的
requests
库来使用
API
,所以这里就选择了“
Web服务
”。

随后,即可生成
Key
,如下图中紫色框内所示。

获取
Key
之后,我们可以验证一下这个
Key
是否可以使用。在这里,给出一个基于
Python
语言,使用高德开放平台提供的“
天气查询
”功能,对
Key
加以验证的代码;代码如下。

# -*- coding: utf-8 -*-
"""
Created on Wed May 29 20:36:11 2024

@author: fkxxgis
"""

import json
import requests

key = "在这里输入您的key"
city_code = "410300"

response = requests.get("https://restapi.amap.com/v3/weather/weatherInfo?key=" + key + "&city=" + city_code)
text = response.text
result = json.loads(text)
need = result["lives"][0]

print("您查询的" + need["province"] + need["city"] + "在" + need["reporttime"] + "的天气为" + need["weather"] + ",温度为" + need["temperature_float"] + "," + need["winddirection"] + "风" + need["windpower"] +"级。")

其中,大家在使用上述验证代码时,需要将
key = "在这里输入您的key"
这句代码引号中的内容,修改为通过前述操作所获取的
Key
;同时,还可以修改
city_code
,从而对不同城市、区域的天气加以查询。这里关于城市的代码,大家可以参考这个
网站

随后,即可执行上述代码;执行代码后,将打印出指定区域在查询时刻的实时天气。

可以看到,已经可以获取到对应地区的实时天气了,说明前述获取
Key
的流程已经成功。

此外,关于
API
使用的方法,我们后续会用更多博客来详细介绍,所以上述代码我们就暂时不再逐句解释了,在这里仅仅用来验证我们所获取的
Key
的可用性。

此外,对于每一位高德开放平台的开发者,我们所能调取不同功能
API
的次数也是有每日限制的,具体配额我们可以在“
我的应用
”中,通过“
查看配额
”选项来查阅;如下图所示。

至此,大功告成。