微服務鏈路追蹤元件 SkyWalking

HuDu發表於2022-08-18

前言

對於一個大型的幾十個、幾百個微服務構成的微服務架構系統,通常會有以下問題,比如

  1. 如何串聯整個呼叫鏈路,快速定位問題
  2. 如何捋清各個微服務的依賴關係
  3. 如何進行各個微服務介面的效能分析
  4. 如何跟蹤整個業務流程的呼叫處理順序

一、什麼是 SkyWalking

skywalking 是一個國產開源框架,2015年開源,2017年加入 Apache 孵化器。skywalking 是分散式系統的應用程式效能監視工具,轉為微服務、雲原生架構和基於容器(Docker、k8s、Mesos)架構而設計的。它是一款優秀的 APM(Application Performance Management)工具,包括了分散式追蹤、效能指標分析、應用和服務依賴分析等

中文文件地址

中文文件地址

1.1、鏈路追蹤框架對比

  1. Zipkin 是 Twitter 開源的呼叫鏈分析工具,目前基於springcloud sleuth 得到了廣泛的使用,特點是輕量,使用部署簡單。
  2. Pinpoint 是韓國人開源的基於位元組碼注入的呼叫鏈分析,以及應用監控分析工具。特點是支援多種外掛, UI 功能強大,接入端無程式碼侵入。
  3. SkyWalking 是本土開源的基於位元組碼注入的呼叫鏈分析,以及應用監控分析工具。特點是支援多種外掛, UI 功能較強,接入端無程式碼侵入。目前已加入 Apache 孵化器。
  4. CAT 是大眾點評開源的基於編碼和配置的呼叫鏈分析,應用監控分析,日誌採集,監控報警等一系列的監控平臺工具。
專案 Cat Zipkin Skywalking
呼叫鏈視覺化
聚合報告 非常豐富 較豐富
服務依賴圖 簡單 簡單
埋點方式 侵入式 侵入式 非侵入,位元組碼增強
VM監控指標
支援語言 java/.net 豐富 java/.net/Nodejs/php/go
儲存機制 mysql(報表)、本地檔案/HDFS(呼叫鏈) 記憶體、es、mysql等 H2、es等
社群支援 主要在國內 國外主流 Apache 支援
使用案例 美團、攜程、陸金所 京東、阿里定之後不開源 華為、小米、噹噹、微眾銀行
APM
開發基礎 eBay cal Google Dapper Google Dapper
是否支援 webflux
Github stars(2022.8) 17.1k 15.6k 20k

在三種鏈路監控元件中, skywalking的探針對吞吐量的影響最小。

1.2、Skywalking 主要特性

1、多種監控手段,可以透過語言探針和 service mesh 獲得監控的資料。
2、支援多種語言自動探針(agent),包括Java,.NET Core 和 Node.JS。
3、輕量高效,無需大資料平臺和大量的伺服器資源。
4、模組化,UI、儲存、叢集管理都有多種機制可選。
5、支援告警。
6、優秀的視覺化解決方案。

二、SkyWalking 環境搭建部署

微服務鏈路追蹤元件 SkyWalking

  • skywalking agent和業務系統繫結在一起,負責收集各種監控資料
  • Skywalking oapservice 是負責處理監控資料的,比如接受 skywalking agent 的監控資料,並儲存在資料庫中;接受 skywalking webapp 的前端請求,從資料庫查詢資料,並返回資料給前端。Skywalking oapservice 通常以叢集的形式存在。
  • skywalking webapp,前端介面,用於展示資料。
  • 用於儲存監控資料的資料庫,比如mysql、elasticsearch 等。

2.1、下載 SkyWalking

下載地址

OAP Server 下載

微服務鏈路追蹤元件 SkyWalking

Java-agent 接入

微服務鏈路追蹤元件 SkyWalking

