You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

154 lines
13 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 31 | 应用性能管理:用户的使用体验应该如何监控?
你好,我是唐扬。
上一节课中,我带你了解了服务端监控搭建的过程。有了监控报表之后,你的团队在维护垂直电商系统时,就可以更早地发现问题,也有直观的工具辅助你们分析排查问题了。
不过你很快发现有一些问题服务端的监控报表无法排查甚至无法感知。比如有用户反馈创建订单失败但是从服务端的报表来看并没有什么明显的性能波动从存储在Elasticsearch里的原始日志中甚至没有找到这次创建订单的请求。这有可能是客户端有Bug或者网络抖动导致创建订单的请求并没有发送到服务端。
再比如有些用户会反馈使用长城宽带打开商品详情页面特别慢甚至出现DNS解析失败的情况。**那么,当我们遇到这类问题时,要如何排查和优化呢?**
这里面涉及一个概念叫应用性能管理Application Performance Management简称APM**它的含义是:**对应用各个层面做全方位的监测,期望及时发现可能存在的问题,并加以解决,从而提升系统的性能和可用性。
你是不是觉得和之前讲到的服务端监控很相似?其实,服务端监控的核心关注点是后端服务的性能和可用性,而应用性能管理的核心关注点是终端用户的使用体验。也就是说,你需要衡量从客户端请求发出开始,到响应数据返回到客户端为止,这个端到端的整体链路上的性能情况。
如果你能做到这一点,那么无论是订单创建问题的排查,还是长城宽带用户页面打开缓慢的问题,都可以通过这套监控来发现和排查。**那么,如何搭建这么一套端到端的监控体系呢?**
## 如何搭建APM系统
与搭建服务端监控系统类似,在搭建端到端的应用性能管理系统时,我们也可以从数据的采集、存储和展示几个方面来思考。
首先在数据采集方面我们可以采用类似Agent的方式在客户端上植入SDK由SDK负责采集信息并且经过采样之后通过一个固定的接口定期发送给服务端。这个固定接口和服务端我们可以称为APM通道服务。
虽然客户端需要监控的指标很多,比如监控网络情况、监控客户端卡顿情况、垃圾收集数据等等,但我们可以定义一种通用的数据采集格式。
比如在我之前的公司里采集的数据包含下面几个部分SDK将这几部分数据转换成JSON格式后就可以发送给APM通道服务了。**这几部分数据格式你可以在搭建自己的APM系统时直接拿来参考。**
* 系统部分:包括数据协议的版本号,以及下面提到的消息头、端消息体、业务消息体的长度;
* 消息头主要包括应用的标识appkey消息生成的时间戳消息的签名以及消息体加密的秘钥
* 端消息体主要存储客户端的一些相关信息主要有客户端版本号、SDK版本号、IDFA、IDFV、IMEI、机器型号、渠道号、运营商、网络类型、操作系统类型、国家、地区、经纬度等等。由于这些信息有些比较敏感所以我们一般会对信息加密
* 业务消息体:也就是真正要采集的数据,这些数据也需要加密。
**加密的方法是这样的:**我们首先会分配给这个应用一对RSA公钥和私钥然后SDK在启动的时候先请求一个策略服务获取RSA公钥。在加密时客户端会随机生成一个对称加密的秘钥Key端消息体和业务消息体都会使用这个秘钥来加密。那么数据发到APM通道服务后要如何解密呢
客户端会使用RSA的公钥对秘钥加密存储在消息头里面也就是上面提到的消息体加密秘钥然后APM通道服务使用RSA秘钥解密得到秘钥就可以解密得到端消息体和业务消息体的内容了。
最后我们把消息头、端消息体、业务消息体还有消息头中的时间戳组装起来用MD5生成摘要后存储在消息头中也就是消息的签名。这样APM通道服务在接收到消息后可以使用同样的算法生成摘要并且与发送过来的摘要比对以防止消息被篡改。
数据被采集到APM通道服务之后我们先对JSON消息做解析得到具体的数据然后发送到消息队列里面。从消息队列里面消费到数据之后会写一份数据到Elasticsearch中作为原始数据保存起来再写一份到统计平台以形成客户端的报表。
![](https://static001.geekbang.org/resource/image/50/5b/50fe40212d09940e1c5c1b860163a15b.jpg)
有了这套APM通道服务我们就可以将从客户端上采集到的信息通过统一的方式上报到服务端做集中处理。这样一来你就可以收集到客户端上的性能数据和业务数据能够及时地发现问题了。
那么问题来了:虽然你搭建了客户端监控系统,但是在我们电商系统客户端中可以收集到用户网络数据、卡顿数据等等,你是要把这些信息都监控到位,还是有所侧重呢?要知道,监控的信息不明确,会给问题排查带来不便,而这就是我们接下来探究的问题,也就是你到底需要监控用户的哪些信息。
## 需要监控用户的哪些信息
在我看来,搭建端到端的监控体系的首要目标,是解决如何监控客户端网络的问题,这是因为我们遇到的客户端问题,**大部分的原因还是出在客户端网络上。**
在中国复杂的网络环境下,大的运营商各行其是,各自为政,在不同的地区的链路质量各有不同,而小的运营商又鱼龙混杂,服务质量得不到保障。我给你说一个典型的问题。
之前在讲解DNS时我曾经提到在做DNS解析的时候为了缩短查询的链路首先会查询运营商的Local DNS。但是Local DNS这个东西很不靠谱有些小的运营商为了节省流量他会把一些域名解析到内容缓存服务器上甚至会解析到广告或者钓鱼网站上去这就是域名劫持。
也有一些运营商比较懒自己不去解析域名而是把解析请求转发到别的运营商上这就导致权威DNS收到请求的来源IP的运营商是不正确的。这样一来解析的IP和请求源会来自不同的运营商形成跨网的流量导致DNS解析时间过长。这些需要我们实时监控以尽快地发现问题反馈给运营商来解决。
**那么,我们如何采集网络数据呢?**一般来说,我们会用埋点的方式,将网络请求的每一个步骤耗时情况、是否发生错误都打印下来,我以安卓系统为例,解释一下是如何做的。
安卓一般会使用OkHttpClient来请求接口数据而OkHttpClient提供了EventListner接口可以让调用者接收到网络请求事件比如开始解析DNS事件解析DNS结束的事件等等。那么你就可以埋点计算出一次网络请求的各个阶段的耗时情况。我写了一段具体的示例代码计算了一次请求的DNS解析时间你可以拿去参考。
```
public class HttpEventListener extends EventListener {
final static AtomicLong nextCallId = new AtomicLong(1L);
private final long callId;
private long dnsStartTime;
private HttpUrl url ;
public HttpEventListener(HttpUrl url) {
this.callId = nextCallId.getAndIncrement(); //初始化唯一标识这次请求的ID
this.url = url;
}
@Override
public void dnsStart(Call call, String domainName) {
super.dnsStart(call, domainName);
this.dnsStartTime = System.nanoTime(); //记录dns开始时间
}
@Override
public void dnsEnd(Call call, String domainName, List<InetAddress> inetAddressList) {
super.dnsEnd(call, domainName, inetAddressList);
System.out.println("url " + url.host() + ", Dns time: " + (System.nanoTime() - dnsStartTime)); //计算dns解析的时间
}
}
```
有了这个EventListner你就可以在初始化HttpClient的时候把它注入进去代码如下
```
OkHttpClient.Builder builder = new OkHttpClient.Builder()
.eventListenerFactory(new Factory() {
@Override
public EventListener create(Call call) {
return new HttpEventListener(call.request().url());
}
});
```
这样,我们可以得出一次请求过程中经历的一系列过程的时间,其中主要包括下面几项。
1. 等待时间异步调用时请求会首先缓存在本地的队列里面由专门的I/O线程负责那么在I/O线程真正处理请求之前会有一个等待的时间。
2. DNS时间域名解析时间。
3. 握手时间TCP三次握手的时间。
4. SSL时间如果服务是HTTPS服务那么就会有一个SSL认证的时间。
5. 发送时间:请求包被发送出去的时间。
6. 首包时间:服务端处理后,客户端收到第一个响应包的时间。
7. 包接收时间:我们接收到所有数据的时间。
![](https://static001.geekbang.org/resource/image/33/ba/33c2f9e1182813fadc0f6b8066379fba.jpg)
有了这些数据之后我们可以通过上面提到的APM通道服务发送给服务端这样服务端和客户端的同学就可以从Elasticsearch中查询到原始的数据也可以对数据做一些聚合处理、统计分析之后形成客户端请求监控的报表。这样我们就可以有针对性地对HTTP请求的某一个过程做优化了。
而对于用户网络的监控,可以给你带来三方面的价值。
首先,这种用户网络监控的所有监控数据均来自客户端,是用户访问数据实时上报,因此能够准确、真实、实时地反映用户操作体验。
再者,它是我们性能优化的指向标,业务架构改造、服务性能优化、网络优化等任何优化行为时,可以反馈用户性能数据,引导业务正向优化接口性能、可用性等指标。
最后它也能帮助我们监控CDN链路质量。之前的CDN的监控严重依赖CDN厂商这有一个问题是CDN无法从端上获取到全链路的监控数据有些时候客户端到CDN的链路出了问题CDN厂商是感知不到的而客户端监控弥补了这方面的缺陷并且可以通过告警机制督促CDN及时优化调整问题线路。
除了上报网络数据之外,我们还可以上报一些异常事件的数据,比如你的垂直电商系统可能会遇到以下一些异常情况:
* 登录失败;
* 下单失败;
* 浏览商品信息失败
* 评论列表加载不出来;
* 无法评分留言;
*
你在业务逻辑的代码中都可以检测到这些异常数据当然也可以通过APM通道服务上传到服务端这样方便服务端和客户端的同学一起来排查问题也能给你的版本灰度提供数据的支持。
总的来说,如果说搭建的系统是骨架,那么具体监控的数据就是灵魂,因为数据是监控的主体内容,系统只是呈现数据的载体。所以,你需要在系统运维的过程中不断完善对数据的收集,这也是对你的监控系统不断升级完善的过程。
## 课程小结
以上就是本节课的全部内容了。本节课我主要带你了解了如何搭建一个端到端的APM监控系统你需要了解的重点是
1.从客户端采集到的数据可以用通用的消息格式上传到APM服务端服务端将数据存入到Elasticsearch中以提供原始日志的查询也可以依据这些数据形成客户端的监控报表
2.用户网络数据是我们排查客户端,和服务端交互过程的重要数据,你可以通过代码的植入,来获取到这些数据;
3.无论是网络数据还是异常数据亦或是卡顿、崩溃、流量、耗电量等数据你都可以通过把它们封装成APM消息格式上传到APM服务端这些用户在客户端上留下的踪迹可以帮助你更好地优化用户的使用体验。
**总而言之,监测和优化用户的使用体验是应用性能管理的最终目标。**然而,服务端的开发人员往往会陷入一个误区,认为我们将服务端的监控做好,保证接口性能和可用性足够好就好了。事实上,接口的响应时间只是我们监控系统中很小的一部分,搭建一套端到端的全链路的监控体系,才是你的监控系统的最终形态。
## 一课一思
在实际的工作中,你的团队是如何通过监控,发现客户端上的问题的呢?欢迎在留言区与我分享你的经验。
最后,感谢你的阅读,如果这篇文章让你有所收获,也欢迎你将它分享给更多的朋友。