鏈路追蹤介紹
對於一個大型的幾十個、幾百個微服務構成的微服務架構系統,通常會遇到下面一些問題,比如:
1、如何串聯整個調用鏈路,快速定位問題?
2、如何縷清各個微服務之間的依賴關系?
3、如何進行各個微服務接口的性能分折?
4、如何跟蹤整個業務流程的調用處理順序?
1. skywalking是什么
kywalking是一個國產開源框架,2015年由吳晟開源 , 2017年加入Apache孵化器。skywalking是分布式系統的應用程序性能監視工具,專為微服務、雲原生架構和基於容器(Docker、K8s、Mesos)架構而設計。它是一款優秀的APM(Application Performance Management)工具,包括了分布式追蹤、性能指標分析、應用和服務依賴分析等。
官網:http://skywalking.apache.org/
下載:http://skywalking.apache.org/downloads/
Github:https://github.com/apache/skywalking
文檔:https://skywalking.apache.org/docs/main/v8.4.0/readme/
中文文檔:https://skyapm.github.io/document-cn-translation-of-skywalking/
1.1 鏈路追蹤框架對比
1. Zipkin是Twitter開源的調用鏈分析工具,目前基於springcloud sleuth得到了廣泛的使用,特點是輕量,使用部署簡單。
2. Pinpoint是韓國人開源的基於字節碼注入的調用鏈分析,以及應用監控分析工具。特點是支持多種插件,UI功能強大,接入端無代碼侵入。
3. SkyWalking是本土開源的基於字節碼注入的調用鏈分析,以及應用監控分析工具。特點是支持多種插件,UI功能較強,接入端無代碼侵入。目前已加入Apache孵化器。
4. CAT是大眾點評開源的基於編碼和配置的調用鏈分析,應用監控分析,日志采集,監控報警等一系列的監控平台工具。
1.2 性能對比
模擬了三種並發用戶:500,750,1000。使用jmeter測試,每個線程發送30個請求,設置思考時間為10ms。使用的采樣率為1,即100%,這邊與生產可能有差別。pinpoint默認的采樣率為20,即50%,通過設置agent的配置文件改為100%。zipkin默認也是1。組合起來,一共有12種。下面看下匯總表:
從上表可以看出,在三種鏈路監控組件中,skywalking的探針對吞吐量的影響最小,zipkin的吞吐量居中。pinpoint的探針對吞吐量的影響較為明顯,在500並發用戶時,測試服務的吞吐量從1385降低到774,影響很大。然后再看下CPU和memory的影響,在內部服務器進行的壓測,對CPU和memory的影響都差不多在10%之內。
1.3 Skywalking主要功能特性
- 多種監控手段,可以通過語言探針和service mesh獲得監控的數據;
- 支持多種語言自動探針,包括 Java,.NET Core 和 Node.JS;
- 輕量高效,無需大數據平台和大量的服務器資源;
- 模塊化,UI、存儲、集群管理都有多種機制可選;
- 支持告警;
- 優秀的可視化解決方案;
2.SkyWalking 環境搭建部署
- skywalking agent和業務系統綁定在一起,負責收集各種監控數據
- Skywalking oapservice是負責處理監控數據的,比如接受skywalking agent的監控數據,並存儲在數據庫中;接受skywalking webapp的前端請求,從數據庫查詢數據,並返回數據給前端。Skywalking oapservice通常以集群的形式存在。
- skywalking webapp,前端界面,用於展示數據。
- 用於存儲監控數據的數據庫,比如mysql、elasticsearch等。
2.1 下載 SkyWalking
下載:http://skywalking.apache.org/downloads/
目錄結構:
2.2 搭建SkyWalking OAP 服務
啟動腳本bin/startup.sh
日志信息存儲在logs目錄
啟動成功后會啟動兩個服務,一個是skywalking-oap-server,一個是skywalking-web-ui:8868
skywalking-oap-server服務啟動后會暴露11800 和 12800 兩個端口,分別為收集監控數據的端口11800和接受前端請求的端口12800,修改端口可以修改config/applicaiton.yml
skywalking-web-ui服務會占用 8080 端口, 修改端口可以修改webapp/webapp.yml
server.port:SkyWalking UI服務端口,默認是8080;
collector.ribbon.listOfServers:SkyWalking OAP服務地址數組,SkyWalking UI界面的數據是通過請求SkyWalking OAP服務來獲得;
訪問:http://192.168.3.100:8080/
頁面的右下角可以中英文切換,可以切換選擇要展示的時間區間的跟蹤數據。
2.3 SkyWalking中三個概念
服務(Service) :表示對請求提供相同行為的一系列或一組工作負載,在使用Agent時,可以定義服務的名字;
服務實例(Service Instance) :上述的一組工作負載中的每一個工作負載稱為一個實例, 一個服務實例實際就是操作系統上的一個真實進程;
端點(Endpoint) :對於特定服務所接收的請求路徑, 如HTTP的URI路徑和gRPC服務的類名 + 方法簽名。
3. SkyWalking 接入微服務
3.1 linux環境—通過jar包方式接入
准備一個springboot程序,打成可執行jar包,寫一個shell腳本,在啟動項目的Shell腳本上,通過 -javaagent 參數進行配置SkyWalking Agent來跟蹤微服務;
startup.sh腳本:
#!/bin/sh
# SkyWalking Agent配置
# Agent名字,一般使用`spring.application.name`
export SW_AGENT_NAME=springboot‐skywalking‐demo
# 配置 Collector 地址。
export SW_AGENT_COLLECTOR_BACKEND_SERVICES=127.0.0.1:11800
# 配置鏈路的最大Span數量,默認為 300。
export SW_AGENT_SPAN_LIMIT=2000
export JAVA_AGENT=‐javaagent:/usr/local/soft/apache‐skywalking‐apm‐bin‐es7/agent/skywalking‐agent.jar
# jar啟動
java $JAVA_AGENT ‐jar springboot‐skywalking‐demo‐0.0.1‐SNAPSHOT.jar
啟動日志
等同於
java ‐javaagent:/usr/local/soft/apache‐skywalking‐apm‐bin‐es7/agent/skywalking‐agent.jar ‐DSW_AGENT_COLLECTOR_BACKEND_SERVICES=127.0.0.1:11800 ‐DSW_AGENT_NAME=springboot‐skywalking‐demo ‐jar springboot‐skywalking‐demo‐0.0.1‐SNAPSHOT.jar
屬性對應的源碼:org.apache.skywalking.apm.agent.core.conf.Config.java
參數名對應agent/config/agent.config配置文件中的屬性。
# The service name in UI
agent.service_name=${SW_AGENT_NAME:Your_ApplicationName}
# Backend service addresses.
collector.backend_service=${SW_AGENT_COLLECTOR_BACKEND_SERVICES:127.0.0.1:11800}
我們也可以使用skywalking.+配置文件中的配置名作為系統配置項來進行覆蓋。 javaagent參數配置方式優先級更高
測試:訪問你的微服務
3.2 windos環境—在IDEA中使用Skywalking
在運行的程序配置jvm參數,如下圖所示:
# skywalking‐agent.jar的本地磁盤的路徑
-javaagent:E:\xie-my-install\devolop\springcloudalibaba\version-2.2.5.RELEASE\apache-skywalking-apm-es7-8.4.0\agent\skywalking-agent.jar
# 在skywalking上顯示的服務名
-DSW_AGENT_NAME=springboot‐skywalking‐demo
# skywalking的collector服務的IP及端口
-DSW_AGENT_COLLECTOR_BACKEND_SERVICES=127.0.0.1:11800
-DSW_AGENT_COLLECTOR_BACKEND_SERVICES 可以指定遠程地址, 但是-javaagent必須綁定你本機物理路徑的skywalking-agent.jar
3.3 Skywalking跨多個微服務跟蹤
Skywalking跨多個微服務跟蹤,只需要每個微服務啟動時添加javaagent參數即可。
測試:
啟動微服務gateway,order,stock ,配置skywalking的jvm參數
注意:此處存在bug,跟蹤鏈路不顯示gateway
拷貝agent/optional-plugins目錄下的gateway插件到agent/plugins目錄
4. Skywalking持久化跟蹤數據
默認使用的H2數據庫存儲
config/application.yml
4.1 基於mysql持久化:
1. 修改config目錄下的application.yml,使用mysql作為持久化存儲的倉庫
2. 修改mysql連接配置
storage:
selector: ${SW_STORAGE:mysql}
mysql:
properties:
# 如果是8的啟動包的,修改:jdbcUrl: ${SW_JDBC_URL:"jdbc:mysql://localhost:3306/swtest?serverTimezone=GMT%2B8"}
jdbcUrl: ${SW_JDBC_URL:"jdbc:mysql://localhost:3306/swtest"}
dataSource.user: ${SW_DATA_SOURCE_USER:root}
dataSource.password: ${SW_DATA_SOURCE_PASSWORD:123456}
dataSource.cachePrepStmts: ${SW_DATA_SOURCE_CACHE_PREP_STMTS:true}
dataSource.prepStmtCacheSize: ${SW_DATA_SOURCE_PREP_STMT_CACHE_SQL_SIZE:250}
dataSource.prepStmtCacheSqlLimit: ${SW_DATA_SOURCE_PREP_STMT_CACHE_SQL_LIMIT:2048}
dataSource.useServerPrepStmts: ${SW_DATA_SOURCE_USE_SERVER_PREP_STMTS:true}
metadataQueryMaxSize: ${SW_STORAGE_MYSQL_QUERY_MAX_SIZE:5000}
maxSizeOfArrayColumn: ${SW_STORAGE_MAX_SIZE_OF_ARRAY_COLUMN:20}
numOfSearchableValuesPerTag: ${SW_STORAGE_NUM_OF_SEARCHABLE_VALUES_PER_TAG:2}
注意:需要添加mysql數據驅動包,因為在lib目錄下是沒有mysql數據驅動包的,所以修改完配置啟動是會報錯,啟動失敗的。
3. 添加mysql數據驅動包到oap-libs目錄下
4. 啟動Skywalking
查看swtest數據庫,可以看到生成了很多表.
說明啟動成功了,打開配置對應的地址http://127.0.0.1:8080/,可以看到skywalking的web界面。
5. 自定義SkyWalking鏈路追蹤
如果我們希望對項目中的業務方法,實現鏈路追蹤,方便我們排查問題,可以使用如下的代碼
- 引入依賴
<!--引入SkyWalking-->
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-trace</artifactId>
<version>8.4.0</version>
</dependency>
5.1 @Trace將方法加入追蹤鏈路
5.2 加入@Tags或@Tag
我們還可以為追蹤鏈路增加其他額外的信息,比如記錄參數和返回信息。實現方式:在方法上增加@Tag或者@Tags。
@Tag 注解中 key = 方法名; value = returnedObj 返回值 arg[0] 參數
@RequestMapping("/add")
@Trace
@Tag(key = "list", value = "returnedObj")
@Tags({@Tag(key = "list", value = "returnedObj"), @Tag(key = "list", value = "returnedObj")})
public String add() {
System.out.println("下單成功");
//String msg = restTemplate.getForObject("http://localhost:8011/stock/reduct", String.class);
String msg = restTemplate.getForObject("http://stock-server/stock/reduct", String.class);
return "Hello order add " + msg;
}
6. 性能分析
skywalking的性能分析,在根據服務名稱、端點名稱,以及相應的規則建立了任務列表后,在調用了此任務列表的端點后。skywalking會自動記錄,剖析當前端口,生成剖析結果,具體流程如圖:
7. Skywalking集成日志框架
logback官方配置
log4j官方配置
log4j2j官方配置
引入依賴
<!--引入skywalking logback-->
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-logback-1.x</artifactId>
<version>8.4.0</version>
</dependency>
添加logback-spring.xml文件,並配置 %tid 占位符
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 引入 Spring Boot 默認的 logback XML 配置文件-->
<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.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" />
</root>
</configuration>
測試
Skywalking通過grpc上報日志 (需要v8.4.0+)
gRPC報告程序可以將收集到的日志轉發到SkyWalking OAP服務器上
打開agent/config/agent.config配置文件,添加如下配置信息:
plugin.toolkit.log.grpc.reporter.server_host=${SW_GRPC_LOG_SERVER_HOST:127.0.0.1}
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.log.grpc.reporter.server_host | 指定要向其報告日志數據的grpc服務器的主機 | 127.0.0.1 |
plugin.toolkit.log.grpc.reporter.server_port | 指定要向其報告日志數據的grpc服務器的端口 | 11800 |
plugin.toolkit.log.grpc.reporter.max_message_size | 指定grpc客戶端要報告的日志數據的最大大小 | 10485760 |
plugin.toolkit.log.grpc.reporter.upstream_time | 客戶端向上游發送數據時將超時多長時間。單位是秒 | 30 |
8. SkyWalking 告警功能
SkyWalking 告警功能是在6.x版本新增的,其核心由一組規則驅動,這些規則定義在config/alarm-settings.yml文件中。 告警規則的定
義分為兩部分:
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-settings.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:告警消息
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
}, {
"scopeId": 1,
"scope": "SERVICE",
"name": "serviceB",
"id0": "23",
"id1": "",
"ruleName": "service_resp_time_rule",
"alarmMessage": "alarmMessage yyy",
"startTime": 1560524171000
}]
字段說明:
- scopeId、scope:所有可用的 Scope 詳見 org.apache.skywalking.oap.server.core.source.DefaultScopeDefine
- name:目標 Scope 的實體名稱
- id0:Scope 實體的 ID
- id1:保留字段,目前暫未使用
- ruleName:告警規則名稱
- alarmMessage:告警消息內容
- startTime:告警時間,格式為時間戳
8.3 郵件告警功能實踐
根據以上兩個小節的介紹,可以得知:SkyWalking是不支持直接向郵箱、短信等服務發送告警信息的,SkyWalking只會在發生告警時將
告警信息發送至配置好的Webhook接口。
但我們總不能人工盯着該接口的日志信息來得知服務是否發生了告警,因此我們需要在該接口里實現發送郵件或短信等功能,從而達到個
性化的告警通知。
接下來開始動手實踐,這里基於Spring Boot進行實現。
添加依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
配置郵箱服務:
server:
port: 8020
spring:
mail:
host: smtp.163.com
username: 你的郵箱@163.com
password: 你的郵箱服務密鑰
default-encoding: UTF-8
port: 465 # 端口號465或587
protocol: smtp
properties:
mail:
smtp:
socketFactoryClass: javax.net.ssl.SSLSocketFactory
# 表示開啟 DEBUG 模式,這樣,郵件發送過程的日志會在控制台打印出來,方便排查錯誤
debug: true
根據SkyWalking發送的JSON數據定義一個DTO,用於接口接收數據:
package com.xiexie.order.dto;
import lombok.Data;
/**
* @Description
* @Date 2022-04-21 11:26
* @Author xie
*/
@Data
public class SwAlarmDTO {
private Integer scopeId;
private String scope;
private String name;
private Integer id0;
private Integer id1;
private String ruleName;
private String alarmMessage;
private Long startTime;
}
接着定義一個接口,實現接收SkyWalking的告警通知,並將數據發送至郵箱:
package com.xiexie.order.controller;
import com.xiexie.order.dto.SwAlarmDTO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* @Description 接收告警信息
* @Date 2022-04-21 11:30
* @Author xie
*/
@RestController
@RequestMapping("/alarm")
@RequiredArgsConstructor
@Slf4j
public class SwAlarmController {
private final JavaMailSender sender;
@Value("${spring.mail.username}")
private String from;
/**
* 接收告警信息
*
* @param alarmList
*/
@PostMapping("/receive")
public void receive(@RequestBody List<SwAlarmDTO> alarmList) {
SimpleMailMessage mailMessage = new SimpleMailMessage();
// 發送者郵箱
mailMessage.setFrom(from);
// 接收者郵箱
mailMessage.setTo(from);
// 主題
mailMessage.setSubject("告警郵件");
String content = getContent(alarmList);
// 郵件內容
mailMessage.setText(content);
sender.send(mailMessage);
log.info("告警郵件已發送...");
}
private String getContent(List<SwAlarmDTO> alarmList) {
StringBuilder sb = new StringBuilder();
for (SwAlarmDTO dto : alarmList) {
sb.append("scopeId: ").append(dto.getScopeId())
.append("\nscope: ").append(dto.getScope())
.append("\n目標Scope的實體名稱: ").append(dto.getName())
.append("\nScope實體的ID: ").append(dto.getId0())
.append("\nid1: ").append(dto.getId1())
.append("\n告警規則名稱: ").append(dto.getRuleName())
.append("\n告警消息內容: ").append(dto.getAlarmMessage())
.append("\n告警時間: ").append(dto.getStartTime())
.append("\n\n‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐\n\n");
}
return sb.toString();
}
}
最后將該接口配置到SkyWalking中,Webhook的配置位於config/alarm-settings.yml文件的末尾,
格式為http://{ip}:{port}/{uri}。如下示例:
webhooks:
- http://127.0.0.1/alarm/receive
9. Skywalking高可用
在大多數生產環境中,后端應用需要支持高吞吐量並且支持高可用來保證服務的穩定,所以你始終需要在生產環境進行集群管理。
Skywalking集群是將skywalking oap作為一個服務注冊到nacos上,只要skywalking oap服務沒有全部宕機,保證有一個skywalking oap在運行,就能進行跟蹤。
搭建一個skywalking oap集群需要:
(1)至少一個Nacos(也可以是nacos集群)
(2)至少一個ElasticSearch/mysql(也可以是es/msql集群)
(3)至少2個skywalking oap服務;
(4)至少1個UI(UI也可以集群多個,用Nginx代理統一入口)
- 修改config/application.yml文件
使用nacos作為注冊中心
修改nacos配置
可以選擇性修改監聽端口
修改存儲策略,使用elasticsearch7作為storage
修改存儲策略,使用elasticsearch7作為storage
2. 配置ui服務webapp.yml文件的listOfServers,寫兩個地址
- 啟動服務測試
啟動Skywalking服務,指定springboot應用的jvm參數
‐DSW_AGENT_COLLECTOR_BACKEND_SERVICES=192.168.3.10:11800,192.168.3.12:11800