在今天的文章里,我們將介紹Elastic的一個重要的應用:應用程序性能管理(Application Performance Monitoring/Management),簡稱APM。那么到底什么是APM呢?
隨着時代的發展,我們的IT架構越來越復雜,比如:
我們系統的服務器越來越多,而且更多的設備都部署在雲端。復雜的系統甚至有成千個微服務及架構所組成,那么我們的業務請求可能需要一個或更多的服務共同來完成。那么現在的問題是,如果我們的請求變得很慢,我們想知道到底是哪個環節出現問題了呢?經驗豐富的程序員或者系統設計者,可能從一些log里找到答案。可是當我們的log變得非常大的時候,而且我們的接口也越來越多時,這個時候,也許我們也無能為力。
當我們在設計頁面或者請求時,經常會遇到上面的這種等待的情況。可能有個別的工具能有效地解決部分的問題,但是如何能從整個的系統里來完成這種問題的定位及分析。Elastic推出的APM解決方案可以完美地解決這些問題。為我們的系統設計者或程序員提供了一個快速定位的方法。
APM 到底是什么呢?
我們先來看一下如下的這個圖:
如上圖所示,在不同時刻我們請求時,我們發現為什么在17:36:30發生的一個請求需要花將近8秒的時間,而另外在17:36:30分發生的一個請求卻返回一個錯誤的代碼?
Elastic APM方案是世界上第一個開源的APM 解決方案:
- APM記錄數據庫查詢,外部HTTP請求以及對應用程序的請求期間發生的其他緩慢操作的跟蹤
- 很容易讓程序員看到應用在運行時各個部分所花的時間
- 它收集未處理的錯誤和異常
- 很容讓程序員調試錯誤
- 在客戶面對性能瓶頸和錯誤之前先定位到問題所在
- 提高開發團隊的生產力
APM適用於Elastic Stack的位置
APM如何把數據存於Elasticsearch中,並提供分析呢?我們看一下如下的架構圖:
如上圖所示,我們看到一個最典型的APM架構圖:
- 我們需要架設一台專門的APM服務器,雖然也可以和Elastic Stack的其它服務器處於同一台服務器中
- AMP agent專門收集數據並發送數據到APM服務器中。這里的APM agents包含:
- APM服務器把數據發送到Elasticsearch中,並進行數據分析
- Kibana可以幫我們把數據進行展示並顯示在Dashboard之中
總體來說,APM數據僅僅是另外Elasticsearch索引。在Kibana中已經有一個現成的APM應用可以被我們所使用。我們也可以根據需求自己定制自己的Dashboard。APM可以完美地結合機器學習和告警。
APM術語
- Service: 在apm agent配置中進行設置,以將特定的apm agent組標識為單個服務,這是一種邏輯上標識一組事務的方法
- Transaction: 組成一個服務的請求和響應,例如 登錄api調用,每個調用由單獨的span組成。
- Span: 事務中的單個事件,例如方法調用,數據庫查詢或緩存插入或檢索,即需要花費時間才能完成的任何事件。
- Erorrs:具有匹配的異常或日志消息的異常組
它們之間的關系可以用如下的圖來表示:
分布式tracing:
例子
在今天的練習中,我們將以Java Spring boot為例來展示如何使用Elastic APM。
下載Spring boot代碼
首先,我們在terminal中打入如下的命令:
git clone https://github.com/liu-xiao-guo/elastic-apm-demo
上面的一個例子是一個簡單的Spring boot應用。它有一下的幾個特點:
- 它可以REST接口訪問MySQL的數據庫進行添加數據,請求數據
- 它可以通過REST接口進行訪問百度天氣接口來獲得天氣數據
下面是它的部分代碼:
@PostMapping(path="/add") // Map ONLY POST Requests
public @ResponseBody String addNewUser (@RequestParam String name
, @RequestParam String email) {
// @ResponseBody means the returned String is the response, not a view name
// @RequestParam means it is a parameter from the GET or POST request
User n = new User();
n.setName(name);
n.setEmail(email);
userRepository.save(n);
return "Saved";
}
@GetMapping(path="/all")
public @ResponseBody Iterable<User> getAllUsers() {
// This returns a JSON or XML with the users
return userRepository.findAll();
}
@GetMapping(path="/weather")
public @ResponseBody String getBaiduWeather() throws InterruptedException {
// Add some random delays before getting the info
double delay = Math.random() * 10;
System.out.println("delay: " + delay);
TimeUnit.SECONDS.sleep((long)delay);
String weather = getWeatherInform("北京");
return weather;
}
在獲得天氣(weather)的接口中,我故意加入了一下隨機數的延遲,這樣來模擬每一次請求的時間是不同的。
我們可以在應用的根目錄下打入如下的命令:
./mvnw clean package
這樣它將會在當前目錄下的target子目錄下生產一個叫做accessing-data-mysql-0.0.1-SNAPSHOT.jar的文件。
$ ls ./target/accessing-data-mysql-0.0.1-SNAPSHOT.jar
./target/accessing-data-mysql-0.0.1-SNAPSHOT.jar
我們可以把這個文件拷入到我們想要的任何一個目錄中。針對我的情況,我把它拷入到我的home目錄下的data/apm目錄中。
$ pwd
/Users/liuxg/data/apm
liuxg-2:apm liuxg$ ls accessing-data-mysql-0.0.1-SNAPSHOT.jar
accessing-data-mysql-0.0.1-SNAPSHOT.jar
安裝MySQL
我們可以按照文檔的需求來安裝我們的MySQL。我們在一個terminal中打入如下的命令:
mysql -uroot -p
我們打入root用戶的密碼進入到MySQL之中。為了創建一個數據庫,我們在MySQL的prompt中打入如下的命令:
mysql> create database db_example; -- Creates the new database
mysql> create user 'springuser'@'%' identified by 'ThePassword'; -- Creates the user
mysql> grant all on db_example.* to 'springuser'@'%'; -- Gives all privileges to the new user on the newly created database
上面的命令創建了一個叫做db_example的數據庫。同時,它也創建了一個叫做springuser的用戶及其密碼ThePassword。我們可以通過Navicat工具來查看:
運行Elastic Stack
安裝及運行我們的Elasticsearch及Kibana。我們打開我們的Kibana界面,並點擊左上角的部分:
然后,我們按照上面的步驟一步一步地進行安裝:
上面的步驟非常詳細。對於APM agent的選擇來講,因為我們是Java應用,所以我們選擇Java agent。我們下載相應的agent jar文件,並存放於我們上面放置spring boot的jar文件所處的文件夾。針對我的情況是home目錄下的data/apm。
$ pwd
/Users/liuxg/data/apm
liuxg-2:apm liuxg$ ls elastic-apm-agent-1.10.0.jar
elastic-apm-agent-1.10.0.jar
在這個時候,我們可以開始運行我們的Spring Java應用了。我們可以通過如下的命令來運行:
java -javaagent:./elastic-apm-agent-1.10.0.jar \
-Delastic.apm.service_name=sample_apm \
-Delastic.apm.server_url=http://localhost:8200 \
-Delastic.apm.secret_token= \
-Delastic.apm.application_packages=accessing-data-mysql \
-jar accessing-data-mysql-0.0.1-SNAPSHOT.jar
注意:這里的sample_apm是我給取的一個服務名稱。你可以根據自己的需求取一個獨特的名字。如果你不想這么麻煩,你可以在當前的目錄下生產一個叫做elasticapm.properties的文件。它的內容如下:
service_name=sample_apm
application_packages=accessing-data-mysql
server_url=http://localhost:8200
那么我們可以通過如下的命令來運行:
java -javaagent:./elastic-apm-agent-1.10.0.jar \
-Delastic.apm.secret_token= \
-jar accessing-data-mysql-0.0.1-SNAPSHOT.jar
等我們的Spring Boot應用完全起來后,我們點擊Kibana中的“Check agent status”按鈕。這個時候可能顯示沒有任何的數據。我們可以打開我們的瀏覽器,並在瀏覽器的地址欄中輸入如下的地址:
我們可以看到我們得到了一下天氣的數據信息。那么這個時候我們可以在Agent status中看到信息:
啟動APM應用
如果你已經運行到這里,那么你基本上已經把整個的環境運行起來了。我們可以在terminal中打入如下的命令:
curl localhost:8080/demo/add -d name=First -d email=someemail@someemailprovider.com
上面的應用是向我們的數據中寫入一條記錄。
curl 'localhost:8080/demo/all'
運行上面的命令可以展示已經輸入的所有的記錄
curl 'localhost:8080/demo/weather'
運行上面的命令可以獲得百度天氣API接口所帶給我們的天氣信息。
上面的所有的信息我們都可以在瀏覽器中的地址欄中輸入。
點擊Kibana中的APM應用圖標:
在上面我們可以看到應用的四個接口的統計情況。
我們在這個APM應用的dashboard上可以看到我們所有的API的調用情況。比如:
因為在我的應用中,我故意加入了一些延遲,所以導致我們的整個getBaiduWeather的請求時間為9.157秒才完成,而api.map.baidu.com的時間只有149ms。
到這里我的講解就完成了。剩下的留給大家自己去挖掘哈!
參考:
【1】Accessing data with MySQL(https://spring.io/guides/gs/accessing-data-mysql/)