OAP 目錄結構

  • webapp:UI 前端(web監控頁面)的 jar 包和配置檔案
  • oap-libs:後臺應用的 jar 包,以及它依賴的 jar 包,裡面有一個 server-starter.jar 啟動程式
  • config:啟動後臺應用程式的配置檔案,是使用的各種配置
  • bin:各種指令碼,一般使用指令碼 startup.sh 來啟動 web 頁面和對應的後臺應用:
    • oapService.*:預設使用後臺程式的啟動指令碼;(使用的是預設模式啟動,還支援其他模式)
    • oapServiceInit.*:使用 init 模式啟動;在此模式下,OAP 伺服器啟動以執行初始化工作,用於叢集環境。為了防止多節點同時啟動導致衝突,單節點執行oapServiceInit.sh進行初始化,其他節點執行oapServiceNoInit.sh 等待初始化完成後再啟動。
    • oapServiceNoInit.*:使用 no init 模式啟動;在此模式下,OAP 伺服器不進行初始化
    • webappService.*:UI 前端的啟動指令碼
    • startup.*:同時啟動oapService.*webappService.*指令碼;

Java Agents 目錄結構

  • activations:工具包
  • bootstrap-plugins:啟動外掛,預設載入
  • config:配置檔案
  • optional-plugins:可選擴充外掛,啟動不載入,如需載入將其移入 plugins 目錄下
  • optional-reporter-plugins:可選統計類外掛,啟動不載入
  • plugins:服務類外掛
  • skywalking-agent.jar:客戶端主程式,需要被服務啟動時引用

2.2、搭建 SkyWalking OAP 服務

為了方式埠衝突,將前端頁面地址進行修改

vim webapp/webapp.yml

server:
  port: 8868

微服務鏈路追蹤元件 SkyWalking

SkyWalking UI 介面是透過請求 SkyWalking OAP 服務來獲得的

啟動指令碼bin/startup.sh

$ bash startup.sh 
SkyWalking OAP started successfully!
SkyWalking Web Application started successfully!

日誌資訊儲存在 logs 目錄

logs/
├── oap.log
├── skywalking-oap-server.log
└── webapp-console.log

啟動成功之後會啟動兩個服務,一個是 skywalking-oap-server,一個是 skywalking-web-ui
skywalking-oap-server 服務啟動後會暴露 11800 和 12800 兩個埠,分別為手機監控資料的埠 11800 和接受前端請求的埠 12800,修改埠可以修改config/application.yml

2.3、SkyWalking 中三個概念

  • 服務(Service):表示對請求提供相同行為的一系列或一組工作負載,在使用 Agent 時,可以定義服務的名字;
  • 服務例項(Service Instance):上述的一組工作負載中的每一個工作負載稱為一 個例項,一個服務例項實際就是作業系統上的一個真實程式;
  • 端點(Endpoint):對於特定服務所接收的請求路徑,如 HTTP 的 URI 路徑和 gRPC 服務的類名+方法簽名;

三、SkyWalking 接入微服務

3.1、linux 透過 jar 包方式接入

準備一個 springboot 程式,打包成可執行 jar 包,寫一個 shell 指令碼,在啟動專案的 shell 指令碼上,透過 -javaagent 引數進行配置 skywalking agent 來跟蹤微服務;

startup.sh 指令碼

#! /bin/bash
# SkyWalking Agent 配置
# Agent 名字,一般使用`spring.application.name`
export SW_AGENT_NAME=springboot-skywalking-demo
# 配置 Collector 地址
export SW_AGENT_COLLECTOR_BACKEND_SERVICERS=127.0.0.1:11800
export JAVA_AGENT=-javaagent:skywalking-agent.java 地址
# jar 啟動
java $JAVA_AGENT -jar spring-boot-skywalking-demo.jar

等同於

java -javaagent:skywalking-agent.jar 地址 -DSW_AGENT_COLLECTOR_BACKEND_SERVICERS=127.0.0.1:11800 -DSW_AGENT_NAME=springboot-skywalking-demo -jar spring-boot-skywalking-demo.jar

引數名對應 agent/config/agent.config 配置檔案中的屬性
屬性對應原始碼:org.apache.skywalking.apm.agent.core.conf.Config.java

