安裝
下載地址:https://skywalking.apache.org/downloads/
由於本地的ES是7.15.1版本 所有需要選擇對應版本的
本地需要啟動ES。ES安裝可查看之前的博客:ES安裝
配置ES啟動
- 進入解壓后的skywalking目錄:
cd apache-skywalking-apm-bin-es7
# 修改config里面的
- 修改config目錄下的
application.yml
文件
默認使用的是h2存儲數據,修改為es7
selector: $
- 進入bin目錄啟動
nohup nice sh startup.sh &
-
打開 webapp/webapp.yml文件 查看UI訪問端口
Agent
agent探針
可以讓我們不修改代碼的情況下,對java應用上使用到的組件進行動態監控,獲取運行數據發送到OAP上進行統計和存儲。agent探針在java中是使用java agent技術實現的,不需要更改任何代碼,java agent會通過虛擬機(VM)接口來在運行期更改代碼。
進入 agent 目錄
cd ~/tools/apache-skywalking-apm-bin-es7/agent/
目錄 | 作用 |
---|---|
config | 配置文件 |
plugins | 使用的插件 |
optional-plugins | 可選的插件 如果需要使用,放入到plugins就會生效 |
項目集成
可查看官方文檔: https://skywalking.apache.org/docs/main/v8.7.0/en/setup/service-agent/java-agent/readme/
修改應用名:打開agent/config目錄,編輯agent_config文件
# The service name in UI
agent.service_name=${SW_AGENT_NAME:Your_ApplicationName}
Tomcat
編輯 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
SpringBoot
IDEA配置VM參數
-javaagent:/Users/mpy/tools/apache-skywalking-apm-bin-es7/agent/skywalking-agent.jar
或者jar命令啟動
java -javaagent:/path/to/skywalking-agent/skywalking-agent.jar -jar yourApp.jar
需要等待一下才能看到數據
常用插件
配置覆蓋
本地多個項目 如何都使用同一份agent配置不同的項目名稱呢?
可以使用Skywalking提供的配置覆蓋功能通過啟動命令動態指定服務名,這樣agent只需要部署一份即可。Skywalking支持的幾種配置方式:
- 系統配置(System properties)
使用skywalking. + 配置文件中的配置名
作為系統配置項來進行覆蓋.
-Dskywalking.agent.service_name=skywalking_mysql
- 探針配置(Agent options)
-javaagent:/path/to/skywalking-agent.jar=[option1]=[value1],[option2]=[value2]
案例:
-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配置項如下所示:
agent.service_name=$
可以在環境變量中設置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 &
獲取追蹤ID
- 添加Jar包 和本地skywalking版本保持一致
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-trace</artifactId>
<version>8.7.0</version>
</dependency>
- 編寫測試類
//獲取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();
}
- 測試
過濾指定的端點
項目中的一些接口是不需要監控指標的,比如Swagger相關的端點,需要進行過濾
-
添加可選插件
apm-trace-ignore-plugin-8.7.0.jar
到 plugins目錄中 -
編寫測試類
//此接口可以被追蹤
@GetMapping("/include")
public String include(){
return "include";
}
//此接口不可被追蹤
@GetMapping("/exclude")
public String exclude(){
return "exclude";
}
- 添加啟動參數
- Dskywalking.trace.ignore_path=/exclude
-
測試 include接口可以被監控,exclude無法被監控
-
匹配多路徑
這里添加-Dskywalking.trace.ignore_path=/exclude參數來標識需要過濾哪些請求,支持 Ant Path 表達式:/path/* , /path/** , /path/?
- ? 匹配任何單字符
- * 匹配0或者任意數量的字符
- ** 匹配0或者更多的目錄
告警功能
Skywalking每隔一段時間根據收集到的鏈路追蹤的數據和配置的告警規則(如服務響應時間、服務響應時間百分比)等,判斷如果達到閾值則發送相應的告警信息。發送告警信息是通過調webhook接口完成,具體的webhook接口可以使用者自行定義,從而開發者可以在指定的webhook接口中編寫各種告警方式,比如郵件、短信等。告警的信息也可以在RocketBot中查看到。
默認的告警規則配置,位於skywalking安裝目錄下的config文件夾下 alarm-settings.yml
文件中
規則屬性說明
屬性 | 含義 |
---|---|
metrics-name | oal腳本中的度量名稱 |
threshold | 閾值,與metrics-name和下面的比較符號相匹配 |
op | 比較操作符,可以設定>,<,= |
period | 多久檢查一次當前的指標數據是否符合告警規則,單位分鍾 |
count | 達到多少次后,發送告警消息 |
silence-period | 在多久之內,忽略相同的告警消息 |
message | 告警消息內容 |
include-names | 本規則告警生效的服務列表 |
webhooks可以配置告警產生時的調用地址。
webhooks:
# - http://127.0.0.1/notify/
# - http://127.0.0.1/go-wechat/
具體信息可以查看官方文檔:https://skywalking.apache.org/docs/main/latest/en/setup/backend/backend-alarm/#webhook
- 配置文件修改
webhooks:
- http://127.0.0.1:8801/webhook
- 接收告警信息 自行處理
產生告警時會調用webhook接口,該接口必須是Post類型,同時接口參數使用RequestBody。參數格式為
[{
"scopeId": 1,
"scope": "SERVICE",
"name": "serviceA",
"id0": "12",
"id1": "",
"ruleName": "service_resp_time_rule",
"alarmMessage": "alarmMessage xxxx",
"startTime": 1560524171000,
"tags": [{
"key": "level",
"value": "WARNING"
}]
}]
private List<AlarmMessage> lastList = new ArrayList<>();
@PostMapping("/webhook")
public void webhook(@RequestBody List<AlarmMessage> alarmMessageList){
lastList = alarmMessageList;
}
java agent原理
上文中我們知道,要使用Skywalking去監控服務,需要在其 VM 參數中添加 -javaagent:/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 包內PremainClass 這個類的 premain 方法 。
如何使用java agent
使用 java agent 需要幾個步驟:
- 定義一個 MANIFEST.MF 文件,必須包含 Premain-Class 選項,通常也會加入Can-RedefineClasses 和 Can-Retransform-Classes 選項。
- 創建一個Premain-Class 指定的類,類中包含 premain 方法,方法邏輯由用戶自己確定。
- 將 premain 的類和 MANIFEST.MF 文件打成 jar 包。
- 使用參數 -javaagent: jar包路徑 啟動要代理的方法。
自定義Agent
Skywalking中對每個調用的時長都進行了統計,我們可以使用ByteBuddy
和Java agent
技術來模擬實現skywalking統計方法的調用時長
- 新建maven項目 agent
<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>
- 編寫PreMain方法
PreMainAgent類 通過MyInterceptor 插入業務邏輯
public static void premain(String agentArgs, Instrumentation inst) {
//創建一個轉換器,轉換器可以修改類的實現
//ByteBuddy對java agent提供了轉換器的實現,直接使用即可
AgentBuilder.Transformer transformer = (builder, typeDescription, classLoader, javaModule) -> builder
// 攔截任意方法
.method(ElementMatchers.any())
// 攔截到的方法委托給TimeInterceptor
.intercept(MethodDelegation.to(MyInterceptor.class));
new AgentBuilder // Byte Buddy專門有個AgentBuilder來處理Java Agent的場景
.Default()
// 根據包名前綴攔截類
.type(ElementMatchers.nameStartsWith("com.agenttest"))
// 攔截到的類由transformer處理
.transform(transformer)
.installOn(inst);
}
- 編寫攔截方法
@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");
}
}
- 指定build方法
<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>com.PreMainAgent</Premain-Class>
<Agent-Class>com.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>
- 新建測試項目agent-test
public class App
{
public static void main( String[] args )
{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println( "Hello World!" );
}
}
啟動時指定javaagent為上面的項目打包生成的jar
- 測試 可以發現運行時自動執行了agent項目里面的方法
Open Tracing介紹
OpenTracing通過提供平台無關、廠商無關的API,使得開發人員能夠方便的添加(或更換)追蹤系統的實現。OpenTracing中最核心的概念就是Trace
。
Trace
在廣義上,一個trace代表了一個事務或者流程在(分布式)系統中的執行過程。在OpenTracing標准中,trace是多個span組成的一個有向無環圖(DAG)
,每一個span代表trace中被命名並計時的連續性的執行片段。
圖中每一個色塊其實就是一個span。
Span
一個Span代表系統中具有開始時間和執行時長的邏輯運行單元。span之間通過嵌套或者順序排列建立邏輯因果關系。
Span里面的信息包括:操作的名字,開始時間和結束時間,可以附帶多個 key:value 構成的Tags(key必須是String,value可以是 String, bool 或者數字),還可以附帶 Logs
信息(不一定所有的實現都支持)
一個span可以和一個或者多個span間存在因果關系。OpenTracing定義了兩種關系: ChildOf
和 FollowsFrom
。這兩種引用類型代表了子節點和父節點間的直接因果關系。未來,OpenTracing將支持非因果關系的span引用關系。(例如:多個span被批量處理,span在同一個隊列中,等等)
- ChildOf 很好理解,就是父親 Span 依賴另一個孩子 Span。比如函數調用,被調者是調用者的孩子,比如說 RPC 調用,服務端那邊的Span,就是 ChildOf 客戶端的。很多並發的調用,然后將結果聚合起來的操作,就構成了 ChildOf 關系。
- 如果父親 Span 並不依賴於孩子 Span 的返回結果,這時可以說它他構成 FollowsFrom 關系。
如圖所示,左邊的每一條追蹤代表一個Trace,而右邊時序圖中每一個節點就是一個Span。
Log的概念
每個span可以進行多次Logs操作,每一次Logs操作,都需要一個帶時間戳的時間名稱,以及可選的任意大小的存儲結構。
Tags的概念
每個span可以有多個鍵值對(key:value)形式的Tags,Tags是沒有時間戳的,支持簡單的對span進行注解和補充。
如下圖就是一個Tags的詳細信息,其中記錄了數據庫訪問的SQL語句等內容。