分布式-构建可观测性系统

分布式系统-可观测性

​ 一般将可观测性分解为具体的三部分,分别是事件日志、链路追踪、聚合度量,这三部分各有侧重但又不完全独立,他们在内容上有重合之处。

  • Logging 日志

  • Tracing 追踪

  • Metrics 度量

image-20230117093635719

APM 系统

​ APM 系统(Application Performance Management,应用性能管理)是对企业的应用系统进行实时监控,实现对应用性能管理和故障定位的系统化解决方案。APM 作为系统运维管理和网络管理的一个重要方向,能够对关键服务进行监控、追踪以及告警,帮助开发和运维人员轻松地在复杂的应用系统中找到故障点,提高服务的稳定性,降低 IT 运维的成本。

流行的APM系统如下:

APM 描述
SkyWalking 中国工程师吴晟(华为)开源的一款分布式追踪,分析,告警的工具,现在是Apache旗下开源项目,对云原生支持,目前增长势头强劲,社区活跃,中文文档没有语言障碍。
Zipkin Twitter公司开源的一个分布式追踪工具,被Spring Cloud Sleuth集成,使用广泛而稳定,需要在应用程序中埋点,对代码侵入性强
Pinpoint 一个韩国团队开源的产品,探针收集的数据粒度非常细,但性能损耗大,因其出现的时间较长,完成度很高。
Cat 美团大众点评开源的一款分布式链路追踪工具。需要在应用程序中埋点,对代码侵入性强。

解决方案

日志:Filebeat + ELK

追踪:Sky Walking、Zipkin、Jaeger

度量: Prometheus (云原生架构) 、Zabbix

日志系统

基于ELKB的日志管理系统主要组件如下:

  • 日志收集 FileBeat(Beats)

  • 缓冲、解耦 Redis 、Kafka

  • 聚合加工(数据处理):LogStash

  • 索引、存储:ElasticSearch

  • 分析查询:Kibana

常用的组合方案有:

  1. filebeat + ElasticSearch + Kibana
  2. filebeat + Kafka + ElasticSearch + Kibana
  3. filebeat + LogStash + Kafka + ElasticSearch + Kibana

最佳实践

QQ音乐使用ELK 构建日志处理平台,提供无侵入、集中式的远程日志采集和检索系统。

image-20230116225858548

Filebeat 作为日志采集和传送器。Filebeat监视服务日志文件并将日志数据发送到Kafka。

Kafka 在Filebeat和Logstash之间做解耦。

Logstash 解析多种日志格式并发送给下游。

ElasticSearch 存储Logstash处理后的数据,并建立索引以便快速检索。

Kibana 是一个基于ElasticSearch查看日志的系统,可以使用查询语法来搜索日志,在查询时制定时间和日期范围或使用正则表达式来查找匹配的字符串。

filebeat

filebeat 是Elastic Stack 组件Beats 中的一个轻量级日志收集器。

官方文档:https://www.elastic.co/guide/en/beats/filebeat/7.11/index.html

filebeat 将日志文件发送到 Logstash 或 Elasticsearch。

Filebeat –> ES

Filebeat –> logstash –> ES

Filebeat –> kafka –> logstash –> ES

配置

启动

./filebeat -e -c filebeat.yml -d "publish"

-c 用于指定 filebeat.yml 配置文件的位置,***-e*** 可在终端上显示 Filebeat 的日志信息。

logstash

https://www.elastic.co/guide/en/logstash/7.11/index.html

Kibana

Kibana 是为 Elasticsearch设计的开源的分析和可视化平台。你可以使用 Kibana 来查看存储在 ES 索引中的数据并与之交互。你可以很容易实现高级的数据分析和可视化,以图表的形式展现出来。

​ 文档:https://www.elastic.co/guide/en/kibana/7.11/index.html

使用步骤:

设置索引模式 Index Pattern

查询数据 Discover

支持多种方式查询

  • Lucene 语法
  • 基于 JSON 的 Elasticsearch 查询 DSL
  • Kibana 查询语言 KQL

可视化 Visualize

Kibana可视化控件基于 Elasticsearch 的查询。利用一系列的 Elasticsearch 查询聚合功能来提取和处理数据,再通过创建图表来呈现数据分布和趋势