# The service name in UI
agent.service_name=${SW_AGENT_NAME:Your_ApplicationName}
# Backend service addresses
collector.backend_sercvice=${SW_AGENT_COLLECTOR_BACKEND_SERVICES:127.0.0.1:11800}

3.2、Windows 環境 - IDEA 中使用 SkyWalking

在執行程式中配置 jvm 引數

# 指定 agent.jar 所在位置
-javaagent:/Users/hudu/Environment/skywalking/skywalking-agent/skywalking-agent.jar
# 指定服務名字
-DSW_AGENT_NAME=服務名稱
# 指定 skywalking 的 collector 服務的 IP 及埠
-DSW_AGENT_COLLECTOR_BACKEND_SERVICES=127.0.0.1:11800

-javaagent 必須繫結本機物理路徑的 skywalking-agent.jar
具體效果參考下面的接入多個微服務

3.2、Skywalking 跨多個微服務跟蹤

Skywalking 跨多個微服務跟蹤,只需要每個微服務啟動時新增 javaagent 引數即可。

-javaagent:/Users/hudu/Environment/skywalking/skywalking-agent/skywalking-agent.jar
-DSW_AGENT_NAME=服務名稱
-DSW_AGENT_COLLECTOR_BACKEND_SERVICES=127.0.0.1:11800

測試:
啟動微服務,接入 seata 的 order 和 stock 服務

微服務鏈路追蹤元件 SkyWalking

微服務鏈路追蹤元件 SkyWalking

請求一個介面進行測試http://localhost:8088/order/add,可以看到 skywalking 控制檯中已經有服務記錄,呼叫鏈路圖如下所示。

微服務鏈路追蹤元件 SkyWalking

四、Skywalking 持久化跟蹤資料

預設使用的是 H2 記憶體資料庫
config/application.yml

storage:
  selector: ${SW_STORAGE:mysql}
  mysql:
    properties:
    jdbcUrl: ${SW_JDBC_URL:"jdbc:mysql://localhost:3306/swtest?rewriteBatchedStatements=true"}
    dataSource.user: ${SW_DATA_SOURCE_USER:root}
    dataSource.password: ${SW_DATA_SOURCE_PASSWORD:root@1234}

建立 swtest 資料庫
啟動 skywalking 服務的時候會自動建立需要的表

oap-lib目錄中新增 mysql-connector-java.jar 包

五、自定義 SkyWalking 鏈路追蹤

如果我們希望對專案中的業務方法實現鏈路追蹤,方便我們排查問題,可以使用如下的程式碼

5.1、引入依賴

<!--SkyWalking 工具類-->
<dependency>
  <groupId>org.apache.skywalking</groupId>
 <artifactId>apm-toolkit-trace</artifactId>
 <version>8.11.0</version>
</dependency>

5.2、@Trace 將方法加入追蹤鏈路

如果一個業務方法想再 ui 介面的跟蹤鏈路上顯示出來,只需要再業務方法上加上@Trace註解即可

微服務鏈路追蹤元件 SkyWalking

5.3、加入 @Tags 或 @Tag

我們還可以為追蹤鏈路增加其他額外的資訊,比如記錄引數和返回資訊。實現方式:在方法上增加@Tag或者@Tags。

@Tag 註解中,key=方法名,value=returnedObj 返回值,arg[0] 引數

@GetMapping("/get/{id}")
@Trace
@Tags({@Tag(key="result",value = "returnedObj"),
  @Tag(key="param",value = "arg[0]")})
public Order get(@PathVariable Integer id) {
  return orderService.get(id);
}

可以看到方法引數和返回值都追蹤到了。

微服務鏈路追蹤元件 SkyWalking

微服務鏈路追蹤元件 SkyWalking

六、效能分析

skywalking 的效能分析,在根據服務名稱、端點名稱,以及相應的規則建立了任務列表後,在呼叫了此任務列表的端點後。skywalking 會自動記錄,剖析當前埠,生成剖析結果,具體流程如圖:

微服務鏈路追蹤元件 SkyWalking

