作者:Jax
前言
在應用實際的運維過程中,我們需要更多的日志和監(jiān)控來讓我們對自己的應用程序的運行狀況有一個全方位的了解。然而對于大部分開發(fā)者而言,平時大家所關注的更多的是如何更優(yōu)雅的實現(xiàn)業(yè)務,或者是如何讓應用的響應速度更快等等與編碼相關的技術,對于應用程序的監(jiān)控,可能還停留在日志文件的層面,而且大多數(shù)是出了事故被人為發(fā)現(xiàn)后,才通過日志嘗試去定位問題。
本文所準備介紹的Elastic
APM是一套用于監(jiān)控應用各項指標,比如系統(tǒng)響應時間、異常、EF執(zhí)行的SQL記錄等等,并且可以將這些記錄組織成一個可追溯的鏈路,方便查詢問題。此外,Elastic
APM還可以通過Kibana來做非常漂亮的可視化展示,方便我們定位和發(fā)現(xiàn)問題。
廢話不再多說,我們開始實戰(zhàn)~
Elastic APM介紹
Elastic APM的由下面四個組件所組成,如下圖:
APM Agent
APM Agent是安裝到你的.NET Core程序中的一個Nuget包,他用于性能、錯誤等各類數(shù)據的收集,并將收集到的數(shù)據緩存起來分批發(fā)送到APM
Server。當然,除了.NET Core使用的Nuget包,他還可以支持很多其他的語言,比如Java,Node.Js,Python等
支持的語言列表請參考這里:https://www.elastic.co/guide/en/apm/agent/index.html
<https://www.elastic.co/guide/en/apm/agent/index.html>
APM Server
APM Server是部署在服務器端的一個用于接收Agent發(fā)來的數(shù)據包的應用程序,并根據這些數(shù)據包自動創(chuàng)建文檔,將數(shù)據轉存到Elastic Server中。
Elastic Search
這個相信大家都很熟悉了,他就是一個基于Lucene實現(xiàn)的高性能、分布式的全文搜索引擎,用于快速、實時的存儲、搜索和分析大量數(shù)據。在這里來說,他提供的是數(shù)據存儲和搜索能力!
Kibana
如果你熟悉Elastic Search,那么你一定多少會了解Kibana,Kibana是開源的分析和可視化平臺,他能與Elastic
Search進行很好的協(xié)同,幫助你快速的可視化存儲在Elastic Search中的數(shù)據,并做成各種各樣漂亮的報表、圖形等。
環(huán)境準備
在本次的實戰(zhàn)過程中,我們需要以下的東西:
* Elastic Search
* Kibana
* APM Server
* 一個基于.NET Standard 2.0 + 的項目
Elastic Search的安裝:https://www.cnblogs.com/baiyunchen/p/11227144.html
<https://www.cnblogs.com/baiyunchen/p/11227144.html>
Kibana的安裝:
我的環(huán)境是Centos 7,所以照著https://www.elastic.co/guide/en/kibana/7.3/rpm.html
<https://www.elastic.co/guide/en/kibana/7.3/rpm.html>?這個官網教程安裝的,整個過程很簡單:
* 下載Kibana RPM包(采用這種方式是因為用yum install網速太慢,所以我用迅雷下載完成rpm文件后上傳到Linux機器中)
* 執(zhí)行命令rpm --install? “下載的文件名” 進行安裝
* 安裝完成后,到/etc/kibana/kibana.yml文件中在文件末尾增加以下配置: server.host: 0.0.0.0
server.name: 主機IP server.port: 一個你喜歡的端口號 elasticsearch.hosts:
["已安裝好的ES地址,多個之間用逗號隔開"] logging.dest: /var/log/kibana.log //需要提前把這個文件創(chuàng)建好,然后把權限給夠
* 將Kibana安裝為系統(tǒng)服務并啟動 sudo /bin/systemctl daemon-reload sudo /bin/systemctl
enable kibana.service sudo systemctl start kibana.service
這里大家一定要注意Elastic
Search的版本和Kibana一定要匹配,不然會報錯的。(我的ES是前段時間裝的,所以會有這問題,如果大家一口氣安裝所有的,應該沒啥問題)
如果不幸遇到了問題,可以通過配置文件中l(wèi)ogging.dest中配置的路徑查看日志。
APM Server的安裝
APM Server的安裝跟Kibana的安裝類似,過程如下:
* 下載RPM包,包在這個頁面找你需要的版本,也需要跟ES、Kibana的版本一致,不然你懂得~?
https://www.elastic.co/cn/downloads/past-releases#apm-server
<https://www.elastic.co/cn/downloads/past-releases#apm-server>
* 執(zhí)行rpm --install “下載的文件名”進行安裝
* 在文件夾/etc/amp-server中修改配置文件apm-server.yml,將配置文件最開始的host:
“l(fā)ocalhost:8200”修改成“0.0.0.0:8200”,以便讓他能允許通過ip:端口號的方式訪問, 并在配置的最后面添加如下配置:
output.elasticsearch: hosts: ["已安裝好的ES地址,多個之間用逗號隔開"]
* 將apm-server安裝為系統(tǒng)服務并啟動 sudo /bin/systemctl daemon-reload sudo /bin/systemctl
enable apm-server.service sudo systemctl start apm-server.service
執(zhí)行上述操作完成后,在瀏覽器中嘗試打開服務器Ip:8200,最終如果APM Server安裝的沒有問題,則瀏覽器中會打印出類似于如下的內容:
{ "build_date": "2019-06-20T14:39:23Z", "build_sha":
"9a099b63c53eac8c55707df96193143ec66337e9", "version": "7.2.0" }
此時我們在瀏覽器中打開Kibana,然后點擊Add APM
然后將新打開的頁面往下滾動,點擊Check APM Server Status按鈕,如果出現(xiàn)You have correctly setup APM
Server則說明安裝完成~
到這里為止,我們的安裝工作就全部完成了,接下來,我們嘗試將.NET Core與Elastic APM集成起來,一起繼續(xù)吧~
.NET Core 應用集成
我們創(chuàng)建一個Demo項目,來用于測試APM的各項功能。
項目的地址請參考GitHub:
引用依賴包
我們需要從Nuget引用相關的SDK來與我們的應用做集成,其實就是引用我們最開始說的APM Agent的部分,在Nuget中,我們引用
Elastic.Apm.NetCoreAll這個包。
依賴這個包其實相當于自動依賴了如下三個包,你也可以根據需要只依賴其中的一部分。
* Elastic.Apm <https://www.nuget.org/packages/Elastic.Apm/>
* Elastic.Apm.AspNetCore
<https://www.nuget.org/packages/Elastic.Apm.AspNetCore/>
* Elastic.Apm.EntityFrameworkCore
<https://www.nuget.org/packages/Elastic.Apm.EntityFrameworkCore/>
這里我們?yōu)榱撕唵纹鹨?,直接印用Elastic.Apm.NetCoreAll這個包
將Agent添加到.NET Core
找到.NET Core的StartUp文件,在里面的Configure方法中添加如下代碼:
public void Configure(IApplicationBuilder app, IHostingEnvironment env) {
app.UseAllElasticApm(Configuration); }
然后在application.json中添加如下內容:
{ "ElasticApm": { "LogLevel": "Error", // Log級別,根據自己的需要來定"ServerUrls":
"http://localhost:8200", //設置前面安裝好的APM Server URL,默認端口號是8200 "ServiceName" :
"MyApp", //應用的名字,跟著實際情況起就行,allowed characters: a-z, A-Z, 0-9, -, _, and space.
Default is the entry assembly of the application } }
此時我們將項目啟動起來,隨便的刷新幾下,然后回到Kibana中,在剛才的頁面中往下滾動,選擇.NET,然后點擊Check Agent
Status按鈕,如果順利,就會顯示“Data successfully received from one or more
agents”,如果不幸沒能顯示這句話,可以通過VS的Diagnostic Tools中的Event跟蹤一下,看看是不是哪里沒有配置對
?
?
監(jiān)控數(shù)據查看
在Kibana的Add APM頁面的最下方,找到Load Kibana Objects,來創(chuàng)建索引,然后點擊APM
dashboard按鈕,就可以進入APM數(shù)據的查看頁面。
?
點擊APM Dashboard按鈕后,展示的頁面如下:
該頁面中的功能分為Services、Traces兩個大的功能模塊,先來簡單了解一下這兩個Tab頁中對應的功能。
Services
下面的列表中顯示的XianDotnetCommunity其實就是你在配置文件中配置的ServiceName,點擊這個名字進入,又可以看到如下的報表,里面有Transactions,Errors,Metrics三個Tab頁。
?
?
其中
Transactions:展示的當前應用請求情況的概覽,包括請求響應時長、請求調用次數(shù)等等
Errors:程序中的異常列表
Metrics:應用程序所在機器的CPU/內存使用情況
PS:其實我覺得非常需要一個當前應用程序所消耗的內存和CPU的值,但是貌似.NET Core版本的代理沒有實現(xiàn)這些功能,期待未來的更新吧
Traces
里面是用于做鏈路追蹤的視圖,首頁包含所有事務的名稱列表以及響應時間等
點擊具體的事務進去,可以看到這個事務經過的鏈路列表以及更詳細的一些響應信息,從而幫你分析出整個鏈路中的瓶頸,更多內容我們在下面細講。
探索更多
Elastic APM還有很多其他的功能,比如鏈路追蹤、數(shù)據庫調用執(zhí)行,讓我們來一起探索吧~
監(jiān)控API調用鏈路追蹤
如果你了解過微服務架構,那你一定了解鏈路追蹤這個概念。那什么是鏈路追蹤呢?舉個栗子:
有個服務A,他會依賴服務B,C,而服務B又會依賴服務D,E,服務C又依賴F,G(省略無數(shù)依賴關系),然后有一天,服務A變得非常慢,那到底該怎么定位是哪個服務慢呢?此時鏈路最終就派上用場了~
我們來簡單模擬一下這種嵌套的調用:
在一個WebAPI項目Demo1中有一個ConsumerController,他里面有一個API A,里面調用了另外一個WEB
API項目Demo2中的接口B/C/D/E。代碼大致如下:
項目甲:
[Route("api/consumer")] [ApiController] public class ConsumerController :
ControllerBase { private readonly IHttpClientFactory _httpClientFactory; public
ConsumerController(IHttpClientFactory httpClientFactory) {
//使用HttpClientFactory時需要先在StartUp中調用services.AddHttpClient();
_httpClientFactory = httpClientFactory; } private const string baseUri =
"http://localhost:54597"; [HttpGet("a")] public async Task<string> A() {
//HttpClient client = new HttpClient(); var client =
_httpClientFactory.CreateClient(); Thread.Sleep(new Random().Next(1, 1500));
var b = await client.GetStringAsync($"{baseUri}/api/data-source/b"); var c =
await client.GetStringAsync($"{baseUri}/api/data-source/c"); var d = await
client.GetStringAsync($"{baseUri}/api/data-source/d"); var e = await
client.GetStringAsync($"{baseUri}/api/data-source/e"); return $"b= & c={c} &
d=eftfwi6vow7 & e={e}"; } }
項目乙:
[Route("api/data-source")] [ApiController] public class DataSourceController :
ControllerBase { [HttpGet("b")] public async Task<string> B() {
Thread.Sleep(new Random().Next(1, 1500)); return "B"; } [HttpGet("c")] public
async Task<string> C() { Thread.Sleep(new Random().Next(1, 1500)); return "C";
} [HttpGet("d")] public async Task<string> D() { Thread.Sleep(new
Random().Next(1, 1500)); return "D"; } [HttpGet("e")] public async Task<string>
E() { Thread.Sleep(new Random().Next(1, 1500)); return "E"; } }
此時我們請求Demo1中的API A (xxx/api/consumer/a),然后在Kibana中打開APM中的Traces,找到”GET
Consumer/A” 這條記錄(看起來默認是根據Controller的名字+Action的名字命名的),然后點擊查看詳情。
在詳情中的最下方,我們找到TimeLine,可以看到如下圖所示的圖形:
我們可以看到我們在請求API A時的時間分別花費在調用4個API中的時間,也可以看出調用第三個API花費的時間更長,點擊藍色的條可以看到請求的詳細信息。
這里不太好的一點是默認顯示的名字是GET localhost這樣的,其實我們更期望的是顯示成調用的api
uri對吧?這個我提了一個pr給他們,大家可以關注下:https://github.com/elastic/apm-agent-dotnet/pull/463
<https://github.com/elastic/apm-agent-dotnet/pull/463>
監(jiān)控EF執(zhí)行記錄
這個不需要過多的解釋,就是在EF執(zhí)行DB操作時,進行監(jiān)控,以便發(fā)現(xiàn)性能等問題,我的代碼大致如下:
[HttpGet("person")] public void TestEfCore() { using (var db = new
ApmDbContext()) { var jax = new Person { Name = "西安.NET社區(qū)", Age = 26, Remark =
"做最好的技術社區(qū)~" }; db.Persons.Add(jax); db.SaveChanges();
db.Persons.FirstOrDefault(x => x.Id == jax.Id ); db.Persons.FirstOrDefault(x =>
x.Name == "西安.NET社區(qū)"); jax.Name = ".NET西安社區(qū)"; db.SaveChanges();
db.Persons.Remove(jax); db.SaveChanges(); } }
當我們使用Kibana查看這次請求時,TimeLine顯示如下:
我們可以比較清晰直觀的看到在這次請求中,執(zhí)行了哪些SQL語句,各耗時多少,對我們的請求分析來說,還是蠻有用處的。點擊具體的藍條,還可以看到更詳細的數(shù)據,但比較遺憾的是,數(shù)據中并沒有記錄SQL
Params ,這對于我們想完全重現(xiàn)這次請求來說,還是不夠友好~
自行埋點記錄
相對來說,Elastic APM目前生態(tài)圈還不夠好,比sky walking還是稍微差一些組件的支持,如果要使用Elastic
APM,免不了自己去做一些性能數(shù)據的埋點記錄,或者在為第三方組件、類庫做支持時,也需要做一些數(shù)據的埋點。接下來我們就在我們的請求中,埋一些我們想額外記錄的信息,示例代碼如下:
[HttpGet] public void RecordMyApmData() { var transaction =
Agent.Tracer.CurrentTransaction; var span1 = transaction.StartSpan("Stage 1",
"Customize"); Thread.Sleep(300); span1.End(); Thread.Sleep(200); var span2 =
transaction.StartSpan("Stage 2", "Customize"); Thread.Sleep(100); span2.End();
Thread.Sleep(100); var span3 = transaction.StartSpan("Stage 3", "Customize");
Thread.Sleep(500); span3.End(); }
最終記錄的效果如下:
這個Demo雖然寫的很簡單,但是我相信你已經能大概腦補如何使用Elastic Apm Agent這個類去自定義自己需要捕捉的一些監(jiān)控數(shù)據了~
異常監(jiān)控
當我們的程序發(fā)生了異常時,Elastic APM能幫助你記錄,這個功能和日志差不多,但可能比日志稍微好用那么一點點。我們一起來看看吧~
示例代碼如下:
[HttpGet] public void TestException() { try { throw new Exception("捕獲的異常"); }
catch (Exception) { } throw new Exception("未捕獲的異常"); }
執(zhí)行代碼后,我們可以通過點擊Service Name,然后在Errors這個Tab頁中查看到這次的異常
點擊詳情,我們能看到詳細的堆棧調用信息:
此外,我們可以在Trasactions Tab中,找到發(fā)生異常的這個請求,然后點擊查看詳情,在詳情中我們也能看到這次異常的發(fā)生:
總結
本文介紹了如何使用Elastic APM在.NET Core應用中收集性能和異常數(shù)據,并使用Kibana進行可視化分析,整體來說,Elastic
APM還是挺強大的,對于性能監(jiān)控、鏈路追蹤、異常監(jiān)控基本是夠用了。
目前來說,Elastic APM 支持的組件還是比較有限,比如對數(shù)據庫查詢還只是支持EF
Core,并不支持更多的組件,鏈路追蹤也僅支持HTTP請求的追蹤,也沒用支持其他的方式。另外,個人認為Elastic APM把監(jiān)控報警(Watcher)
給放到X-Pack收費包中也是挺讓人傷心的,異常監(jiān)控報警其實還是蠻關鍵的。
歡迎大家嘗試Elastic APM,有問題的地方共同探討~
熱門工具 換一換