0.目标
- 能夠知道什麼是Skywalking
- 能夠搭建Skywalking環境
- 能夠使用Skywalking進行rpc調用監控
- 能夠使用Skywalking進行mysql調用監控
- 了解Skywalking插件
- 了解Skywalking agent和Open Tracing原理
1.Skywalking概述
在這一部分我們主要了解以下 2 個問題:
- 什麼是APM系統
- 什麼是Skywalking
1.1 什麼是APM系統
1.1.1 APM系統概述
APM (Application Performance Management)即應用性能管理系統,是對企業系統即時監控以實作對應用程式性能管理和故障管理的系統化的解決方案。應用性能管理,主要指對企業的關鍵業務應用進行監測、優化,提高企業應用的可靠性和品質,保證使用者得到良好的服務,降低IT總擁有成本。
APM系統是可以幫助了解系統行為、用于分析性能問題的工具,以便發生故障的時候,能夠快速定位和解決問題。
1.1.2 分布式鍊路追蹤
随着分布式系統和微服務架構的出現,一次使用者的請求會經過多個系統,不同服務之間的調用關系十分複雜,任何一個系統出錯都可能影響整個請求的處理結果。以往的監控系統往往隻能知道單個系統的健康狀況、一次請求的成功失敗,無法快速定位失敗的根本原因。
除此之外,複雜的分布式系統也面臨這下面這些問題:
- 性能分析:一個服務依賴很多服務,被依賴的服務也依賴了其他服務。如果某個接口耗時突然變長了,那未必是直接調用的下遊服務慢了,也可能是下遊的下遊慢了造成的,如何快速定位耗時變長的根本原因呢?
- 鍊路梳理:需求疊代很快,系統之間調用關系變化頻繁,靠人工很難梳理清楚系統鍊路拓撲(系統之間的調用關系)。
為了解決這些問題,Google 推出了一個分布式鍊路跟蹤系統 Dapper,之後各個網際網路公司都參照Dapper 的思想推出了自己的分布式鍊路跟蹤系統,而這些系統就是分布式系統下的APM系統。
1.1.3 什麼是OpenTracing
分布式鍊路跟蹤最先由Google在Dapper論文中提出,而OpenTracing通過提供平台無關、廠商無關的API,使得開發人員能夠友善的添加(或更換)追蹤系統的實作。
下圖是一個分布式調用的例子,用戶端發起請求,請求首先到達負載均衡器,接着經過認證服務,訂單服務,然後請求資源,最後傳回結果。
雖然這種圖對于看清各元件的組合關系是很有用的,但是存在下面兩個問題:
- 它不能很好顯示元件的調用時間,是串行調用還是并行調用,如果展現更複雜的調用關系,會更加複雜,甚至無法畫出這樣的圖。
- 這種圖也無法顯示調用間的時間間隔以及是否通過定時調用來啟動調用。
一種更有效的展現一個調用過程的圖:
基于OpenTracing我們就可以很輕松的建構出上面這幅圖。
1.1.4 主流的開源APM産品
PinPoint
Pinpoint是由一個南韓團隊實作并開源,針對Java編寫的大規模分布式系統設計,通過JavaAgent的機制做位元組代碼植入,實作加入traceid和擷取性能資料的目的,對應用代碼零侵入。
官方網站:
https://github.com/naver/pinpoint
SkyWalking
SkyWalking是apache基金會下面的一個開源APM項目,為微服務架構和雲原生架構系統設計。它通過探針自動收集所需的名額,并進行分布式追蹤。通過這些調用鍊路以及名額,Skywalking APM會感覺應用間關系和服務間關系,并進行相應的名額統計。Skywalking支援鍊路追蹤和監控應用元件基本涵蓋主流架構和容器,如國産RPC Dubbo和motan等,國際化的spring boot,spring cloud。
官方網站:
http://skywalking.apache.org/
Zipkin
Zipkin是由Twitter開源,是分布式鍊路調用監控系統,聚合各業務系統調用延遲資料,達到鍊路調用監控跟蹤。Zipkin基于Google的Dapper論文實作,主要完成資料的收集、存儲、搜尋與界面展示。
官方網站:
https://zipkin.io/
CAT
CAT是由大衆點評開源的項目,基于Java開發的實時應用監控平台,包括實時應用監控,業務監控,可以提供十幾張報表展示。
官方網站:
https://github.com/dianping/cat
1.2 什麼是Skywalking
1.2.1 Skywalking概述
根據官方的解釋,Skywalking是一個可觀測性分析平台(Observability Analysis Platform簡稱OAP)和應用性能管理系統(Application Performance Management簡稱APM)。
提供分布式鍊路追蹤、服務網格(Service Mesh)遙測分析、度量(Metric)聚合和可視化一體化解決方案。
下面是Skywalking的幾大特點:
- 多語言自動探針,Java,.NET Core和Node.JS。
- 多種監控手段,語言探針和service mesh。
- 輕量高效。不需要額外搭建大資料平台。
- 子產品化架構。UI、存儲、叢集管理多種機制可選。
- 支援告警。
- 優秀的可視化效果。
Skywalking整體架構如下:
Skywalking提供Tracing和Metrics資料的擷取和聚合。
Metric的特點是,它是可累加的:他們具有原子性,每個都是一個邏輯計量單元,或者一個時間段内的柱狀圖。 例如:隊列的目前深度可以被定義為一個計量單元,在寫入或讀取時被更新統計; 輸入HTTP請求的數量可以被定義為一個計數器,用于簡單累加; 請求的執行時間可以被定義為一個柱狀圖,在指定時間片上更新和統計彙總。
Tracing的最大特點就是,它在單次請求的範圍内,處理資訊。 任何的資料、中繼資料資訊都被綁定到系統中的單個事務上。 例如:一次調用遠端服務的RPC執行過程;一次實際的SQL查詢語句;一次HTTP請求的業務性ID。
總結,Metric主要用來進行資料的統計,比如HTTP請求數的計算。Tracing主要包含了某一次請求的鍊路資料。
詳細的内容可以檢視Skywalking開發者吳晟翻譯的文章,Metrics, tracing 和 logging 的關系 :
http://blog.oneapm.com/apm-tech/811.html
整體架構包含如下三個組成部分:
- 探針(agent)負責進行資料的收集,包含了Tracing和Metrics的資料,agent會被安裝到服務所在的伺服器上,以友善資料的擷取。
- 可觀測性分析平台OAP(Observability Analysis Platform),接收探針發送的資料,并在記憶體中使用分析引擎(Analysis Core)進行資料的整合運算,然後将資料存儲到對應的存儲媒體上,比如Elasticsearch、MySQL資料庫、H2資料庫等。同時OAP還使用查詢引擎(Query Core)提供HTTP查詢接口。
- Skywalking提供單獨的UI進行資料的檢視,此時UI會調用OAP提供的接口,擷取對應的資料然後進行展示。
1.2.2 Skywalking優勢
Skywalking相比較其他的分布式鍊路監控工具,具有以下特點:
- 社群相當活躍。Skywalking已經進入apache孵化,目前的start數已經超過11K,最新版本6.5.0已經釋出。開發者是國人,可以直接和項目發起人交流進行問題的解決。
- Skywalking支援Java,.NET Core和Node.JS語言。相對于其他平台:比如Pinpoint支援Java和PHP,具有較大的優勢。
- 探針無傾入性。對比CAT具有傾入性的探針,優勢較大。不修改原有項目一行代碼就可以進行內建。
- 探針性能優秀。有網友對Pinpoint和Skywalking進行過測試,由于Pinpoint收集的資料過多,是以對性能損耗較大,而Skywalking探針性能十分出色。
- 支援元件較多。特别是對Rpc架構的支援,這是其他架構所不具備的。Skywalking對Dubbo、gRpc等有原生的支援,甚至連小衆的motan和sofarpc都支援。
1.2.3 Skywalking主要概念介紹
使用如下案例來進行Skywalking主要概念的介紹,Skywalking主要概念包含:
- 服務(Service)
- 端點(Endpoint)
- 執行個體(Instance)
上圖中,我們編寫了使用者服務,這是一個web項目,在生産中部署了兩個節點:192.168.1.100和192.168.1.101。
- 使用者服務就是Skywalking的服務(Service),使用者服務其實就是一個獨立的應用(Application),在6.0之後的Skywalking将應用更名為服務(Service)。
- 使用者服務對外提供的HTTP接口/usr/queryAll就是一個端點,端點就是對外提供的接口。
- 192.168.1.100和192.168.1.101這兩個相同服務部署的節點就是執行個體,執行個體指同一服務可以部署多個。
1.3 環境搭建
接下來我們在虛拟機CentOS中搭建Skywalking的可觀測性分析平台OAP環境。Skywalking預設使用H2記憶體中進行資料的存儲,我們可以替換存儲源為ElasticSearch保證其查詢的高效及可用性。
具體的安裝步驟可以在Skywalking的官方github上找到:
https://github.com/apache/skywalking/blob/master/docs/en/setup/README.md
1 、建立目錄
mkdir /usr/local/skywalking
建議将虛拟機記憶體設定為3G并且将CPU設定成 2 核,防止資源不足。
2 、将資源目錄中的elasticsearch和skywalking安裝包上傳到虛拟機/usr/local/skywalking目錄下。
elasticsearch-6.4.0.tar.gz —elasticsearch 6.4的安裝包,Skywalking對es版本号有一定要求,最好使用6.3.2以上版本,如果是7.x版本需要額外進行配置。
apache-skywalking-apm-6.5.0.tar.gz —Skywalking最新的安裝包
3 、首先安裝elasticsearch,将壓縮包解壓。
tar -zxvf ./elasticsearch-6.4.0.tar.gz
修改Linux系統的限制配置,将檔案建立數修改為 65536 個。
- 修改系統中允許應用最多建立多少檔案等的限制權限。Linux預設來說,一般限制應用最多建立的檔案是 65535 個。但是ES至少需要 65536 的檔案建立數的權限。
- 修改系統中允許使用者啟動的程序開啟多少個線程。預設的Linux限制root使用者開啟的程序可以開啟任意數量的線程,其他使用者開啟的程序可以開啟 1024 個線程。必須修改限制數為4096+。因為ES至少需要 4096 的線程池預備。
vi /etc/security/limits.conf
#新增如下内容在limits.conf檔案中
es soft nofile 65536
es hard nofile 65536
es soft nproc 4096
es hard nproc 4096
修改系統控制權限,ElasticSearch需要開辟一個 65536 位元組以上空間的虛拟記憶體。Linux預設不允許任何使用者和應用程式直接開辟這麼大的虛拟記憶體。
vi /etc/sysctl.conf
#新增如下内容在sysctl.conf檔案中,目前使用者擁有的記憶體權限大小
vm.max_map_count=262144
#讓系統控制權限配置生效
sysctl -p
建一個使用者, 用于ElasticSearch啟動。
ES在5.x版本之後,強制要求在linux中不能使用root使用者啟動ES程序。是以必須使用其他使用者啟動ES程序才可以。
#建立使用者
useradd es
#修改上述使用者的密碼
passwd es
#修改elasicsearch目錄的擁有者
chown -R es elasticsearch-6.4.0
使用es使用者啟動elasticsearch
#切換使用者
su es
#到ElasticSearch的bin目錄下
cd bin/
#背景啟動
./elasticsearch -d
預設ElasticSearch是不支援跨域通路的,是以在不修改配置檔案的情況下我們隻能從虛拟機内部進行通路測試ElasticSearch是否安裝成功,使用curl指令通路 9200 端口:
curl http://localhost:9200
如果顯示出如下資訊,就證明ElasticSearch安裝成功:
{
"name" : "xbruNxf",
"cluster_name" : "elasticsearch",
"cluster_uuid" : "JJQfHN 9 QQVuXpH 5 fu 9 H 1 jg",
"version" : {
"number" : " 6. 4. 0 ",
"build_flavor" : "default",
"build_type" : "tar",
"build_hash" : " 595516 e",
"build_date" : " 2018 - 08 - 17 T 23 : 18 : 47. 308994 Z",
"build_snapshot" : false,
"lucene_version" : " 7. 4. 0 ",
"minimum_wire_compatibility_version" : " 5. 6. 0 ",
"minimum_index_compatibility_version" : " 5. 0. 0 "
},
"tagline" : "You Know, for Search"
}
4 、安裝Skywalking,分為兩個步驟:
- 安裝Backend後端服務
- 安裝UI
首先切回到root使用者,切換到目錄下,解壓Skywalking壓縮包。
#切換到root使用者
su root
#切換到skywalking目錄
cd /usr/local/skywalking
#解壓壓縮包
tar -zxvf apache-skywalking-apm-6.4.0.tar.gz
修改Skywalking存儲的資料源配置:
cd apache-skywalking-apm-bin
vi config/application.yml
我們可以看到預設配置中,使用了H2作為資料源。我們将其全部注釋。
# h2:
# driver: ${SW_STORAGE_H 2 _DRIVER:org.h 2 .jdbcx.JdbcDataSource}
# url: ${SW_STORAGE_H 2 _URL:jdbc:h 2 :mem:skywalking-oap-db}
# user: ${SW_STORAGE_H 2 _USER:sa}
# metadataQueryMaxSize: ${SW_STORAGE_H 2 _QUERY_MAX_SIZE: 5000 }
# mysql:
# metadataQueryMaxSize: ${SW_STORAGE_H 2 _QUERY_MAX_SIZE: 5000 }
将ElasticSearch對應的配置取消注釋:
storage:
elasticsearch:
nameSpace: ${SW_NAMESPACE:""}
clusterNodes: ${SW_STORAGE_ES_CLUSTER_NODES:localhost: 9200 }
protocol: ${SW_STORAGE_ES_HTTP_PROTOCOL:"http"}
trustStorePath: ${SW_SW_STORAGE_ES_SSL_JKS_PATH:"../es_keystore.jks"}
trustStorePass: ${SW_SW_STORAGE_ES_SSL_JKS_PASS:""}
user: ${SW_ES_USER:""}
password: ${SW_ES_PASSWORD:""}
indexShardsNumber: ${SW_STORAGE_ES_INDEX_SHARDS_NUMBER: 2 }
indexReplicasNumber: ${SW_STORAGE_ES_INDEX_REPLICAS_NUMBER: 0 }
# Those data TTL settings will override the same settings in core module.
recordDataTTL: ${SW_STORAGE_ES_RECORD_DATA_TTL: 7 } # Unit is day
otherMetricsDataTTL: ${SW_STORAGE_ES_OTHER_METRIC_DATA_TTL: 45 } # Unit is day
monthMetricsDataTTL: ${SW_STORAGE_ES_MONTH_METRIC_DATA_TTL: 18 } # Unit is month
# # 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
metadataQueryMaxSize: ${SW_STORAGE_ES_QUERY_MAX_SIZE: 5000 }
segmentQueryMaxSize: ${SW_STORAGE_ES_QUERY_SEGMENT_SIZE: 200 }
預設使用了localhost下的ES,是以我們可以不做任何處理,直接進行使用。啟動OAP程式:
bin/oapService.sh
這樣安裝Backend後端服務就已經完畢了,接下來我們安裝UI。先來看一下UI的配置檔案:
cat webapp/webapp.yml
#預設啟動端口
server:
port: 8080
collector:
path: /graphql
ribbon:
ReadTimeout: 10000
#OAP服務,如果有多個用逗号隔開
listOfServers: 127.0.0.1:12800
目前的預設配置不用修改就可以使用,啟動UI程式:
/bin/webappService.sh
然後我們就可以通過浏覽器通路Skywalking的可視化頁面了,通路位址:http://虛拟機IP位址:8080,如果出現下面的圖,就代表安裝成功了。
/bin/startup.sh可以同時啟動backend和ui,後續可以執行該檔案進行重新開機。
2.Skywalking基礎
2.1 agent的使用
agent探針可以讓我們不修改代碼的情況下,對java應用上使用到的元件進行動态監控,擷取運作資料發送到OAP上進行統計和存儲。agent探針在java中是使用java agent技術實作的,不需要更改任何代碼,java agent會通過虛拟機(VM)接口來在運作期更改代碼。
Agent探針支援 JDK 1.6 - 12的版本,Agent探針所有的檔案在Skywalking的agent檔案夾下。檔案目錄如下;
+-- agent
+-- activations
apm-toolkit-log 4 j- 1 .x-activation.jar
apm-toolkit-log 4 j- 2 .x-activation.jar
apm-toolkit-logback- 1 .x-activation.jar
...
//配置檔案
+-- config
agent.config
//元件的所有插件
+-- plugins
apm-dubbo-plugin.jar
apm-feign-default-http- 9 .x.jar
apm-httpClient- 4 .x-plugin.jar
.....
//可選插件
+-- optional-plugins
apm-gson- 2 .x-plugin.jar
.....
+-- bootstrap-plugins
jdk-http-plugin.jar
.....
+-- logs
skywalking-agent.jar
部分插件在使用上會影響整體的性能或者由于版權問題放置于可選插件包中,不會直接加載,如果需要使用,将可選插件中的jar包拷貝到plugins包下。
由于沒有修改agent探針中的應用名,是以預設顯示的是Your_ApplicationName。我們修改下應用名稱,讓他顯示的更加正确。編輯agent配置檔案:
cd /usr/local/skywalking/apache-skywalking-apm-bin/agent/config
vi agent.config
我們在配置中找到這麼一行:
# The service name in UI
agent.service_name=${SW_AGENT_NAME:Your_ApplicationName}
這裡的配置含義是可以讀取到SW_AGENT_NAME配置屬性,如果該配置沒有指定,那麼預設名稱為Your_ApplicationName。這裡我們把Your_ApplicationName替換成skywalking_tomcat。
# The service name in UI
agent.service_name=${SW_AGENT_NAME:skywalking_tomcat}
然後将tomcat重新開機:
./shutdown.sh
./startup.sh
2.1.1 Linux 下Tomcat7和 8 中使用
1.要使用Skywalking監控Tomcat中的應用,需要先準備一個Spring Mvc項目,在資源中已經提供了打包好的檔案skywalking_springmvc- 1. 0 - SNAPSHOT.war。
以下是該項目的接口代碼:
package com.xiaoge.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping("/hello")
public class HelloController {
@RequestMapping("/sayHello")
@ResponseBody
public String sayHello(String name){
return "hello world";
}
}
将資源檔案下的apache-tomcat- 8. 5. 47 .tar.gz檔案上傳至虛拟機/usr/local/skywalking目錄下,然後解壓:
tar -zxvf apache-tomcat-8.5.47.tar.gz
将war包上傳至/usr/local/skywalking/apache-tomcat-8.5.47/webapps/下。編輯/usr/local/skywalking/apache-tomcat- 8. 5. 47 /bin/catalina.sh檔案,在檔案頂部添加:
CATALINA_OPTS="$CATALINA_OPTS -javaagent:/usr/local/skywalking/apache-
skywalking-apm-bin/agent/skywalking-agent.jar"; export CATALINA_OPTS
修改tomcat啟動端口:
vi conf/server.xml
#修改這一行的端口為 8081
<Connector port=" 8080 " protocol=...
執行bin目錄下的./startup.sh 檔案啟動tomcat。然後通路位址:
http://虛拟機IP:8081/skywalking_springmvc-1.0-SNAPSHOT/hello/sayHello.do
如果正确打開,能輸出hello world。
注意:一定要保證虛拟機和實體機的時間一緻,否則通路資料的時候會因為時間不一緻而擷取不到資料。
此時再通路Skywalking的頁面,會發現出現了一個服務和端點,同時有一筆調用顯示了調用的應用名和接口位址。
由于沒有修改agent探針中的應用名,是以預設顯示的是Your_ApplicationName。接下來我們修改下應用名稱,讓他顯示的更加正确。編輯agent配置檔案:
cd /usr/local/skywalking/apache-skywalking-apm-bin/agent/config
vi agent.config
我們在配置中找到這麼一行:
# The service name in UI
agent.service_name=${SW_AGENT_NAME:Your_ApplicationName}
這裡的配置含義是可以讀取到SW_AGENT_NAME配置屬性,如果該配置沒有指定,那麼預設名稱為Your_ApplicationName。這裡我們把Your_ApplicationName替換成skywalking_tomcat。
# The service name in UI
agent.service_name=${SW_AGENT_NAME:skywalking_tomcat}
然後将tomcat重新開機:
./shutdown.sh
./startup.sh
2.1.2 Windows 下Tomcat7和 8 中使用(了解)
Windows下隻需要修改f tomcat目錄/bin/catalina.bat檔案的第一行為:
set JAVA_OPTS=-javaagent:C:\software\skywalking\apache-skywalking-apm-bin\agent\skywalking-agent.jar=agent.service_name=服務命1,agent.service_name=服務名2
2.1.3 Spring Boot中使用
Skywalking與Spring Boot內建提供了完善的支援。
1 、首先我們複制一份agent,防止與tomcat使用的沖突。
cd /usr/local/skywalking/apache-skywalking-apm-bin/
cp -r agent agent_boot
vi agent_boot/config/agent.config
修改配置中的應用名為:
# The service name in UI
agent.service_name=${SW_AGENT_NAME:skywalking_boot}
2 、将資源檔案夾中skywalking_springboot.jar檔案上傳到/usr/local/skywalking目錄下。
Controller層代碼如下,提供了一個正常通路的接口和一個異常通路接口:
package com.xiaoge.skywalking_springboot.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MyController {
//正常通路接口
@RequestMapping("/sayBoot")
public String sayBoot(){
return "Hello Boot!";
}
//異常通路接口
@RequestMapping("/exception")
public String exception(){
int i = 1 / 0 ;
return "Hello Boot!";
}
}
使用指令啟動spring boot項目:
java -javaagent:/usr/local/skywalking/apache-skywalking-apm-
bin/agent_boot/skywalking-agent.jar -Dserver.port=8082 -jar
skywalking_springboot.jar &
使用jar包啟動的項目如果需要內建skywalking,需要添加-javaagent參數,參數值為agent的jar包梭子啊位置。
-Dserver.port參數用于指定端口号,防止與tomcat沖突。
末尾添加&背景運作模式啟動Spring Boot項目。
此時我們可以通路http://虛拟機IP:8082/sayBoot位址來進行通路,通路之後稍等片刻通路Skywalking的UI頁面。
2.2 RocketBot的使用
Skywalking的監控UI頁面成為RocketBot,我們可以通過 8080 端口進行通路, 由于 8080 端口很容易沖突,可以修改webapp/webapp.yml來更改啟動端口:
server:
port: 8080
本例中我們更改為 9010 端口防止沖突。通路http://虛拟機IP:9010/打開RocketBot的頁面。
2.2.1 儀表盤
打開RocketBot預設會出現儀表盤頁面:
儀表盤頁面分為兩大塊:
- 服務儀表盤,展示服務的調用情況
- 資料庫儀表盤,展示資料庫的響應時間等資料
選中服務儀表盤,有四個次元的統計資料可以進行檢視:
- 全局,檢視全局接口的調用,包括全局響應時長的百分比,最慢的端點,服務的吞吐量等
- 服務,顯示服務的響應時長、SLA、吞吐量等資訊
- 端點,顯示端點的響應時長、SLA、吞吐量等資訊
- 執行個體,顯示執行個體的響應時長、SLA、吞吐量等資訊,還可以檢視執行個體的JVM的GC資訊、CPU資訊、記憶體資訊
2.2.2 拓撲圖
Skywalking提供拓撲圖,直覺的檢視服務之間的調用關系:
User代表使用者應用,目前案例中其實是浏覽器
圖中Skywalking_boot應用被User調用,同時顯示它是一個Spring MVC的應用。後續案例中會出現多個應用調用,使用拓撲圖就能清楚的分析其調用關系了。
2.2.3 追蹤
在Skywalking中,每一次使用者發起一條請求,就可以視為一條追蹤資料,每條追蹤資料攜帶有一個ID值。追蹤資料在追蹤頁面中可以進行查詢:
左側是追蹤清單,也可以通過上方的追蹤ID來進行查詢。點選追蹤清單某一條記錄之後,右側會顯示出此條追蹤的詳細資訊。有三種顯示效果:
- 清單
- 樹結構
- 表格
可以很好的展現此條追蹤的調用鍊情況而鍊路上每個節點,可以通過左鍵點選節點檢視詳細資訊:
目前的接口是HTTP的GET請求,相對比較簡單,後續的示例中出現異常情況或者資料庫通路,可以列印出異常資訊、堆棧甚至詳細的SQL語句。
2.2.4 告警
Skywalking中的告警功能相對比較簡單,在達到告警門檻值之後會生成一條告警記錄,在告警頁面上進行展示。後面會詳細介紹告警功能如何使用。
3.Skywalking進階
3.1 Rpc調用監控
Skywalking( 6. 5. 0 )支援的Rpc架構有以下幾種:
- Dubbo 2.5.4 -> 2.6.
- Dubbox 2.8.
- Apache Dubbo 2.7.
- Motan 0.2.x -> 1.1.
- gRPC 1.x
- Apache ServiceComb Java Chassis 0.1 -> 0.5,1.0.x
- SOFARPC 5.4.
本節中我們使用Spring Boot和Dubbo搭建一個簡單的服務提供方和服務消費方來測試Skywalking對于Rpc調用的支援。可以使用資源檔案夾下已經完成打包的skywalking_dubbo_consumer.jar和skywalking_dubbo_provider.jar來進行測試。
3.1.1 服務提供方
pom檔案:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version> 2. 1. 10 .RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.xiaoge</groupId>
<artifactId>skywalking_dubbo_provider</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>skywalking_dubbo_provider</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--添加springboot和dubbo內建配置-->
<dependency>
<groupId>com.alibaba.spring.boot</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
這裡直接使用了dubbo-spring-boot-starter這一dubbo與spring-boot內建的元件。
官方文檔位址:https://github.com/alibaba/dubbo-spring-boot-starter/blob/master/README_zh.md
application.properties:
spring.application.name=skywalking_dubbo_provider
spring.dubbo.server=true
spring.dubbo.registry=N/A
server.port= 8086
為了簡化環境搭建,采用了本地直接調用的方式,是以将注冊中心寫成N/A表示不注冊到注冊中心。
IHelloService接口:
package com.xiaoge.api;
public interface IHelloService {
public String hello();
}
簡化項目的開發,将IHelloService接口在消費方和提供方都編寫一份。
HelloServiceImpl實作類:
package com.xiaoge.skywalking_dubbo_provider.service;
import com.alibaba.dubbo.config.annotation.Service;
import com.xiaoge.api.IHelloService;
import org.springframework.stereotype.Component;
@Service(interfaceClass = IHelloService.class)
@Component
public class HelloServiceImpl implements IHelloService {
@Override
public String hello() {
return "hello skywalking";
}
}
SkywalkingDubboProviderApplication啟動類:
package com.xiaoge.skywalking_dubbo_provider;
import com.alibaba.dubbo.spring.boot.annotation.EnableDubboConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
//添加dubbo生效注解
@EnableDubboConfiguration
public class SkywalkingDubboProviderApplication {
public static void main(String[] args) {
SpringApplication.run(SkywalkingDubboProviderApplication.class, args);
}
}
需要添加@EnableDubboConfiguration注解。
3.1.2 服務消費方
pom檔案:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.10.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.xiaoge</groupId>
<artifactId>skywalking_dubbo_consumer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>skywalking_dubbo_consumer</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba.spring.boot</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.properties:
spring.application.name=skywalking_dubbo_consumer
server.port=8085
IHelloService接口:
package com.xiaoge.api;
public interface IHelloService {
public String hello();
}
簡化項目的開發,将IHelloService接口在消費方和提供方都編寫一份。
TestController:
package com.xiaoge.skywalking_dubbo_consumer.controller;
import com.alibaba.dubbo.config.annotation.Reference;
import com.xiaoge.api.IHelloService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
@Reference(url = "dubbo://127.0.0.1:20880")
private IHelloService helloService;
@GetMapping("/hello")
public String hello(){
return helloService.hello();
}
}
采用直連而非從注冊中心擷取服務位址的方式,在@Reference注解中聲明url = "dubbo://127.0.0.1:20880"
SkywalkingDubboConsumerApplication啟動類:
package com.xiaoge.skywalking_dubbo_consumer;
import com.alibaba.dubbo.spring.boot.annotation.EnableDubboConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
//添加dubbo生效注解
@EnableDubboConfiguration
public class SkywalkingDubboConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(SkywalkingDubboConsumerApplication.class, args);
}
}
需要添加@EnableDubboConfiguration注解。
3.1.3 部署方式
1 、将skywalking_dubbo_consumer.jar和skywalking_dubbo_provider.jar上傳至/usr/local/skywalking目錄下。
2 、首先我們複制兩份agent,防止使用的沖突。
cd /usr/local/skywalking/apache-skywalking-apm-bin/
cp -r agent agent_dubbo_provider
cp -r agent agent_dubbo_consumer
vi agent_dubbo_provider/config/agent.config
修改agent_dubbo_provider配置中的應用名為:
# The service name in UI
agent.service_name=${SW_AGENT_NAME:dubbo_provider}
接着修改agent_dubbo_consumer:
vi agent_dubbo_consumer/config/agent.config
修改應用名:
# The service name in UI
agent.service_name=${SW_AGENT_NAME:dubbo_consumer}
3 、先啟動provider,等待啟動成功。
#切換到目錄下
cd /usr/local/skywalking
#啟動provider
java -javaagent:/usr/local/skywalking/apache-skywalking-apm-
bin/agent_dubbo_provider/skywalking-agent.jar -jar
skywalking_dubbo_provider.jar &
出現如圖所示内容,應用就已經啟動成功了。
4 、啟動consumer,等待啟動成功。
#啟動consumer
java -javaagent:/usr/local/skywalking/apache-skywalking-apm-
bin/agent_dubbo_consumer/skywalking-agent.jar -jar
skywalking_dubbo_consumer.jar &
5 、調用接口,接口位址為:http://虛拟機IP位址:8085/hello
6 、此時如果頁面顯示
那麼dubbo的調用就成功了。
7 、打開skywalking檢視dubbo調用的監控情況。
儀表盤:
目前dubbo_provider和dubbo_consumer的服務已經出現,同時出現了兩個接口分别是:
- /hello接口,是浏覽器調用dubb_consumer的http接口
- com.xiaoge.api.IHelloService.hello()是dubbo_consumer調用dubbo_provider的dubbo接口
拓撲圖:
該圖中已經表示出了一個調用的鍊路關系:
User(浏覽器) ----> dubber_consumer ----> dubbo_provider
并且在服務的上方辨別出了每個服務代表的内容,dubbo_consumer是SpringMvc的服務,而dubbo_provider是Dubbo的服務。
追蹤:
追蹤圖中顯示本次調用耗時49ms,其中dubbo接口耗時29ms,那麼另外的20ms其實是SpringMVC接口的開銷,這樣就能很好的評估出每個環節的耗時時間。
3.2 MySql調用監控
3.2.1 使用docker啟動Mysql
虛拟機中已經安裝了docker,我們先将docker啟動:
systemctl start docker
使用docker指令啟動mysql:
docker run -di --name=skywalking_mysql -p 33306:3306 -e MYSQL_ROOT_PASSWORD=123456 centos/mysql-57-centos7
MYSQL_ROOT_PASSWORD環境變量指定root的密碼為123456
這樣就可以在外部通路mysql了。使用工具連接配接mysql,端口為 33306 密碼為 123456 。建立資料庫:
執行建表語句:
CREATE TABLE `t_user` (
`id` int( 11 ) NOT NULL AUTO_INCREMENT,
`name` varchar( 50 ) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
插入幾條資料:
insert into `t_user`(`name`) values ('張三'),('李四'),('王五');
3.2.2 Spring Data JDBC通路Mysql
建立一個Spring Boot工程,內建Spring Data JDBC。可以直接使用資源檔案中提供的skywalking_mysql.jar。
pom檔案:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version> 2.1.10.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.xiaoge</groupId>
<artifactId>skywalking_mysql</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>skywalking_mysql</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version> 5. 1. 46 </version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
引入了spring-boot-starter-data-jdbc,由于使用了5.7的mysql版本,是以驅動版本固定為5.1.46。
pojo類:
package com.xiaoge.skywalking_mysql.pojo;
import org.springframework.data.annotation.Id;
import org.springframework.data.relational.core.mapping.Table;
@Table("t_user")
public class User {
@Id
private Integer id;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
添加Table注解,修改表明為t_user。
dao接口:
package com.xiaoge.skywalking_mysql.dao;
import com.xiaoge.skywalking_mysql.pojo.User;
import org.springframework.data.repository.CrudRepository;
public interface UserRepository extends CrudRepository<User, Integer> {
}
controller:
package com.xiaoge.skywalking_mysql.controller;
import com.xiaoge.skywalking_mysql.dao.UserRepository;
import com.xiaoge.skywalking_mysql.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
@RestController
public class MysqlController {
@Autowired
private UserRepository userRepository;
@GetMapping("/users")
public List<User> findAll(){
List<User> result = new ArrayList<>();
//使用疊代器進行周遊
userRepository.findAll().forEach((user) - > {
result.add(user);
});
return result;
}
}
由于Spring Data JDBC的findAll方法傳回的是一個疊代器,是以需要周遊疊代器将資料進行傳回。
啟動類:
package com.xiaoge.skywalking_mysql;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SkywalkingMysqlApplication {
public static void main(String[] args) {
SpringApplication.run(SkywalkingMysqlApplication.class, args);
}
}
application.properties:
spring.datasource.url=jdbc:mysql://localhost:33306/skywalking
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.username=root
spring.datasource.password= 123456
server.port= 8087
3.2.3 部署方式
1 、将skywalking_mysql.jar上傳至/usr/local/skywalking目錄下。
2 、首先我們複制agent,防止使用的沖突。
cd /usr/local/skywalking/apache-skywalking-apm-bin/
cp -r agent agent_mysql
vi agent_mysql/config/agent.config
修改agent_mysql配置中的應用名為:
# The service name in UI
agent.service_name=${SW_AGENT_NAME:skywalking_mysql}
3 、啟動skywalking_mysql應用,等待啟動成功。
#切換到目錄下
cd /usr/local/skywalking
#啟動spring boot
java -javaagent:/usr/local/skywalking/apache-skywalking-apm-bin/agent_mysql/skywalking-agent.jar -jar skywalking_mysql.jar &
4 、調用接口,接口位址為:http://虛拟機IP位址:8087/users
5 、此時如果頁面顯示
那麼mysql的調用就成功了。
6 、打開skywalking檢視mysql調用的監控情況。
服務儀表盤:
資料庫儀表盤:
點選資料庫儀表盤可以看到詳細的資料庫響應時長、吞吐量、SLA等資料。
拓撲圖:
該圖中已經表示出了一個調用的鍊路關系:
User(浏覽器) ----> skywalking_mysql ----> localhost:33306
并且在服務的上方辨別出了每個服務代表的内容,skywalking_mysql是SpringMvc的服務,而localhost:33306是mysql的服務。
追蹤:
追蹤圖中顯示本次調用耗時5ms,其中spring MVC接口耗時4ms,那麼另外的1ms是調用Mysql的耗時。
點選mysql的調用,可以看到詳細的sql語句。
這樣可以很好的定位問題産生的原因,特别是在某些sql語句執行慢的場景下。
3.3 Skywalking常用插件
3.3.1 配置覆寫
在之前的案例中,我們每次部署應用都需要複制一份agent,修改其中的服務名稱,這樣顯得非常麻煩。可以使用Skywalking提供的配置覆寫功能通過啟動指令動态指定服務名,這樣agent隻需要部署一份即可Skywalking支援的幾種配置方式:
系統配置(System properties)
使用 skywalking. + 配置檔案中的配置名作為系統配置項來進行覆寫.
- 為什麼需要添加字首?
agent的系統配置和環境與目标應用共享,是以加上字首可以有效的避免沖突。
- 案例
通過 如下進行agent.service_name 的覆寫
-Dskywalking.agent.service_name=skywalking_mysql
探針配置(Agent options)
Add the properties after the agent path in JVM arguments.
-javaagent:/path/to/skywalking-agent.jar=[option1]=[value1],[option2]=[value2]
- 案例
通過 如下進行agent.service_name 的覆寫
-javaagent:/path/to/skywalking-agent.jar=agent.service_name=skywalking_mysql
- 特殊字元
如果配置中包含分隔符(, 或者=) , 就必須使用引号包裹起來
-javaagent:/path/to/skywalking-agent.jar=agent.ignore_suffix='.jpg,.jpeg'
系統環境變量(System environment variables)
- 案例
由于agent.service_name配置項如下所示:
# The service name in UI
agent.service_name=${SW_AGENT_NAME:Your_ApplicationName}
可以在環境變量中設定SW_AGENT_NAME的值來指定服務名。
覆寫優先級
探針配置 > 系統配置 >系統環境變量 > 配置檔案中的值
是以我們的啟動指令可以修改為:
java -javaagent:/usr/local/skywalking/apache-skywalking-apm-bin/agent_mysql/skywalking-agent.jar -Dskywalking.agent.service_name=skywalking_mysql -jar skywalking_mysql.jar &
或者
java -javaagent:/usr/local/skywalking/apache-skywalking-apm-
bin/agent_mysql/skywalking-agent.jar=agent.service_name=skywalking_mysql -jar
skywalking_mysql.jar &
3.3.2 擷取追蹤ID
Skywalking提供我們Trace工具包,用于在追蹤鍊路時進行資訊的列印或者擷取對應的追蹤ID。我們使用Spring Boot編寫一個案例,也可以直接使用資源下的skywalking_plugins.jar進行測試。
pom:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w 3 .org/ 2001 /XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.10.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.xiaoge</groupId>
<artifactId>skywalking_plugins</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>skywalking_plugins</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<skywalking.version>6.5.0</skywalking.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--skywalking trace工具包-->
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-trace</artifactId>
<version>${skywalking.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
添加了對應的坐标:本案例中使用6.5.0的版本号。<!--skywalking trace工具包--> <dependency> <groupId>org.apache.skywalking</groupId> <artifactId>apm-toolkit-trace</artifactId> <version>${skywalking.version}</version> </dependency>
PluginController:
package com.xiaoge.skywalking_plugins.controller;
import org.apache.skywalking.apm.toolkit.trace.ActiveSpan;
import org.apache.skywalking.apm.toolkit.trace.TraceContext;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class PluginController {
//擷取trace id,可以在RocketBot追蹤中進行查詢
@GetMapping("/getTraceId")
public String getTraceId(){
//使目前鍊路報錯,并且提示報錯資訊
ActiveSpan.error(new RuntimeException("Test-Error-Throwable"));
//列印info資訊
ActiveSpan.info("Test-Info-Msg");
//列印debug資訊
ActiveSpan.debug("Test-debug-Msg");
return TraceContext.traceId();
}
}
使用TraceContext.traceId()可以列印出目前追蹤的ID,友善在RocketBot中進行搜尋。
ActiveSpan提供了三個方法進行資訊的列印:
error方法會将本次調用變為失敗狀态,同時可以列印對應的堆棧資訊和錯誤提示。
info方法列印info級别的資訊。
debug方法列印debug級别的資訊。
部署方式
1 、将skywalking_plugins.jar上傳至/usr/local/skywalking目錄下。
2 、啟動skywalking_plugins應用,等待啟動成功。
java -javaagent:/usr/local/skywalking/apache-skywalking-apm-
bin/agent/skywalking-agent.jar -
Dskywalking.agent.service_name=skywalking_plugins -jar skywalking_plugins.jar &
4 、調用接口,接口位址為:http://虛拟機IP位址:8088/getTraceId
5 、此時如果頁面顯示
可以看到追蹤ID已經列印出來,然後我們在RocketBot上進行搜尋。
可以搜尋到對應的追蹤記錄,但是顯示調用是失敗的,這是因為使用了ActiveSpan.error方法。點開追蹤的詳細資訊:
異常的資訊包含了以下幾個部分:
1.事件類型為error
2.調用方法時傳遞的異常類型RuntimeException
3.調用方法時傳遞的異常資訊Test-Error-Throwable
4.異常堆棧
通過上述内容,我們可以根據業務來定制調用異常時的詳細資訊。
除了異常資訊之外,還有info資訊和debug資訊也都會被列印。
3.3.3 過濾指定的端點
在開發過程中,有一些端點(接口)并不需要去進行監控,比如Swagger相關的端點。這個時候我們就可以使用Skywalking提供的過濾插件來進行過濾。在skywalking_plugins中編寫兩個接口進行測試:
package com.xiaoge.skywalking_plugins.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class FilterController {
//此接口可以被追蹤
@GetMapping("/include")
public String include(){
return "include";
}
//此接口不可被追蹤
@GetMapping("/exclude")
public String exclude(){
return "exclude";
}
}
部署方式
1 、将skywalking_plugins.jar上傳至/usr/local/skywalking目錄下。
2 、将agent中的/agent/optional-plugins/apm-trace-ignore-plugin- 6. 4. 0 .jar插件拷貝到plugins目錄中。
cd /usr/local/skywalking/apache-skywalking-apm-bin
cp optional-plugins/apm-trace-ignore-plugin-6.4.0.jar plugins/apm-trace-ignore-
plugin-6.4.0.jar
3 、啟動skywalking_plugins應用,等待啟動成功。
java -javaagent:/usr/local/skywalking/apache-skywalking-apm-
bin/agent/skywalking-agent.jar -
Dskywalking.agent.service_name=skywalking_plugins -
Dskywalking.trace.ignore_path=/exclude jar skywalking_plugins.jar &
這裡添加-Dskywalking.trace.ignore_path=/exclude參數來辨別需要過濾哪些請求,支援AntPath表達式:
/path/*, /path/**, /path/?
- ? 比對任何單字元
- 比對 0 或者任意數量的字元
- ** 比對 0 或者更多的目錄
4 、調用接口,接口位址為:
http://虛拟機IP位址:8088/exclude
http://虛拟機IP位址:8088/include
5 、在追蹤中進行檢視:
exclude接口已經被過濾,隻有include接口能被看到。
3.4 告警功能
3.4.1 告警功能簡介
Skywalking每隔一段時間根據收集到的鍊路追蹤的資料和配置的告警規則(如服務響應時間、服務響應時間百分比)等,判斷如果達到門檻值則發送相應的告警資訊。發送告警資訊是通過調用webhook接口完成,具體的webhook接口可以使用者自行定義,進而開發者可以在指定的webhook接口中編寫各種告警方式,比如郵件、短信等。告警的資訊也可以在RocketBot中檢視到。
以下是預設的告警規則配置,位于skywalking安裝目錄下的config檔案夾下alarm-settings.yml檔案中:
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 1000 ms in 3 minutes of last 10 minutes.
service_sla_rule:
# Metrics value need to be long, double or int
metrics-name: service_sla
op: "<"
threshold: 8000
# The length of time to evaluate the metrics
period: 10
# How many times after the metrics match the condition, will trigger alarm
count: 2
# How many times of checks, the alarm keeps silence after alarm triggered,default as same as period.
silence-period: 3
message: Successful rate of service {name} is lower than 80 % in 2 minutes of
last 10 minutes
service_p 90 _sla_rule:
# Metrics value need to be long, double or int
metrics-name: service_p 90
op: ">"
threshold: 1000
period: 10
count: 3
silence-period: 5
message: 90 % response time of service {name} is more than 1000 ms in 3 minutes of last 10 minutes
service_instance_resp_time_rule:
metrics-name: service_instance_resp_time
op: ">"
threshold: 1000
period: 10
count: 2
silence-period: 5
message: Response time of service instance {name} is more than 1000 ms in 2
minutes of last 10 minutes
# Active endpoint related metrics alarm will cost more memory than service and service instance metrics alarm.
# Because the number of endpoint is much more than service and instance.
#
# endpoint_avg_rule:
# metrics-name: endpoint_avg
# op: ">"
# threshold: 1000
# period: 10
# count: 2
# silence-period: 5
# message: Response time of endpoint {name} is more than 1000 ms in 2 minutes
of last 10 minutes
webhooks:
# - http:// 127. 0. 0. 1 /go-wechat/
以上檔案定義了預設的 4 種規則:
- 最近 3 分鐘内服務的平均響應時間超過 1 秒
- 最近 2 分鐘服務成功率低于80%
- 最近 3 分鐘90%服務響應時間超過 1 秒
- 最近 2 分鐘内服務執行個體的平均響應時間超過 1 秒
規則中的參數屬性如下:
屬性 | 含義 |
---|---|
metrics-name | oal腳本中的度量名稱 |
threshold | 門檻值,與metrics-name和下面的比較符号相比對 |
op | 比較操作符,可以設定>,<,= |
period | 多久檢查一次目前的名額資料是否符合告警規則,機關分鐘 |
count | 達到多少次後,發送告警消息 |
silence-period | 在多久之内,忽略相同的告警消息 |
message | 告警消息内容 |
include-names | 本規則告警生效的服務清單 |
webhooks可以配置告警産生時的調用位址。
3.4.2 告警功能測試代碼
編寫告警功能接口來進行測試,建立skywalking_alarm項目。可以直接使用資源中的skywalking_alarm.jar。
AlarmController:
package com.xiaoge.skywalking_alarm.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class AlarmController {
//每次調用睡眠1.5秒,模拟逾時的報警
@GetMapping("/timeout")
public String timeout(){
try {
Thread.sleep( 1500 );
} catch (InterruptedException e) {
e.printStackTrace();
}
return "timeout";
}
}
該接口主要用于模拟逾時,多次調用之後就可以生成告警資訊。
WebHooks:
package com.xiaoge.skywalking_alarm.controller;
import com.xiaoge.skywalking_alarm.pojo.AlarmMessage;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
@RestController
public class WebHooks {
private List<AlarmMessage> lastList = new ArrayList<>();
//産生告警時調用的接口
@PostMapping("/webhook")
public void webhook(@RequestBody List<AlarmMessage> alarmMessageList){
lastList = alarmMessageList;
}
//展示告警的資訊
@GetMapping("/show")
public List<AlarmMessage> show(){
return lastList;
}
}
産生告警時會調用webhook接口,該接口必須是Post類型,同時接口參數使用RequestBody。參數格式為:[{ "scopeId": 1 , "scope": "SERVICE", "name": "serviceA", "id 0 ": 12 , "id 1 ": 0 , "ruleName": "service_resp_time_rule", "alarmMessage": "alarmMessage xxxx", "startTime": 1560524171000 }, { "scopeId": 1 , "scope": "SERVICE", "name": "serviceB", "id 0 ": 23 , "id 1 ": 0 , "ruleName": "service_resp_time_rule", "alarmMessage": "alarmMessage yyy", "startTime": 1560524171000 }]
AlarmMessage:
package com.xiaoge.skywalking_alarm.pojo;
public class AlarmMessage {
private int scopeId;
private String name;
private int id0;
private int id1;
private String alarmMessage;
private long startTime;
public int getScopeId() {
return scopeId;
}
public void setScopeId(int scopeId) {
this.scopeId = scopeId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId0 () {
return id0 ;
}
public void setId0 (int id0) {
this.id0 = id0;
}
public int getId1 () {
return id1;
}
public void setId1 (int id1) {
this.id1 = id1;
}
public String getAlarmMessage() {
return alarmMessage;
}
public void setAlarmMessage(String alarmMessage) {
this.alarmMessage = alarmMessage;
}
public long getStartTime() {
return startTime;
}
public void setStartTime(long startTime) {
this.startTime = startTime;
}
@Override
public String toString() {
return "AlarmMessage{" +
"scopeId=" + scopeId +
", name='" + name + '\'' +
", id 0 =" + id 0 +
", id 1 =" + id 1 +
", alarmMessage='" + alarmMessage + '\'' +
", startTime=" + startTime +
'}';
}
}
實體類用于接口告警資訊。
3.4.3 部署測試
首先需要修改告警規則配置檔案,将webhook位址修改為:
webhooks:
- http://127.0.0.1:8089/webhook
然後重新開機skywalking。
1 、将skywalking_alarm.jar上傳至/usr/local/skywalking目錄下。
2 、啟動skywalking_alarm應用,等待啟動成功。
java -javaagent:/usr/local/skywalking/apache-skywalking-apm-
bin/agent/skywalking-agent.jar -Dskywalking.agent.service_name=skywalking_alarm -jar skywalking_alarm.jar
3 、不停調用接口,接口位址為:http://虛拟機IP:8089/timeout
4 、直到出現告警:
5 、檢視告警資訊接口:http://虛拟機IP:8089/show
從上圖中可以看到,我們已經擷取到了告警相關的資訊,在生産中使用可以在webhook接口中對接短信、郵件等平台,當告警出現時能迅速發送資訊給對應的處理人員,提高故障處理的速度。
4.Skywalking原理
4.1 java agent原理
上文中我們知道,要使用Skywalking去監控服務,需要在其 VM 參數中添加 “-javaagent:/usr/local/skywalking/apache-skywalking-apm-bin/agent/skywalking-agent.jar"。這裡就使用到了java agent技術。
Java agent 是什麼?
Java agent是java指令的一個參數。參數 javaagent 可以用于指定一個 jar 包。
- 這個 jar 包的 MANIFEST.MF 檔案必須指定 Premain-Class 項。
- Premain-Class 指定的那個類必須實作 premain() 方法。
當Java 虛拟機啟動時,在執行 main 函數之前,JVM 會先運作-javaagent所指定 jar 包内 Premain-Class 這個類的 premain 方法 。
如何使用java agent?
使用 java agent 需要幾個步驟:
- 定義一個 MANIFEST.MF 檔案,必須包含 Premain-Class 選項,通常也會加入Can-Redefine-Classes 和 Can-Retransform-Classes 選項。
- 建立一個Premain-Class 指定的類,類中包含 premain 方法,方法邏輯由使用者自己确定。
- 将 premain 的類和 MANIFEST.MF 檔案打成 jar 包。
- 使用參數 -javaagent: jar包路徑 啟動要代理的方法。
4.1.1 搭建java agent工程
使用maven建立java_agent_demo工程:
在java檔案夾下建立PreMainAgent類:
import java.lang.instrument.Instrumentation;
public class PreMainAgent {
/**
* 在這個 premain 函數中,開發者可以進行對類的各種操作。
* 1 、agentArgs 是 premain 函數得到的程式參數,随同 “– javaagent”一起傳入。與 main
函數不同的是,
* 這個參數是一個字元串而不是一個字元串數組,如果程式參數有多個,程式将自行解析這個字元串。
* 2 、Inst 是一個 java.lang.instrument.Instrumentation 的執行個體,由 JVM 自動傳入。*
* java.lang.instrument.Instrumentation 是 instrument 包中定義的一個接口,也是這
個包的核心部分,
* 集中了其中幾乎所有的功能方法,例如類定義的轉換和操作等等。
* @param agentArgs
* @param inst
*/
public static void premain(String agentArgs, Instrumentation inst) {
System.out.println("=========premain方法執行 1 ========");
System.out.println(agentArgs);
}
/**
* 如果不存在 premain(String agentArgs, Instrumentation inst)
* 則會執行 premain(String agentArgs)
* @param agentArgs
*/
public static void premain(String agentArgs) {
System.out.println("=========premain方法執行 2 ========");
System.out.println(agentArgs);
}
}
類中提供兩個靜态方法,方法名均為premain,不能拼錯。
在pom檔案中添加打包插件:
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<appendAssemblyId>false</appendAssemblyId>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<!--自動添加META-INF/MANIFEST.MF -->
<manifest>
<addClasspath>true</addClasspath>
</manifest>
<manifestEntries>
<Premain-Class>PreMainAgent</Premain-Class>
<Agent-Class>PreMainAgent</Agent-Class>
<Can-Redefine-Classes>true</Can-Redefine-Classes>
<Can-Retransform-Classes>true</Can-Retransform-Classes>
</manifestEntries>
</archive>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
該插件會在自動生成META-INF/MANIFEST.MF檔案時,幫我們添加agent相關的配置資訊。
使用maven的package指令進行打包:
打包成功之後,複制打包出來的jar包位址。
4.1.2 搭建主工程
使用maven建立java_agent_user工程:
Main類代碼:
public class Main {
public static void main(String[] args) {
System.out.println("Hello World");
}
}
先運作一次,然後點選編輯MAIN啟動類:
在VM options中添加代碼:
代碼為
-javaagent:路徑\java-agent-demo-1.0-SNAPSHOT.jar=HELLOAGENT
啟動時加載javaagent,指向上一節中編譯出來的java agent工程jar包位址,同時在最後追加參數HELLOAGENT。
運作MAIN方法,檢視結果:
可以看到java agent的代碼優先于MAIN函數的方法運作,證明java agent運作正常。
4.1.3 統計方法調用時間
Skywalking中對每個調用的時長都進行了統計,這一小節中我們會使用ByteBuddy和Java agent技術來統計方法的調用時長。
Byte Buddy是開源的、基于Apache 2.0許可證的庫,它緻力于解決位元組碼操作和instrumentation API的複雜性。Byte Buddy所聲稱的目标是将顯式的位元組碼操作隐藏在一個類型安全的領域特定語言背後。通過使用Byte Buddy,任何熟悉Java程式設計語言的人都有望非常容易地進行位元組碼操作。ByteBuddy提供了額外的API來生成Java agent,可以輕松的增強我們已有的代碼。
添加依賴:
<dependencies>
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>1.9.2</version>
</dependency>
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy-agent</artifactId>
<version>1.9.2</version>
</dependency>
</dependencies>
修改PreMainAgent代碼:
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.matcher.ElementMatchers;
import net.bytebuddy.utility.JavaModule;
import java.lang.instrument.Instrumentation;
public class PreMainAgent {
public static void premain(String agentArgs, Instrumentation inst) {
//建立一個轉換器,轉換器可以修改類的實作
//ByteBuddy對java agent提供了轉換器的實作,直接使用即可
AgentBuilder.Transformer transformer = new AgentBuilder.Transformer() {
public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader, JavaModule javaModule) {
return builder
// 攔截任意方法
.method(ElementMatchers.<MethodDescription>any())
// 攔截到的方法委托給TimeInterceptor
.intercept(MethodDelegation.to(MyInterceptor.class));
}
};
new AgentBuilder // Byte Buddy專門有個AgentBuilder來處理Java Agent的場景
.Default()
// 根據包名字首攔截類
.type(ElementMatchers.nameStartsWith("com.agent"))
// 攔截到的類由transformer處理
.transform(transformer)
// 将配置安裝到Instrumentation
.installOn(inst);
}
}
先生成一個轉換器,ByteBuddy提供了java agent專用的轉換器。通過實作Transformer接口利用builder對象來建立一個轉換器。轉換器可以配置攔截方法的格式,比如用名稱,本例中攔截所有方法,并定義一個攔截器類MyInterceptor。
建立完攔截器之後可以通過Byte Buddy的AgentBuilder建造者來建構一個agent對象。AgentBuilder可以對指定的包名字首來生效,同時需要指定轉換器對象。
MyInterceptor類:
import net.bytebuddy.implementation.bind.annotation.Origin;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import net.bytebuddy.implementation.bind.annotation.SuperCall;
import java.lang.reflect.Method;
import java.util.concurrent.Callable;
public class MyInterceptor {
@RuntimeType
public static Object intercept(@Origin Method method, @SuperCall Callable<?> callable) throws Exception {
long start = System.currentTimeMillis();
try {
//執行原方法
return callable.call();
} finally {
//列印調用時長
System.out.println(method.getName() + ":" +
(System.currentTimeMillis() - start) + "ms");
}
}
}
MyInterceptor就是一個攔截器的實作,統計的調用的時長。參數中的method是反射出的方法對象,而callable就是調用對象,可以通過callable.call()方法來執行原方法。
重新打包,執行maven package指令。接下來修改主工程代碼。主工程将Main類放置到com.agent包下。修改代碼内容為:
package com.agent;
public class Main {
public static void main(String[] args) {
try {
Thread.sleep( 1000 );
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Hello World");
}
}
休眠 1 秒,使統計時長的示範效果更好一些。執行main方法之後顯示結果:
我們在沒有修改代碼的情況下,利用java agent和Byte Buddy統計出了方法的時長,Skywalking的agent也是基于這些技術來實作統計調用時長。
4.2 Open Tracing介紹
OpenTracing通過提供平台無關、廠商無關的API,使得開發人員能夠友善的添加(或更換)追蹤系統的實作。OpenTracing中最核心的概念就是Trace。
4.2.1 Trace的概念
在廣義上,一個trace代表了一個事務或者流程在(分布式)系統中的執行過程。在OpenTracing标準中,trace是多個span組成的一個有向無環圖(DAG),每一個span代表trace中被命名并計時的連續性的執行片段。
例如用戶端發起的一次請求,就可以認為是一個Trace。将上面的圖通過Open Tracing的語義修改完之後做可視化,得到下面的圖:
圖中每一個色塊其實就是一個span。
4.2.2 Span的概念
一個Span代表系統中具有開始時間和執行時長的邏輯運作單元。span之間通過嵌套或者順序排列建立邏輯因果關系。
Span裡面的資訊包括:操作的名字,開始時間和結束時間,可以附帶多個 key:value 構成的 Tags(key必須是String,value可以是 String, bool 或者數字),還可以附帶 Logs 資訊(不一定所有的實作都支援)也是 key:value形式。
下面例子是一個 Trace,裡面有 8 個 Span:
[Span A] ←←←(the root span)
|
+------+------+
| |
[Span B] [Span C] ←←←(Span C 是 Span A 的孩子節點, ChildOf)
| |
[Span D] +---+-------+
| |
[Span E] [Span F] >>> [Span G] >>> [Span H]
↑
↑
↑
(Span G 在 Span F 後被調用, FollowsFrom)
一個span可以和一個或者多個span間存在因果關系。OpenTracing定義了兩種關系:ChildOf 和FollowsFrom。這兩種引用類型代表了子節點和父節點間的直接因果關系。未來,OpenTracing将支援非因果關系的span引用關系。(例如:多個span被批量處理,span在同一個隊列中,等等)
ChildOf 很好了解,就是父親 Span 依賴另一個孩子 Span。比如函數調用,被調者是調用者的孩子,比如說 RPC 調用,服務端那邊的Span,就是 ChildOf 用戶端的。很多并發的調用,然後将結果聚合起來的操作,就構成了 ChildOf 關系。
如果父親 Span 并不依賴于孩子 Span 的傳回結果,這時可以說它他構成 FollowsFrom 關系。
如圖所示,左邊的每一條追蹤代表一個Trace,而右邊時序圖中每一個節點就是一個Span。
4.2.3 Log的概念
每個span可以進行多次Logs操作,每一次Logs操作,都需要一個帶時間戳的時間名稱,以及可選的任意大小的存儲結構。
如下圖是一個異常的Log:
如下圖是兩個正常資訊的Log,它們都帶有時間戳和對應的事件名稱、消息内容。
4.2.4 Tags的概念
每個span可以有多個鍵值對(key:value)形式的Tags,Tags是沒有時間戳的,支援簡單的對span進行注解和補充。
如下圖就是一個Tags的詳細資訊,其中記錄了資料庫通路的SQL語句等内容。