在今天的文章里,我们将介绍Elastic的一个重要的应用:应用程序性能管理(Application Performance Monitoring/Management),简称APM。那么到底什么是APM呢?您已经在Elasticsearch中应用了日志和系统指标吗? 使用Elastic APM扩展到应用程序指标。 您可以准确了解您的应用程序在哪里花时间,以便您可以快速解决问题并对所推送的代码感到满意。
随着时代的发展,我们的IT架构越来越复杂,比如:
当今运维监控的挑战 - 复杂的基础设施
基础架构层面是复杂的
多种服务器
多种网络设备
多种安全设备
多种存储设备
我们系统的服务器越来越多,而且更多的设备都部署在云端。复杂的系统甚至有成千个微服务及架构所组成,那么我们的业务请求可能需要一个或成百上千的的微服务来共同来完成。
由于业务的复杂度增加,微服务之间的互相调用也变得更加复杂。那么现在的问题是:如果我们的请求变得很慢,我们想知道到底是哪个环节出现问题了呢?
经验丰富的程序员或者系统设计者,可能从一些log里找到答案:
日志是事件的时间顺序记录。我们可以从这里成千上万的log里找到一些蛛丝马迹,比如我们可以通过请求返回的代码。Log在很多的时候很有用,但是它有一个几个缺点:
同时,应用开发者也可以从指标(Metric)里找到答案。指标是对数字KPI的定期测量:
在上面,我们可以通过metric每x分钟测量一次CPU负载并使用元数据进行注释。通过指标,我们可以分析出如果在一个请求发生错误或者响应时间过长的时候CPU的负载情况,或者磁盘的使用率是多少等这样的信息:
上面是在Mac上面使用iostat命令时我们看到的输出。指标在很多我们想要显示趋势或者历史数据时非常有用。在尝试创建简单,可预测,可靠的规则以捕获事件和异常时则很有用。 指标的一个问题是它们倾向于监视基础结构层,获取有关组件实例级别(如主机,容器和网络)的数据,而不是自定义应用程序级别。
但是现在的问题是:在上面的每个事件期间和事件之间到底发生了什么呢?比如上面的三个log事件之间,第一个事件发生在16:10:02,第二个事件发生在16:58:58,第三个事件发生在16:20:55。我们可以看出来第二个事件和第三个事件之间的间隔是9分钟。这之间到底发生了什么呢?
我们先看一下在第二事件发生时的日志和指标:
日志:
指标:
可是当我们的log变得非常大的时候,而且我们的接口也越来越多时,这个时候,我们再看这些日志和指标时也无能为力。
当我们在设计页面或者请求时,经常会遇到上面的这种等待的情况。可能有个别的工具能有效地解决部分的问题,但是如何能从整个的系统里来完成这种问题的定位及分析。Elastic推出的APM解决方案可以完美地解决这些问题。为我们的系统设计者或程序员提供了一个快速定位的方法。
运用Elastic APM,它可以很方便地帮我们定位出为什么我们的每个请求需要花这么多的时间来完成,中间的每一个时间都是花在哪里了?
Elastic 通过整合日志,指标及APM,统一整个生态系统可见性,打造一个完整的可观测性。将您的日志,指标和APM跟踪大规模整合到一个堆栈中,以便您可以监视环境中发生的事件并对事件做出反应。按照文章“Metrics, tracing, and logging”的描述,打造全面的可观测性,需要如下的三个要素:
在Elastic Stack中,我们都有相应的模块处理:
Elastic Stack可为所有运营数据提供快速,可靠和相关的搜索,因此无论数据类型如何,您都可以提出自己想要的问题-并获得所需的答案。APM扩展了日志记录和服务器级别的监视功能,为系统的可观察性增加了新的维度。通 Real User Monitoring (RUM),这也可以扩展到最终用户的体验。运用Elastic Stack,可以打造一站式的全栈监控:
简单地说:APM就是监视和管理软件应用程序的性能和可用性。Elastic APM是基于Elastic Stack构建的应用程序性能监视系统。 它使您可以实时监视软件服务和应用程序-收集有关传入请求,数据库查询,对缓存的调用,外部HTTP请求等的响应时间的详细性能信息。 这样可以轻松快速地找出并解决性能问题。
Elastic APM还会自动收集未处理的错误和异常。 错误主要根据堆栈跟踪进行分组,因此您可以在新错误出现时识别它们,并密切注意特定错误发生的次数。
指标是调试生产系统时的另一个重要信息来源。 Elastic APM代理会自动选择基本主机级别指标和特定于代理的指标,例如Java代理中的JVM指标和Go代理中的Go运行时指标。
我们先来看一下如下的这个图:
如上图所示,在不同时刻我们请求时,我们发现为什么在17:36:38发生的一个请求需要花将近8秒的时间,而另外在17:36:31分发生的一个请求却返回一个错误的代码?
Elastic APM方案是世界上第一个开源的APM 解决方案:
- 很容易让程序员看到应用在运行时各个部分所花的时间
- 很容让程序员调试错误
APM如何把数据存于Elasticsearch中,并提供分析呢?我们看一下如下的架构图:
如上图所示,我们看到一个最典型的APM架构图:
总体来说,APM数据仅仅是另外Elasticsearch索引。在Kibana中已经有一个现成的APM应用可以被我们所使用。我们也可以根据需求自己定制自己的Dashboard。APM可以完美地结合机器学习和告警。
它们之间的关系可以用如下的图来表示:
分布式tracing:
当请求从一个微服务流向另一个微服务时,追踪器添加逻辑以创建唯一的追踪识别代码, 跨度 id
在今天的练习中,我们将以Java Spring boot为例来展示如何使用Elastic APM。
首先,我们在terminal中打入如下的命令:
git clone https://github.com/liu-xiao-guo/elastic-apm-demo
上面的一个例子是一个简单的Spring boot应用。它有一下的几个特点:
下面是它的部分代码:
@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 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的文件。
你可以打开localhost:8080在网页中查看是否有页面的输出。
$ ls ./target/accessing-data-mysql-0.0.1-SNAPSHOT.jar
./target/accessing-data-mysql-0.0.1-SNAPSHOT.jar
如果你想测试这个应用的话,你可以在应用的根目录中打入如下的命令:
java -jar ./target/accessing-data-mysql-0.0.1-SNAPSHOT.jar
你可以打开localhost:8080在网页中查看是否有页面的输出。
我们可以把这个文件拷入到我们想要的任何一个目录中。针对我的情况,我把它拷入到我的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。我们在一个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。一定要记住这里的用户名和密码要和我们在上面的Java应用中的用户名及密码是一致的:
我们可以通过MySQL工具来查看:
请注意在上面的数据库中,我们有三个字段:id, email及name。
安装Elastic Stack,有两种方法。最简单的一种方法是运用docker来一键部署Elasticsearch, Kibana和APM服务器。详细安装步骤请参阅链接liu-xiao-guo/apm-contrib。下面我们采用一种手动的方法来部署Elastic Stack。
我们可以按照我们的文章“Elastic:菜鸟上手指南”来安装及运行我们的Elasticsearch及Kibana。
我们也必须安装和Elasticsearch一样版本的APM服务器。我们打开我们的Kibana界面,并点击左上角的部分:
然后,我们按照上面的步骤一步一步地进行安装:
我们按照上面的要求配置好Elasticsearch的地址及用户名和密码(如果你已经启动了安全的设置):
上面的步骤非常详细。如果我们希望对Real User Monitoring (RUM) 进行监控的话,我们必须修改我们的apm-server.yml进行修改。我们在apm-server.yml文件的最后添加:
apm-server.rum.enabled: true
对于APM agent的选择来讲,因为我们是Java应用,所以我们选择Java agent。我们下载相应的agent jar文件,并存放于我们上面放置spring boot的jar文件所处的文件夹。针对我的情况是home目录下的data/apm。
$ pwd
/Users/liuxg/data/apm
liuxg-2:apm liuxg$ ls *.jar
accessing-data-mysql-0.0.1-SNAPSHOT.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中看到信息:
我们点击上面的 “Confirm overwrite”按钮:
如果你已经运行到这里,那么你基本上已经把整个的环境运行起来了。我们可以在terminal中打入如下的命令:
curl localhost:8080/demo/add -d name=First -d [email protected]
上面的应用是向我们的数据中写入一条记录。
curl 'localhost:8080/demo/all'
运行上面的命令可以展示已经输入的所有的记录
curl 'localhost:8080/demo/weather'
运行上面的命令可以获得百度天气API接口所带给我们的天气信息。
上面的所有的信息我们都可以在浏览器中的地址栏中输入。
点击Kibana中的APM应用图标:
我们点击sample_apm链接:
在上面我们可以看到应用的四个接口的统计情况。
我们在这个APM应用的dashboard上可以看到我们所有的API的调用情况。比如:
因为在我的应用中,我故意加入了一些延迟,所以导致我们的整个getBaiduWeather的请求时间为9.157秒才完成,而api.map.baidu.com的时间只有149ms。我们点击上面的蓝色的线条,我们可以看到这个API的调用情况:
我们点击 “addNewUser” 链接:
我们可以看到如下的画面:
我们可以看到在addNewUser调用MySQL里的几个命令所花的时间。
我们也可以点击Error来看所有的错误信息:
我们点击上面的链接可以产出来我们的错误的stack:
我们点击上面的JVM,可以查看出来的目前的JVM的使用情况:
到这里我的讲解就完成了。剩下的留给大家自己去挖掘哈!
【1】Accessing data with MySQL