微服務鏈路追蹤元件 SkyWalking
然後請求服務至少三次,由於設定了取樣次數

微服務鏈路追蹤元件 SkyWalking

七、整合日誌框架

官方配置地址

這裡以 logback 為例

7.1、引入依賴

<!--配置-->
<dependency>
  <groupId>org.apache.skywalking</groupId>
 <artifactId>apm-toolkit-logback-1.x</artifactId>
 <version>8.11.0</version>
</dependency>

logback-spring.xml 配置

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <include resource="org/springframework/boot/logging/logback/defaults.xml"/>

    <!-- 控制檯輸出 -->
    <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.TraceIdPatternLogbackLayout">
                <Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
            </layout>
        </encoder>
    </appender>
    <root level="INFO">
        <appender-ref ref="console" />
    </root>
</configuration>

新增 tid

微服務鏈路追蹤元件 SkyWalking

透過 日誌的 tid 去 skywalking 中查詢介面

微服務鏈路追蹤元件 SkyWalking

微服務鏈路追蹤元件 SkyWalking

透過 gRpc 方式將日誌反饋到 skywalking 中去

微服務鏈路追蹤元件 SkyWalking

logback-spring.xml 中 新增

<!--日誌配置-->
<appender name="grpc-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>%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{tid}] [%thread] %-5level %logger{36} -%msg%n</Pattern>
  </layout>
  </encoder>
</appender>

<root level="INFO">
  <appender-ref ref="console" />
 <appender-ref ref="grpc-log" />
</root>

此時可以看到 skywalking 中增加了日誌資訊。
微服務鏈路追蹤元件 SkyWalking

注意事項

如果 Skywalking 與服務不在同一伺服器內,需要進行配置的修改,否則日誌無法上報。
skywalking-agent/config/agent.config配置檔案中新增如下配置

plugin.toolkit.log.grpc.reporter.server_host=$[SW_GRPC_LOG_SERVER_HOST:192.168.33.62]
plugin.toolkit.log.grpc.reporter.server_port=$[SW_GRPC_LOG_ SERVER_PORT:11800]
plugin.toolkit.log.grpc.reporter.max_message_size=$[SW_GRPC_LOG_MAX_MESSAGE_SIZE:10485760]
plugin.toolkit.log.grpc.reporter.upstream_timeout=$[SW_GRPC_LOG_GRPC_UPSTREAM_TIMEOUT:30]

以上配置是預設配置資訊,agent 與 oap 在本地的可以不配

配置名 解釋 預設值
plugin.toolkit.log.transmit_formatted 是否以格式化或未格式化的格式傳輸記錄的資料 true
plugin.toolkit.grpc.reporter.server_host 指定要向其報告日誌資料的grpc伺服器的主機 127.0.0.1
plugin.toolkit.grpc.reporter.server_port 指定要向其報告日誌資料的grpc伺服器的埠 11800
plugin.toolkit.log.grpc.reporter.max_message_size 指定grcp客戶端要報告的日誌資料的最大大小 10485760
plugin.toolkit.log.grpc.reporter.upstream_timeout 客戶端向上遊傳送資料時將超時多長時間,單位秒 30

八、SkyWalking 告警功能

SkyWalking 告警功能是在 6.x 版本新增的,其核心由一組規則驅動,這些規則定義在config/alarm-settings.yml檔案中。告警規則的定義分為兩部分:

  1. 告警規則:它們定義了應該如何處罰度量告警,應該考慮什麼條件
  2. Webhook(網路鉤子):定義警告觸發時,哪些服務終端需要被告知

8.1、告警規則

SkyWalking 的發行版都會預設提供config/alarm-settings.yml檔案,裡面預先定義了一些常用的告警規則。如下:

  1. 過去3分鐘內服務平均響應時間超過1秒。
  2. 過去2分鐘服務成功率低於80%。
  3. 過去3分鐘內服務響應時間超過1s的百分比。
  4. 服務例項在過去2分鐘內平均響應時間超過1s,並且例項名稱與正規表示式匹配。
  5. 過去2分鐘內端點平均響應時間超過1秒。
  6. 過去2分鐘內資料庫訪問平均響應時間超過1秒。
  7. 過去2分鐘內端點關係平均響應時間超過1秒。

