JavaEE鸿蒙应用开发HTML&JS+前端Python+大数据开发人工智能开发电商视觉设计软件测试新媒体+短视频直播运营产品经理集成电路应用开发(含嵌入式)Linux云计算+运维开发C/C++拍摄剪辑+短视频制作PMP项目管理认证电商运营Go语言与区块链大数据PHP工程师Android+物联网iOS.NET

还在用Zipkin分布式服务链路追踪?来试试这个吧!

来源:黑马程序员

浏览7638人

2020.07.22

自SpringCloud问世以来,微服务以席卷之势风靡全球,企业架构都在从传统SOA向微服务转型。然而微服务这把双刃剑在带来各种优势的同时,也给运维、性能监控、错误的排查带来的极大的困难。

在大型项目中,服务架构会包含数十乃至上百个服务节点。往往一次请求会设计到多个微服务,想要排查一次请求链路中经过了哪些节点,每个节点的执行情况如何,就称为了亟待解决的问题。于是分布式系统的APM管理系统应运而生。

什么是APM系统?

APM系统可以帮助理解系统行为、用于分析性能问题的工具,以便发生故障的时候,能够快速定位和解决问题,这就是APM系统,全称是(**A**pplication **P**erformance **M**onitor)。

谷歌公开的论文提到的 [Google Dapper](http://bigbully.github.io/Dapper-translation)可以说是最早的APM系统了,给google的开发者和运维团队帮了大忙,所以谷歌公开论文分享了Dapper。

而后,很多的技术公司基于这篇论文的原理,设计开发了很多出色的APM框架,例如`Pinpoint`、`SkyWalking`等。

而SpringCloud官网也集成了一套这样的系统:`Spring Cloud Sleuth`,结合`Zipkin`。

APM的基本原理

目前大部分的APM系统都是基于Google的Dapper原理实现,我们简单来看看Dapper中的概念和实现原理。

先来看一次请求调用示例:

1. 服务集群中包括:前端(A),两个中间层(B和C),以及两个后端(D和E)

2. 当用户发起一个请求时,首先到达前端A服务,然后A分别对B服务和C服务进行RPC调用;

3. B服务处理完给A做出响应,但是C服务还需要和后端的D服务和E服务交互之后再返还给A服务,最后由A服务来响应用户的请求;

1595405395672965.png

如何才能实现跟踪呢?

 Google的Dapper设计了下面的几个概念用来记录请求链路:

 - Span:请求中的基本工作单元,每一次链路调用(RPC、Rest、数据库调用)都会创建一个Span。大概结构如下:

 c++

  type Span struct {

      TraceID    int64 // 用于标示一次完整的请求id

      Name       string // 单元名称

      ID         int64 // 当前这次调用span_id

      ParentID   int64 // 上层服务的span_id,最上层服务parent_id为null,代表根服务

      Annotation []Annotation // 注释,用于记录调用中的详细信息,例如时间

  }

 - Trace:一次完整的调用链路,包含多个Span的树状结构,具有唯一的TraceID

 一次请求的每个链路,通过spanId、parentId就能串联起来:

1595405442111675.png

当然,从请求到服务器开始,服务器返回response结束,每个span存在相同的唯一标识trace_id。

APM的筛选标准

目前主流的APM框架都会包含下列几个组件来完成链路信息的收集和展示:

 - 探针(Agent):负责在客户端程序运行时搜索服务调用链路信息,发送给收集器

- 收集器(Collector):负责将数据格式化,保存到存储器

- 存储器(Storage):保存数据

- UI界面(WebUI):统计并展示收集到的信息

因此,要筛选一款合格的APM框架,就是对比各个组件的使用差异,主要对比项:

**探针的性能**

主要是agent对服务的吞吐量、CPU和内存的影响。如果探针在收集微服务运行数据时,对微服务的运行产生了比较大的性能影响,相信没什么人愿意使用。

 **collector的可扩展性**

 能够水平扩展以便支持大规模服务器集群,保证收集器的高可用特性。

 - **全面的调用链路数据分析**

 数据的分析要**快** ,分析的维度尽可能**多**。跟踪系统能提供足够快的信息反馈,就可以对生产环境下的异常状况做出快速反应,最好提供代码级别的可见性以便轻松定位失败点和瓶颈。

 - **对于开发透明,容易开关**

 即也作为业务组件,应当尽可能少入侵或者无入侵其他业务系统,对于使用方透明,减少开发人员的负担。

 - **完整的调用链应用拓扑**

 自动检测应用拓扑,帮助你搞清楚应用的架构

 接下来,我们就对比下目前比较常见的三种APM框架的各项指标,分别是:

 - **[Zipkin](https://link.juejin.im/?target=http%3A%2F%2Fzipkin.io%2F)**:由Twitter公司开源,开放源代码分布式的跟踪系统,用于收集服务的定时数据,以解决微服务架构中的延迟问题,包括:数据的收集、存储、查找和展现。

- **[Pinpoint](https://pinpoint.com/)**:一款对Java编写的大规模分布式系统的APM工具,由韩国人开源的分布式跟踪组件。

- **[Skywalking](https://skywalking.apache.org/zh/)**:国产的优秀APM组件,是一个对JAVA分布式应用程序集群的业务运行情况进行追踪、告警和分析的系统。现在是Apache的顶级项目之一

 三者对比如下:


| 对比项           | zipkin | pinpoint | skywalking |

| ---------------- | ------ | -------- | ---------- |

| 探针性能         | 中     | 低       | **高**     |

| collector扩展性  | **高** | 中       | **高**     |

| 调用链路数据分析 | 低     | **高**   | 中         |

| 对开发透明性     | 中     | **高**   | **高**     |

| 调用链应用拓扑   | 中     | **高**   | 中         |

| 社区支持         | **高** | 中       | **高**     |

 

可见,zipkin的探针性能、开发透明性、数据分析能力都不占优,实在是下下之选。

而pinpoint在数据分析能力、开发透明性上有较大的优势,不过Pinpoint的部署相对比较复杂,需要的硬件资源较高。

 Skywalking的探针性能和开发透明性上具有较大优势,数据分析能力上也还不错,重要的是其部署比较方便灵活,比起Pinpoint更适合中小型企业使用。

 因此,本文会带着大家学习Skywalking的使用。

Skywalking介绍

SkyWalking** 创建与2015年,提供分布式追踪功能。从5.x开始,项目进化为一个完成功能的Application Performance Management系统。

他被用于追踪、监控和诊断分布式系统,特别是使用微服务架构,云原生或容积技术。提供以下主要功能:

 - 分布式追踪和上下文传输

- 应用、实例、服务性能指标分析

- 根源分析

- 应用拓扑分析

- 应用和服务依赖分析

- 慢服务检测

- 性能优化

 官网地址:http://skywalking.apache.org/

1595405581809245.png

主要的特征:

 - 多语言探针或类库

 - Java自动探针,追踪和监控程序时,不需要修改源码。

  - 社区提供的其他多语言探针

    - [.NET Core](https://github.com/OpenSkywalking/skywalking-netcore)

    - [Node.js](https://github.com/OpenSkywalking/skywalking-nodejs)

 - 多种后端存储: ElasticSearch, H2

 - 支持

 OpenTracing

 - Java自动探针支持和OpenTracing API协同工作

 - 轻量级、完善功能的后端聚合和分析

 - 现代化Web UI

 - 日志集成

 - 应用、实例和服务的告警

Skywalking的安装

先来看下Skywalking的官方给出的结构图:

1595405646634033.png


 大致分四个部分:

- skywalking-oap-server:就是**O**bservability **A**nalysis **P**latformd的服务,用来收集和处理探针发来的数据

- skywalking-UI:就是skywalking提供的Web UI 服务,图形化方式展示服务链路、拓扑图、trace、性能监控等

- agent:探针,获取服务调用的链路信息、性能信息,发送到skywalking的OAP服务

- Storage:存储,一般选择elasticsearch

Skywalking支持windows或者Linux环境部署。这里我们选择在Linux下安装Skywalking,大家要**先确保自己的Linux环境中有elasticsearch在启动中**。

 接下来的安装分为三步:

- 下载安装包

- 安装Skywalking的OAP服务和WebUI

- 在服务中部署探针

 

下载安装包

 安装包可以再Skywalking的官网下载,http://skywalking.apache.org/downloads/

 目前最新版本是8.0.1版本:

1595405723162984.png

下载好的安装包:

1595405755642073.png

安装OAP服务和WebUI

安装

将下载好的安装包解压到Linux的某个目录下:

```

tar xvf apache-skywalking-apm-es7-8.0.1.tar.gz

```

然后对解压好的文件夹重命名: 

```sh

mv apache-skywalking-apm-es7 skywalking

```

进入解压好的目录:

```sh

cd skywalking

```

查看目录结构:

1595405809750379.png


几个关键的目录:

- agent:探针

- bin:启动脚本

- config:配置文件

- logs:日志

- oap-libs:依赖

- webapp:WebUI

这里要修改config目录中的application.yml文件,详细配置见官网:https://github.com/apache/skywalking/blob/v8.0.1/docs/en/setup/backend/backend-setup.md

配置

进入`config`目录,修改`application.yml`,主要是把存储方案从h2改为elasticsearch

可以直接使用下面的配置:

```yaml

cluster:

  selector: ${SW_CLUSTER:standalone}

  standalone:

core:

  selector: ${SW_CORE:default}

  default:

    role: ${SW_CORE_ROLE:Mixed} # Mixed/Receiver/Aggregator

    restHost: ${SW_CORE_REST_HOST:0.0.0.0}

    restPort: ${SW_CORE_REST_PORT:12800}

    restContextPath: ${SW_CORE_REST_CONTEXT_PATH:/}

    gRPCHost: ${SW_CORE_GRPC_HOST:0.0.0.0}

    gRPCPort: ${SW_CORE_GRPC_PORT:11800}

    gRPCSslEnabled: ${SW_CORE_GRPC_SSL_ENABLED:false}

    gRPCSslKeyPath: ${SW_CORE_GRPC_SSL_KEY_PATH:""}

    gRPCSslCertChainPath: ${SW_CORE_GRPC_SSL_CERT_CHAIN_PATH:""}

    gRPCSslTrustedCAPath: ${SW_CORE_GRPC_SSL_TRUSTED_CA_PATH:""}

    downsampling:

      - Hour

      - Day

      - Month

    # Set a timeout on metrics data. After the timeout has expired, the metrics data will automatically be deleted.

    enableDataKeeperExecutor: ${SW_CORE_ENABLE_DATA_KEEPER_EXECUTOR:true} # Turn it off then automatically metrics data delete will be close.

    dataKeeperExecutePeriod: ${SW_CORE_DATA_KEEPER_EXECUTE_PERIOD:5} # How often the data keeper executor runs periodically, unit is minute

    recordDataTTL: ${SW_CORE_RECORD_DATA_TTL:3} # Unit is day

    metricsDataTTL: ${SW_CORE_RECORD_DATA_TTL:7} # Unit is day

    # Cache metric data for 1 minute to reduce database queries, and if the OAP cluster changes within that minute,

    # the metrics may not be accurate within that minute.

    enableDatabaseSession: ${SW_CORE_ENABLE_DATABASE_SESSION:true}

    topNReportPeriod: ${SW_CORE_TOPN_REPORT_PERIOD:10} # top_n record worker report cycle, unit is minute

    # Extra model column are the column defined by in the codes, These columns of model are not required logically in aggregation or further query,

    # and it will cause more load for memory, network of OAP and storage.

    # But, being activated, user could see the name in the storage entities, which make users easier to use 3rd party tool, such as Kibana->ES, to query the data by themselves.

    activeExtraModelColumns: ${SW_CORE_ACTIVE_EXTRA_MODEL_COLUMNS:false}

    # The max length of service + instance names should be less than 200

    serviceNameMaxLength: ${SW_SERVICE_NAME_MAX_LENGTH:70}

    instanceNameMaxLength: ${SW_INSTANCE_NAME_MAX_LENGTH:70}

    # The max length of service + endpoint names should be less than 240

    endpointNameMaxLength: ${SW_ENDPOINT_NAME_MAX_LENGTH:150}

storage:

  selector: ${SW_STORAGE:elasticsearch7}

  elasticsearch7:

    nameSpace: ${SW_NAMESPACE:""}

    clusterNodes: ${SW_STORAGE_ES_CLUSTER_NODES:localhost:9200}

    protocol: ${SW_STORAGE_ES_HTTP_PROTOCOL:"http"}

    trustStorePath: ${SW_STORAGE_ES_SSL_JKS_PATH:""}

    trustStorePass: ${SW_STORAGE_ES_SSL_JKS_PASS:""}

    dayStep: ${SW_STORAGE_DAY_STEP:1} # Represent the number of days in the one minute/hour/day index.

    user: ${SW_ES_USER:""}

    password: ${SW_ES_PASSWORD:""}

    secretsManagementFile: ${SW_ES_SECRETS_MANAGEMENT_FILE:""} # Secrets management file in the properties format includes the username, password, which are managed by 3rd party tool.

    indexShardsNumber: ${SW_STORAGE_ES_INDEX_SHARDS_NUMBER:1} # The index shards number is for store metrics data rather than basic segment record

    superDatasetIndexShardsFactor: ${SW_STORAGE_ES_SUPER_DATASET_INDEX_SHARDS_FACTOR:5} # Super data set has been defined in the codes, such as trace segments. This factor provides more shards for the super data set, shards number = indexShardsNumber * superDatasetIndexShardsFactor. Also, this factor effects Zipkin and Jaeger traces.

    indexReplicasNumber: ${SW_STORAGE_ES_INDEX_REPLICAS_NUMBER:0}

    # Batch process setting, refer to https://www.elastic.co/guide/en/elasticsearch/client/java-api/5.5/java-docs-bulk-processor.html

    bulkActions: ${SW_STORAGE_ES_BULK_ACTIONS:1000} # Execute the bulk every 1000 requests

    flushInterval: ${SW_STORAGE_ES_FLUSH_INTERVAL:10} # flush the bulk every 10 seconds whatever the number of requests

    concurrentRequests: ${SW_STORAGE_ES_CONCURRENT_REQUESTS:2} # the number of concurrent requests

    resultWindowMaxSize: ${SW_STORAGE_ES_QUERY_MAX_WINDOW_SIZE:10000}

    metadataQueryMaxSize: ${SW_STORAGE_ES_QUERY_MAX_SIZE:5000}

    segmentQueryMaxSize: ${SW_STORAGE_ES_QUERY_SEGMENT_SIZE:200}

    profileTaskQueryMaxSize: ${SW_STORAGE_ES_QUERY_PROFILE_TASK_SIZE:200}

    advanced: ${SW_STORAGE_ES_ADVANCED:""}

  h2:

    driver: ${SW_STORAGE_H2_DRIVER:org.h2.jdbcx.JdbcDataSource}

    url: ${SW_STORAGE_H2_URL:jdbc:h2:mem:skywalking-oap-db}

    user: ${SW_STORAGE_H2_USER:sa}

    metadataQueryMaxSize: ${SW_STORAGE_H2_QUERY_MAX_SIZE:5000}

receiver-sharing-server:

  selector: ${SW_RECEIVER_SHARING_SERVER:default}

  default:

    authentication: ${SW_AUTHENTICATION:""}

receiver-register:

  selector: ${SW_RECEIVER_REGISTER:default}

  default:

 

receiver-trace:

  selector: ${SW_RECEIVER_TRACE:default}

  default:

    sampleRate: ${SW_TRACE_SAMPLE_RATE:10000} # The sample rate precision is 1/10000. 10000 means 100% sample in default.

    slowDBAccessThreshold: ${SW_SLOW_DB_THRESHOLD:default:200,mongodb:100} # The slow database access thresholds. Unit ms.

 

receiver-jvm:

  selector: ${SW_RECEIVER_JVM:default}

  default:

 

receiver-clr:

  selector: ${SW_RECEIVER_CLR:default}

  default:

 

receiver-profile:

  selector: ${SW_RECEIVER_PROFILE:default}

  default:

 

service-mesh:

  selector: ${SW_SERVICE_MESH:default}

  default:

 

istio-telemetry:

  selector: ${SW_ISTIO_TELEMETRY:default}

  default:

 

envoy-metric:

  selector: ${SW_ENVOY_METRIC:default}

  default:

    acceptMetricsService: ${SW_ENVOY_METRIC_SERVICE:true}

    alsHTTPAnalysis: ${SW_ENVOY_METRIC_ALS_HTTP_ANALYSIS:""}

 

prometheus-fetcher:

  selector: ${SW_PROMETHEUS_FETCHER:default}

  default:

    active: ${SW_PROMETHEUS_FETCHER_ACTIVE:false}

 

receiver_zipkin:

  selector: ${SW_RECEIVER_ZIPKIN:-}

  default:

    host: ${SW_RECEIVER_ZIPKIN_HOST:0.0.0.0}

    port: ${SW_RECEIVER_ZIPKIN_PORT:9411}

    contextPath: ${SW_RECEIVER_ZIPKIN_CONTEXT_PATH:/}

 

receiver_jaeger:

  selector: ${SW_RECEIVER_JAEGER:-}

  default:

    gRPCHost: ${SW_RECEIVER_JAEGER_HOST:0.0.0.0}

    gRPCPort: ${SW_RECEIVER_JAEGER_PORT:14250}

 

query:

  selector: ${SW_QUERY:graphql}

  graphql:

    path: ${SW_QUERY_GRAPHQL_PATH:/graphql}

 

alarm:

  selector: ${SW_ALARM:default}

  default:

 

telemetry:

  selector: ${SW_TELEMETRY:none}

  none:

  prometheus:

    host: ${SW_TELEMETRY_PROMETHEUS_HOST:0.0.0.0}

    port: ${SW_TELEMETRY_PROMETHEUS_PORT:1234}

 

configuration:

  selector: ${SW_CONFIGURATION:none}

  none:

  grpc:

    host: ${SW_DCS_SERVER_HOST:""}

    port: ${SW_DCS_SERVER_PORT:80}

    clusterName: ${SW_DCS_CLUSTER_NAME:SkyWalking}

    period: ${SW_DCS_PERIOD:20}

  apollo:

    apolloMeta: ${SW_CONFIG_APOLLO:http://106.12.25.204:8080}

    apolloCluster: ${SW_CONFIG_APOLLO_CLUSTER:default}

    apolloEnv: ${SW_CONFIG_APOLLO_ENV:""}

    appId: ${SW_CONFIG_APOLLO_APP_ID:skywalking}

    period: ${SW_CONFIG_APOLLO_PERIOD:5}

  zookeeper:

    period: ${SW_CONFIG_ZK_PERIOD:60} # Unit seconds, sync period. Default fetch every 60 seconds.

    nameSpace: ${SW_CONFIG_ZK_NAMESPACE:/default}

    hostPort: ${SW_CONFIG_ZK_HOST_PORT:localhost:2181}

    # Retry Policy

    baseSleepTimeMs: ${SW_CONFIG_ZK_BASE_SLEEP_TIME_MS:1000} # initial amount of time to wait between retries

    maxRetries: ${SW_CONFIG_ZK_MAX_RETRIES:3} # max number of times to retry

  etcd:

    period: ${SW_CONFIG_ETCD_PERIOD:60} # Unit seconds, sync period. Default fetch every 60 seconds.

    group: ${SW_CONFIG_ETCD_GROUP:skywalking}

    serverAddr: ${SW_CONFIG_ETCD_SERVER_ADDR:localhost:2379}

    clusterName: ${SW_CONFIG_ETCD_CLUSTER_NAME:default}

  consul:

    # Consul host and ports, separated by comma, e.g. 1.2.3.4:8500,2.3.4.5:8500

    hostAndPorts: ${SW_CONFIG_CONSUL_HOST_AND_PORTS:1.2.3.4:8500}

    # Sync period in seconds. Defaults to 60 seconds.

    period: ${SW_CONFIG_CONSUL_PERIOD:1}

    # Consul aclToken

    aclToken: ${SW_CONFIG_CONSUL_ACL_TOKEN:""}

 

exporter:

  selector: ${SW_EXPORTER:-}

  grpc:

    targetHost: ${SW_EXPORTER_GRPC_HOST:127.0.0.1}

    targetPort: ${SW_EXPORTER_GRPC_PORT:9870}

 

```

启动

要确保已经启动了elasticsearch,并且防火墙开放8080、11800、12800端口。

进入`bin`目录,执行命令即可运行:

```sh

./startup.sh

```

默认的UI端口是8080,可以访问:http://192.168.150.101:8080

1595405891941593.png


部署微服务探针

现在,Skywalking的服务端已经启动完成,我们还需要在微服务中加入服务探针,来收集数据。

解压

首先,将课前资料给的压缩包解压:

1595405942751677.png

将其中的`agent`解压到某个目录,不要出现中文,可以看到其结构如下:

1595405970940470.png

其中有一个`skywalking-agent.jar`就是一我们要用的探针。

配置

如果是运行一个jar包,可以在运行时输入参数来指定探针:

ava -jar xxx.jar -javaagent:C:/lesson/skywalking-agent/skywalking-agent.jar -Dskywalking.agent.service_name=ly-registry -Dskywalking.collector.backend_service=192.168.150.101:11800

```

本例中,我们用开发工具来运行和配置。

使用IDEA开发工具打开一个你的项目,在IDEA工具中,选择要修改的启动项,点击右键,选择`Edit Configuration`:

1595406023836631.png

然后在弹出的窗口中,点击`Environment`,选择`VM options`后面对应的展开按钮:

1595406055494117.png

在展开的输入框中,输入下面的配置:

```powershell

-javaagent:C:/lesson/skywalking-agent/skywalking-agent.jar

-Dskywalking.agent.service_name=ly-registry

-Dskywalking.collector.backend_service=192.168.150.101:11800

```

注意:

- `-javaagent:C:/lesson/skywalking-agent/skywalking-agent.jar`:配置的是skywalking-agent.jar这个包的位置,要修改成你自己存放的目录

- `-Dskywalking.agent.service_name=ly-registry`:是当前项目的名称,需要分别修改为`ly-registry`、`ly-gateway`、`ly-item-service`

- `-Dskywalking.collector.backend_service=192.168.150.101:11800`:是Skywalking的OPA服务地址,采用的是GRPC通信,因此端口是11800,不是8080

启动

Skywalking的探针会在项目启动前对class文件进行修改,完成探针植入,对业务代码**零侵入**,所以我们只需要启动项目,即可生效了。

启动项目,然后对项目中的的业务接口访问,探针就开始工作了。

WebUI界面

访问:http://192.168.150.101:8080可以看到统计数据已经出来了:

1595406127368379.png

服务实例的性能监控:

1595406156979163.png

服务拓扑图:

1595406186634696.png

1595406211767997.png

某次请求的链路追踪信息:

1595406240967874.png

表格视图:

1595406273818577.png