使用Elastic APM監控你的.NET Core應用

做者:Jax

前言

在應用實際的運維過程當中,咱們須要更多的日誌和監控來讓咱們對本身的應用程序的運行情況有一個全方位的瞭解。然而對於大部分開發者而言,平時你們所關注的更多的是如何更優雅的實現業務,或者是如何讓應用的響應速度更快等等與編碼相關的技術,對於應用程序的監控,可能還停留在日誌文件的層面,並且大多數是出了事故被人爲發現後,才經過日誌嘗試去定位問題。javascript

本文所準備介紹的Elastic APM是一套用於監控應用各項指標,好比系統響應時間、異常、EF執行的SQL記錄等等,而且能夠將這些記錄組織成一個可追溯的鏈路,方便查詢問題。此外,Elastic APM還能夠經過Kibana來作很是漂亮的可視化展現,方便咱們定位和發現問題。html

廢話再也不多說,咱們開始實戰~java

Elastic APM介紹

Elastic APM的由下面四個組件所組成,以下圖:git

Architecture of Elastic APM

APM Agentgithub

APM Agent是安裝到你的.NET Core程序中的一個Nuget包,他用於性能、錯誤等各種數據的收集,並將收集到的數據緩存起來分批發送到APM Server。固然,除了.NET Core使用的Nuget包,他還能夠支持不少其餘的語言,好比Java,Node.Js,Python等數據庫

支持的語言列表請參考這裏:https://www.elastic.co/guide/en/apm/agent/index.htmljson

APM Serverapi

APM Server是部署在服務器端的一個用於接收Agent發來的數據包的應用程序,並根據這些數據包自動建立文檔,將數據轉存到Elastic Server中。瀏覽器

Elastic Search緩存

這個相信你們都很熟悉了,他就是一個基於Lucene實現的高性能、分佈式的全文搜索引擎,用於快速、實時的存儲、搜索和分析大量數據。在這裏來講,他提供的是數據存儲和搜索能力!

Kibana

若是你熟悉Elastic Search,那麼你必定多少會了解Kibana,Kibana是開源的分析和可視化平臺,他能與Elastic Search進行很好的協同,幫助你快速的可視化存儲在Elastic Search中的數據,並作成各類各樣漂亮的報表、圖形等。

環境準備

在本次的實戰過程當中,咱們須要如下的東西:

  • Elastic Search
  • Kibana
  • APM Server
  • 一個基於.NET Standard 2.0 + 的項目

Elastic Search的安裝:http://www.javashuo.com/article/p-nhqftoxi-ey.html

Kibana的安裝:

個人環境是Centos 7,因此照着https://www.elastic.co/guide/en/kibana/7.3/rpm.html 這個官網教程安裝的,整個過程很簡單:

  • 下載Kibana RPM包(採用這種方式是由於用yum install網速太慢,因此我用迅雷下載完成rpm文件後上傳到Linux機器中)
  • 執行命令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 //須要提早把這個文件建立好,而後把權限給夠
  • 將Kibana安裝爲系統服務並啓動
sudo /bin/systemctl daemon-reload
sudo /bin/systemctl enable kibana.service

sudo systemctl start kibana.service

這裏你們必定要注意Elastic Search的版本和Kibana必定要匹配,否則會報錯的。(個人ES是前段時間裝的,因此會有這問題,若是你們一口氣安裝全部的,應該沒啥問題)

若是不幸遇到了問題,能夠經過配置文件中logging.dest中配置的路徑查看日誌。

APM Server的安裝

APM Server的安裝跟Kibana的安裝相似,過程以下:

  • 下載RPM包,包在這個頁面找你須要的版本,也須要跟ES、Kibana的版本一致,否則你懂得~ https://www.elastic.co/cn/downloads/past-releases#apm-server
  • 執行rpm --install 「下載的文件名」進行安裝
  • 在文件夾/etc/amp-server中修改配置文件apm-server.yml,將配置文件最開始的host: 「localhost:8200」修改爲「0.0.0.0:8200」,以便讓他能容許經過ip:端口號的方式訪問, 並在配置的最後面添加以下配置:
output.elasticsearch:
    hosts: ["已安裝好的ES地址,多個之間用逗號隔開"]
  • 將apm-server安裝爲系統服務並啓動
sudo /bin/systemctl daemon-reload
sudo /bin/systemctl enable apm-server.service

sudo systemctl start apm-server.service

執行上述操做完成後,在瀏覽器中嘗試打開服務器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按鈕,若是出現You have correctly setup APM Server則說明安裝完成~

到這裏爲止,咱們的安裝工做就所有完成了,接下來,咱們嘗試將.NET Core與Elastic APM集成起來,一塊兒繼續吧~

.NET Core 應用集成

咱們建立一個Demo項目,來用於測試APM的各項功能。

項目的地址請參考GitHub:

引用依賴包

咱們須要從Nuget引用相關的SDK來與咱們的應用作集成,其實就是引用咱們最開始說的APM Agent的部分,在Nuget中,咱們引用Elastic.Apm.NetCoreAll這個包。

依賴這個包其實至關於自動依賴了以下三個包,你也能夠根據須要只依賴其中的一部分。