点击Visualize菜单,进入可视化图表创建界面,Kibana自带有上10种图表,我们来创建一个自己的图表

仪表盘 Dashboard

Kibana 仪表板展示保存的可视化结果集合。
就是可以把上面定义好的图表展示
创建一个Dashboard

开发者工具

控制台 Dashboard

控制台插件提供一个用户界面来和 Elasticsearch 的 REST API 交互

其他

语言切换为中文

config/kibana.yml添加

i18n.locale: "zh-CN"

链路追踪系统

​ 分布式链路追踪的原理是通过在分布式系统中跟踪请求的完整路径,记录各服务节点的调用关系和时间消耗,从而构建完整的请求调用链。其核心原理可归纳为以下几个关键点:

Trace(追踪)

​ 表示一次完整的分布式请求链路,由多个Span 组成。每个Trace通过全局唯一的TraceID标识,贯穿请求的所有服务节点。例如,用户发起一次订单请求,经过网关、订单服务、库存服务和支付服务,这些服务调用构成一条完整的Trace

Span(跨度)

Span是链路的最小单元,表示单个服务节点内的操作(如HTTP请求、数据库查询)。每个Span包含以下信息:

  • SpanID:唯一标识当前操作。
  • ParentSpanID:标识父级Span,用于构建调用层级关系
  • 时间戳:记录操作开始和结束时间,计算耗时

例如,订单服务调用库存服务时,订单服务生成一个Span,库存服务生成子Span,通过ParentSpanID关联

上下文传递(Context Propagation)

​ 通过请求头(如HTTP Header)传递TraceID、SpanID等上下文信息,确保跨服务调用时链路连续性。例如,Dubbo通过attachment字段传递,HTTP请求通过Header传递。

​ Trace Segment代表在单一操作系统进程(例如JVM)中执行的追踪部分。它包含了一组跨度(spans),这些跨度通常与单一请求或执行上下文关联。

​ skywalking 默认情况会采集大量 trace 数据,这样可以比较全的追踪所有请求调用链路的请求,但同时对 ES 存储资源要求非常高,需要我们投入很大的存储节点才可以。那么有没有一种采样的请求上报的机制呢?答案是有的,通过设置采样数据的比例,我们就可以在资源成本和采集条目之前取得一个平衡。

Apache SkyWalking

SkyWalking 是分布式系统的应用程序性能监视工具,专为微服务、云原生架构和基于容器(Docker、K8S、Mesos)架构而设计。SkyWalking是观察性分析平台和应用性能管理系统。提供分布式追踪、服务网格遥测分析、度量聚合和可视化一体化解决方案。

谁在用

​ 国内使用SkyWalking作为APM工具的公司已经非常多了,如腾讯、字节跳动、阿里云、百度、滴滴、华为、中国电信、中国移动、中国联通、招商银行、民生银行、微众银行等等。可以在官网查看更多用户列表。

架构设计

image-20221229234410191

整个架构,分成上、下、左、右四部分:

  • 上部分 Agent :负责从应用中,收集链路信息,发送给 SkyWalking OAP 服务器。目前支持 SkyWalking、Zikpin、Jaeger 等提供的 Tracing 数据信息。而我们目前采用的是,SkyWalking Agent 收集 SkyWalking Tracing 数据,传递给服务器。
  • 下部分 SkyWalking OAP :负责接收 Agent 发送的 Tracing 数据信息,然后进行分析(Analysis Core) ,存储到外部存储器( Storage ),最终提供查询( Query )功能。
  • 右部分 Storage :Tracing 数据存储。目前支持 ES、MySQL、Sharding Sphere、TiDB、H2 多种存储器。而我们目前采用的是 ES ,主要考虑是 SkyWalking 开发团队自己的生产环境采用 ES 为主。
  • 左部分 SkyWalking UI :负责提供控台,查看链路等等。

启动

bin目录下执行startup.bat ,此时会同时启动三个服务:

  • UI 服务默认使用8080端口
  • OAP Serve 服务默认使用 12800 端口
  • OTLP gRPC 默认使用 11800 端口

如果使用 SkyWalking 10.x,需确保 JDK 版本不低于 11。