這些預定義的告警規則,開啟config/alarm-setting.yml檔案即可看到。

告警規則配置項說明:

  • Rule name:規則名稱,也是在告警資訊中顯示的唯一名稱。 必須以 rule 結尾,字首可自定義
  • Metrics name:度量名稱,取值為 oal 指令碼中的度量名,目前只支援 long、double 和 int 型別。 詳見Official OAL script
  • Include names:該規則作用於哪些實體名稱,比如服務名,終端名(可選,預設為全部)
  • Exclude names:該規則作不用於哪些實體名稱,比如服務名,終端名(可選,預設為空)
  • Threshold:閾值
  • OP:運算子,目前支援 >、<、=
  • Period:多久告警規則需要被核實一下。 這是一個時間視窗, 與後端部署環境時間相匹配
  • Count:在一個 Period 視窗中,如果 values 超過 Threshold 值(按op),達到Count值, 需要傳送警報
  • Silence period:在時間N中觸發報警後,在TN -> TN + period這個階段不告警。預設情況下, 它和Period一樣,這意味著相同的告警(在同一個Metrics name擁有相同的id)在同一個Period內只會觸發一次
  • message:告警訊息

例如

rules:
  # Rule unique name, must be ended with `_rule`.
  service_resp_time_rule:
    metrics-name: service_resp_time
    op: ">"
    threshold: 1000
    period: 10
    count: 3
    silence-period: 5
    message: Response time of service {name} is more than 1000ms in 3 minutes of last 10 minutes.

表示三分鐘到十分鐘之內,如果請求次數大於 5 次,並且有介面請求超過 1 秒,就進行報警

8.2、Webhook(網路鉤子)

Webhook可以簡單理解為是一種Web層面的回撥機制,通常由一些事件觸發,與程式碼中的事件回撥類似,只不過是Web層面的。由於是Web層面的,所以當事件發生時,回撥的不再是程式碼中的方法或函式,而是服務介面。例如,在告警這個場景,告警就是一個事件。當該事件發生時,SkyWalking 就會主動去呼叫一個配好的介面,該介面就是所謂的Webhook。
SkyWalking 的告警訊息會透過 HTTP 請求進行傳送,請求方法為 POST,Content-Type 為application/json,其 JSON 資料實基於List<org.apache.skywalking.oap.server.core.alarm.AlarmMessage> 進行序列化的。JSON 資料示例

[{
  "scopeId": 1, 
  "scope": "SERVICE",
  "name": "serviceA", 
  "id0": "12",  
  "id1": "",  
    "ruleName": "service_resp_time_rule",
  "alarmMessage": "alarmMessage xxxx",
  "startTime": 1560524171000,
    "tags": [{
        "key": "level",
        "value": "WARNING"
     }]
}, {
  "scopeId": 1,
  "scope": "SERVICE",
  "name": "serviceB",
  "id0": "23",
  "id1": "",
    "ruleName": "service_resp_time_rule",
  "alarmMessage": "alarmMessage yyy",
  "startTime": 1560524171000,
    "tags": [{
        "key": "level",
        "value": "CRITICAL"
    }]
}]
  • scopeId, scope. 所有範圍都在 org.apache.skywalking.oap.server.core.source.DefaultScopeDefine 中定義
  • name. 目標範圍實體名稱。請按照實體名稱定義
  • id0. 範圍實體的 ID 與名稱匹配。使用關係範圍時,它是源實體 ID
  • id1. 使用關係範圍時,它將是目標實體 ID。否則,它是空的
  • ruleName. 您在 中配置的規則名稱alarm-settings.yml
  • alarmMessage. 報警訊息
  • startTime. 告警時間以毫秒為單位,介於當前時間和 UTC 時間 1970 年 1 月 1 日午夜之間

