在应用实际的运维过程当中,咱们须要更多的日志和监控来让咱们对本身的应用程序的运行情况有一个全方位的了解。然而对于大部分开发者而言,平时你们所关注的更多的是如何更优雅的实现业务,或者是如何让应用的响应速度更快等等与编码相关的技术,对于应用程序的监控,可能还停留在日志文件的层面,并且大多数是出了事故被人为发现后,才经过日志尝试去定位问题。javascript
本文所准备介绍的Elastic APM是一套用于监控应用各项指标,好比系统响应时间、异常、EF执行的SQL记录等等,而且能够将这些记录组织成一个可追溯的链路,方便查询问题。此外,Elastic APM还能够经过Kibana来作很是漂亮的可视化展现,方便咱们定位和发现问题。html
废话再也不多说,咱们开始实战~java
Elastic APM的由下面四个组件所组成,以下图:git
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的安装:http://www.javashuo.com/article/p-nhqftoxi-ey.html
Kibana的安装:
个人环境是Centos 7,因此照着https://www.elastic.co/guide/en/kibana/7.3/rpm.html 这个官网教程安装的,整个过程很简单:
server.host: 0.0.0.0 server.name: 主机IP server.port: 一个你喜欢的端口号 elasticsearch.hosts: ["已安装好的ES地址,多个之间用逗号隔开"] logging.dest: /var/log/kibana.log //须要提早把这个文件建立好,而后把权限给够
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的安装相似,过程以下:
output.elasticsearch: hosts: ["已安装好的ES地址,多个之间用逗号隔开"]
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集成起来,一块儿继续吧~
咱们建立一个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还有不少其余的功能,好比链路追踪、数据库调用执行,让咱们来一块儿探索吧~
若是你了解过微服务架构,那你必定了解链路追踪这个概念。那什么是链路追踪呢?举个栗子:
有个服务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执行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,有问题的地方共同探讨~