上报日志(以logback 为例)

<!-- 如果想在项目代码中获取链路TraceId,则需要引入此依赖 -->
      <dependency>
          <groupId>org.apache.skywalking</groupId>
          <artifactId>apm-toolkit-trace</artifactId>
          <version>9.4.0</version>
      </dependency>

<!-- skywalking logback插件 -->
      <dependency>
          <groupId>org.apache.skywalking</groupId>
          <artifactId>apm-toolkit-logback-1.x</artifactId>
          <version>9.4.0</version>
      </dependency>

编写logback日志配置文件:打印traceId

<configuration>
    <!-- 日志存放路径 -->
	<property name="log.path" value="/var/log/ruoyi/" />
    <!-- 日志输出格式 -->
	<property name="log.pattern.trace" value="%d{HH:mm:ss} [%tid] [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
	<!-- 控制台输出 -->
	<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
		<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
            <layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.mdc.TraceIdMDCPatternLogbackLayout">
                <Pattern>${log.pattern.trace}</Pattern>
            </layout>
		</encoder>
	</appender>
	<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
		<encoder  class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
            <layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
                <Pattern>${log.pattern.trace}</Pattern>
            </layout>
		</encoder>
	</appender>

项目的启动配置中,添加jvm参数

编写logback日志配置文件:将日志数据上报给oap服务

<configuration>
	<property name="APM_PATTERN"
              value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{tid}] [%thread] %-5level %logger{36} -%msg%n" />
	<!--  skyWalking日志采集  -->
	<appender name="APM_LOG" class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender">
        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
            <layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.mdc.TraceIdMDCPatternLogbackLayout">
                <Pattern>${APM_PATTERN}</Pattern>
            </layout>
        </encoder>
    </appender>
    
    <root level="INFO">
        <appender-ref ref="APM_LOG"/>
    </root>

</configuration>

工作原理

自动采集 span 数据

​ SkyWalking 采用了插件化 + javaagent 的形式来实现了 span 数据的自动采集,这样可以做到对代码的 无侵入性,插件化意味着可插拔,扩展性好。

跨进程传递 context

​ 我们知道网络传输数据一般分为 header 和 body, 就像 http 有 header 和 body, RocketMQ 也有 MessageHeader,Message Body, body 一般放着业务数据,所以不宜在 body 中传递 context,应该在 header 中传递 context

​ dubbo 中的 attachment 就相当于 header ,所以我们把 context 放在 attachment 中,这样就解决了 context 的传递问题。

traceId 保证全局唯一

​ 要保证全局唯一 ,我们可以采用分布式或者本地生成的 ID,使用分布式话需要有一个发号器,每次请求都要先请求一下发号器,会有一次网络调用的开销,所以 SkyWalking 最终采用了本地生成 ID 的方式,它采用了大名鼎鼎的 snowflow 算法,性能很高。

​ 不过 snowflake 算法有一个众所周知的问题:时间回拨,这个问题可能会导致生成的 id 重复。那么 SkyWalking 是如何解决时间回拨问题的呢。

​ 每生成一个 id,都会记录一下生成 id 的时间(lastTimestamp),如果发现当前时间比上一次生成 id 的时间(lastTimestamp)还小,那说明发生了时间回拨,此时会生成一个随机数来作为 traceId。这里可能就有同学要较真了,可能会觉得生成的这个随机数也会和已生成的全局 id 重复,是否再加一层校验会好点。

这里要说一下系统设计上的方案取舍问题了,首先如果针对产生的这个随机数作唯一性校验无疑会多一层调用,会有一定的性能损耗,但其实时间回拨发生的概率很小(发生之后由于机器时间紊乱,业务会受到很大影响,所以机器时间的调整必然要慎之又慎),再加上生成的随机数重合的概率也很小,综合考虑这里确实没有必要再加一层全局唯一性校验。对于技术方案的选型,一定要避免过度设计,过犹不及。

采样机制

​ 如果对每个请求调用都采集,那毫无疑问数据量会非常大,但反过来想一下,是否真的有必要对每个请求都采集呢,其实没有必要,我们可以设置采样频率,只采样部分数据,SkyWalking 默认设置了 3 秒采样 3 次,其余请求不采样。