官方文件地址

config/alarm-settings.yml配置中配置鉤子

webhooks:
  - http://127.0.0.1:8088/alarm/notify

定義 AlarmMessage

import lombok.Data;
//import org.apache.skywalking.oap.server.core.analysis.manual.searchtag.Tag;

import java.util.List;

@Data
public class SwAlarmMessage {
    private int scopeId;
    private String scope;
    private String name;
    private String id0;
    private String id1;
    private String ruleName;
    private String alarmMessage;
    private List<Tag> tags;
    private long startTime;
    private transient int period;
    private transient boolean onlyAsCondition;

    @Data
    public static class Tag {
        private String key;
        private String value;
    }
}

控制層程式碼

@RestController
@RequiredArgsConstructor
@RequestMapping("/alarm")
public class SwAlarmController {

    Logger log = LoggerFactory.getLogger(this.getClass());

    /**
     * 接收Skywalking服務的告警通知併傳送至郵箱
     * 必須是post請求
     * @param alarmList
     */
    @PostMapping("/notify")
    public void receive(@RequestBody List<SwAlarmMessage> alarmList){
//        SimpleMailMessage mailMessage = new SimpleMailMessage();
//        // 傳送者郵箱
//        mailMessage.setFrom("from");
//        // 接收者郵箱
//        mailMessage.setTo("to");
//        // 主題
//        mailMessage.setSubject("主題");
//        // 郵件內容
//        String content = getContent(alarmList);
//        mailMessage.setText("content");
//        sender.send(mailMessage);

        String content = getContent(alarmList);
        log.info("告警郵箱已傳送..."+content);

    }

    private String getContent(List<SwAlarmMessage> alarmList){
        StringBuilder sb = new StringBuilder();
        alarmList.forEach(message -> sb.append("scopeId: ").append(message.getScopeId())
                .append("\nscope: ").append(message.getScope())
                .append("\n目標 Scope 的實體類名稱: ").append(message.getName())
                .append("\nScope 實體類的 ID: ").append(message.getId0())
                .append("\nid1: ").append(message.getId1())
                .append("\n告警規則名稱: ").append(message.getRuleName())
                .append("\n告警訊息內容: ").append(message.getAlarmMessage())
                .append("\n告警時間: ").append(message.getStartTime())
                .append("\n標籤: ").append(message.getTags())
                .append("\n\n-----------------\n\n "));

        return sb.toString();
    }
}

api 閘道器配置

spring:
    gateway:
      # 路由規則
      routes:
        - id: alarm_route
          uri: lb://sw-alarm-server
          predicates:
            - Path=/alarm/**

請求介面,由於介面響應時間慢,skywalking 傳送的告警。

微服務鏈路追蹤元件 SkyWalking

微服務鏈路追蹤元件 SkyWalking

八、Skywalking 高可用

Sykwalking 叢集是集那個skywalking oap 作為一個服務註冊到 nacos 上,只要 skywalking oap 服務沒有全部當機,保證有一個 skywalking oap 在執行,就能進行跟蹤

搭建 skywalking oap 叢集需要:

  1. 至少一個 Nacos(也可以是 nacos 叢集)
  2. 至少一個 ElasticSearch/mysql(也可以是 es/mysql 叢集)
  3. 至少 2 個 skywalking oap 服務
  4. 至少 1 個 UI(也可以是叢集,多個,用 Nginx 代理統一入口)

8.1、修改 config/application.yml 檔案

使用 Nacos 作為註冊中心,預設為單機模式

微服務鏈路追蹤元件 SkyWalking

微服務鏈路追蹤元件 SkyWalking

8.2、配置 UI 服務 webapp/webapp.yml 檔案中的 oap-service,填寫多個地址

微服務鏈路追蹤元件 SkyWalking

8.3、啟動服務測試

啟動 Skywalking 服務,指定SpringBoot 應用的 jvm 引數

-DSW_AGENT_COLLECTOR_BACKEND_SERVICES=192.168.33.61:11800,192.168.33.62:11800
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章