這裏咱們爲了簡單起見,直接印用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跟蹤一下,看看是否是哪裏沒有配置對

 

 

監控數據查看

在Kibana的Add APM頁面的最下方,找到Load Kibana Objects,來建立索引,而後點擊APM dashboard按鈕,就能夠進入APM數據的查看頁面。

 

點擊APM Dashboard按鈕後,展現的頁面以下:

該頁面中的功能分爲Services、Traces兩個大的功能模塊,先來簡單瞭解一下這兩個Tab頁中對應的功能。

Services

下面的列表中顯示的XianDotnetCommunity其實就是你在配置文件中配置的ServiceName,點擊這個名字進入,又能夠看到以下的報表,裏面有Transactions,Errors,Metrics三個Tab頁。

 

 

其中

Transactions:展現的當前應用請求狀況的概覽,包括請求響應時長、請求調用次數等等

Errors:程序中的異常列表

Metrics:應用程序所在機器的CPU/內存使用狀況

PS:其實我以爲很是須要一個當前應用程序所消耗的內存和CPU的值,可是貌似.NET Core版本的代理沒有實現這些功能,期待將來的更新吧

Traces

裏面是用於作鏈路追蹤的視圖,首頁包含全部事務的名稱列表以及響應時間等

點擊具體的事務進去,能夠看到這個事務通過的鏈路列表以及更詳細的一些響應信息,從而幫你分析出整個鏈路中的瓶頸,更多內容咱們在下面細講。

探索更多

Elastic APM還有不少其餘的功能,好比鏈路追蹤、數據庫調用執行,讓咱們來一塊兒探索吧~

監控API調用鏈路追蹤

若是你瞭解過微服務架構,那你必定了解鏈路追蹤這個概念。那什麼是鏈路追蹤呢?舉個栗子:

有個服務A,他會依賴服務B,C,而服務B又會依賴服務D,E,服務C又依賴F,G(省略無數依賴關係),而後有一天,服務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={b} & c={c} & d={d} & 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

監控EF執行記錄

這個不須要過多的解釋,就是在EF執行DB操做時,進行監控,以便發現性能等問題,個人代碼大體以下:

[HttpGet("person")]
public void TestEfCore()
{
    using (var db = new ApmDbContext())
    {
        var jax = new Person
        {
            Name = "西安.NET社區",
            Age = 26,
            Remark = "作最好的技術社區~"
        };

        db.Persons.Add(jax);

        db.SaveChanges();

        db.Persons.FirstOrDefault(x => x.Id == jax.Id );

        db.Persons.FirstOrDefault(x => x.Name == "西安.NET社區");

        jax.Name = ".NET西安社區";
        db.SaveChanges();

        db.Persons.Remove(jax);
        db.SaveChanges();
    }
}

當咱們使用Kibana查看此次請求時,TimeLine顯示以下:

咱們能夠比較清晰直觀的看到在此次請求中,執行了哪些SQL語句,各耗時多少,對咱們的請求分析來講,仍是蠻有用處的。點擊具體的藍條,還能夠看到更詳細的數據,但比較遺憾的是,數據中並無記錄SQL Params ,這對於咱們想徹底重現此次請求來講,仍是不夠友好~

自行埋點記錄

相對來講,Elastic APM目前生態圈還不夠好,比sky walking仍是稍微差一些組件的支持,若是要使用Elastic APM,免不了本身去作一些性能數據的埋點記錄,或者在爲第三方組件、類庫作支持時,也須要作一些數據的埋點。接下來咱們就在咱們的請求中,埋一些咱們想額外記錄的信息,示例代碼以下:

[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這個類去自定義本身須要捕捉的一些監控數據了~

異常監控

當咱們的程序發生了異常時,Elastic APM能幫助你記錄,這個功能和日誌差很少,但可能比日誌稍微好用那麼一點點。咱們一塊兒來看看吧~

示例代碼以下:

[HttpGet]
public void TestException()
{
    try
    {
        throw new Exception("捕獲的異常");
    }
    catch (Exception)
    {

    }

    throw new Exception("未捕獲的異常");
}

執行代碼後,咱們能夠經過點擊Service Name,而後在Errors這個Tab頁中查看到此次的異常

點擊詳情,咱們能看到詳細的堆棧調用信息:

此外,咱們能夠在Trasactions Tab中,找到發生異常的這個請求,而後點擊查看詳情,在詳情中咱們也能看到此次異常的發生:

總結

本文介紹瞭如何使用Elastic APM在.NET Core應用中收集性能和異常數據,並使用Kibana進行可視化分析,總體來講,Elastic APM仍是挺強大的,對於性能監控、鏈路追蹤、異常監控基本是夠用了。

目前來講,Elastic APM 支持的組件仍是比較有限,好比對數據庫查詢還只是支持EF Core,並不支持更多的組件,鏈路追蹤也僅支持HTTP請求的追蹤,也沒用支持其餘的方式。另外,我的認爲Elastic APM把監控報警(Watcher) 給放到X-Pack收費包中也是挺讓人傷心的,異常監控報警其實仍是蠻關鍵的。

歡迎你們嘗試Elastic APM,有問題的地方共同探討~

相關文章
相關標籤/搜索