这样的采样频率其实足够我们分析组件的性能了,按 3 秒采样 3 次这样的频率来采样数据会有啥问题呢。理想情况下,每个服务调用都在同一个时间点(如下图示)这样的话每次都在同一时间点采样确实没问题。但在生产上,每次服务调用基本不可能都在同一时间点调用,因为期间有网络调用延时等,实际调用情况很可能是下图这样。这样的话就会导致某些调用在服务 A 上被采样了,在服务 B,C 上不被采样,也就没法分析调用链的性能,那么 SkyWalking 是如何解决的呢。

它是这样解决的:如果上游有携带 Context 过来(说明上游采样了),则下游强制采集数据。这样可以保证链路完整。

skywalking优势:

  • 性能卓越
  • 对多语言支持;
  • 插件可扩展;

服务端配置

关于服务端数据清理的相关配置:

core:
  selector: ${SW_CORE:default}
  default:
    enableDataKeeperExecutor: ${SW_CORE_ENABLE_DATA_KEEPER_EXECUTOR:true} # 开启数据自动清理机制
    dataKeeperExecutePeriod: ${SW_CORE_DATA_KEEPER_EXECUTE_PERIOD:5} # 清理周期,单位:分钟
    recordDataTTL: ${SW_CORE_RECORD_DATA_TTL:3}  # 明细记录有效期,单位:天
    metricsDataTTL: ${SW_CORE_METRICS_DATA_TTL:7} # 度量数据有效期,单位:天

以上是skywalking 10.x 版本的默认配置,不同版本可能有所不同。

Agent客户端配置

Agent客户端有两种方式修改配置:

  • 在skywalking-agent/config 目录的agent.conf文件中修改;
  • 在应用添加启动参数(如JVM)

进入skywalking-agent/config 目录,编辑所有的agent_xxx_xxx.conf文件,找到如下参数,将他们全部修改为下面指定的值:

# 指定 SkyWalking OAP 服务器的地址,用于上报数据
collector.backend_service=${SW_AGENT_COLLECTOR_BACKEND_SERVICES:10.2.113.58:11800}
# 客户端应用的服务名称
agent.service_name=${SW_AGENT_NAME:Your_ApplicationName}
# 每3秒钟采集的segment数量,默认为-1,表示全部采样,这里配置为500,如果存储量还是很大,后续可以设置更小
agent.sample_n_per_3_secs=${SW_AGENT_SAMPLE:500}

设置完成后,需要重启每一个java进程

第二种方式:在应用添加启动参数(如JVM)

-javaagent:D:\APM\skywalking-agent\skywalking-agent.jar
-Dskywalking.agent.service_name=group1::ruoyi-service
-Dskywalking.collector.backend_service=10.2.113.58:11800
-Dskywalking.agent.sample_n_per_3_secs=300

客户端采样率优化

SkyWalking Agent的采集频率设置需根据具体场景和性能需求进行动态调整,以下为推荐配置及实践建议:

  1. 默认配置与生产环境推荐
  • 默认全量采集:默认参数为 agent.sample_n_per_3_secs=-1 ,即全量采集所有TraceSegment,适用于开发或测试环境。
  • 生产环境调优:
    • 低负载场景:每3秒采集10-50个TraceSegment(如 agent.sample_n_per_3_secs=10 ),满足基本监控需求且性能损耗可控。
    • 高并发场景:建议进一步降低采样率(如每3秒5次),以减轻Agent对业务性能的影响。例如某银行系统在性能测试中,通过降低采样率将性能损耗从15%降至3%以内。
  1. 动态调整与自适应策略
  • 动态采样:结合业务高峰期和低谷期,通过环境变量(如 SW_AGENT_SAMPLE=5 )动态调整采样率。
  • 自适应算法:在微服务架构中,可基于QPS(每秒请求量)自动调整采样率。例如,当QPS超过1000时,采样率可降至每3秒10次,避免数据洪峰冲击后端存储。
  1. 性能与数据完整性的平衡
  • 性能影响:采样率越高,Agent的CPU和内存消耗越大。例如,全量采集可能导致应用吞吐量下降10%-20%,而低采样率(如5次/3秒)可将损耗控制在5%以内。
  • 数据价值:优先采集关键链路(如核心服务、跨微服务调用),通过 agent.ignore_suffix 忽略静态资源等非关键路径(如 js、css),提升数据价值密度。
  1. 测试环境与生产环境差异
  • 测试环境:建议全量采集或高采样率(如每3秒500-1500次),用于压测和瓶颈分析
  • 生产环境:结合存储容量(如Elasticsearch集群规模)调整。例如,每3秒10次采样时,单日存储量约为10GB,需确保存储引擎的写入性能和扩容能力

总结建议

  • 通用配置:生产环境初始可设置为agent.sample_n_per_3_secs=10,并根据监控数据逐步优化。
  • 性能验证:通过AB测试对比启用Agent前后的性能指标(如TPS、响应时间),确保采样率与业务承载能力匹配
  • 告警联动:当采样率调整导致数据覆盖率下降时,需同步优化告警规则,避免漏报关键故障

​ 具体参数需结合系统实际负载、存储能力和运维目标综合评估。例如,某金融系统通过上述策略将日均采集量从百万级降至十万级,同时保证95%以上的关键链路覆盖。

OpenTelemetry

OpenTelemetry 也被称为 OTel,是一个供应商中立的、开源的可观测性框架, 可用于插桩、生成、采集和导出链路、 指标和日志等遥测数据。

Java agent

opentelemetry-java-instrumentation

主要有以下三个

  • Trace:一个完整请求链路
  • Span:一次调用过程(需要有开始时间和结束时间)
  • SpanContext:Trace 的全局上下文信息, 如里面有traceId

最佳实践

QQ音乐基于 Jaeger 构建分布式链路追踪系统

度量系统

Prometheus 是开源的应用监控解决方案。 (云原生架构)

Zabbix

Prometheus

springboot应用添加依赖:

<dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-actuator</artifactId>
      </dependency>
      <dependency>
          <groupId>io.micrometer</groupId>
          <artifactId>micrometer-core</artifactId>
      </dependency>
      <dependency>
          <groupId>io.micrometer</groupId>
          <artifactId>micrometer-registry-prometheus</artifactId>
      </dependency>

配置参数:

spring:
  application:
    name: ExampleApplication
management:
  server:
    port: 8089
  endpoints:
    web:
      exposure:
        include: prometheus
  metrics:
    tags:
      application: ${spring.application.name}

启动Java应用,访问 http://127.0.0.1:8089/actuator/prometheus 进行验证。

下载Prometheus,并修改配置文件 prometheus.yml:

scrape_configs:
  - job_name: "prometheus"
    metrics_path: '/actuator/prometheus'
    static_configs:
      - targets: ["localhost:8089"]

启动 Prometheus,访问 http://127.0.0.1:9090/targets

exporter

若需要监控服务器资源、数据库、kafka等需要下载对应的exporter

linux常用的有node_exporter:监控服务器资源
mysqld_exporter:监控数据库
下载后放到需要监控的机器,直接运行,然后修改 prometheus的配置文件prometheus.yml,添加exporter对应的ip以及端口,node_exporter默认端口是9100,mysqld_exporter默认端口9104

  • memcached_exporter
  • mysqld_exporter
  • node_exporter
  • consul_exporter
  • graphite_exporter

Grafana

​ Grafana是一个开源的数据可视化和监控分析平台。它提供了强大的数据查询、可视化和仪表盘功能,可以帮助用户通过图表、指标和警报来监控和分析各种数据源。

​ Grafana支持多种数据源,包括时序数据库(如Prometheus、InfluxDB、Graphite等)、关系型数据库、日志文件、云服务等。用户可以通过配置数据源来连接到不同的数据存储系统,并使用Grafana的查询语言和查询编辑器来从这些数据源中提取数据。

README

作者:银法王

参考:

常见APM技术选型

优雅应对故障:QQ音乐怎么做高可用架构体系?

SkyWalking 官网中文

SkyWalking 极简入门

修改记录:

​ 2022-10-22 第一次修订


分布式-构建可观测性系统
http://jackpot-lang.online/2022/10/08/系统技术架构设计/分布式-构建可观测性系统/
作者
Jackpot
发布于
2022年10月8日
许可协议