现如今,用户在使用产品时越来越关注产品易用性和可用性,产品在交互上的表现好坏其实影响了整个服务的品质。大家也许会遇到突然有人投诉说服务不可用,或者一个App突然没有响应,发生了崩溃。这种情况下对于工作中数据中心里的运维运营人员来说,怎么获取用户使用产品的状况?以往只能通过用户投诉,才能知道服务出现了问题。后来也做了一些改善,例如做基础监控,对CPU、内存、网络、磁盘、硬件做一些监控,但是这些监控就满足了我们现在对于复杂应用的要求了吗?显然它不能很好的去满足目前复杂应用场景下应用状态的获取。
在交互过程中,怎样拿到移动端、浏览器端、服务器端服务的性能状态,又怎么根据这些性能状态来优化服务呢。今天想要讲到的就是能不能深入到应用的内部,通过一些技术手段拿到应用状态的来源,来分析应用的性能。
应用性能是整个应用的表现。用户使用服务的时候,最先接触到的是App,或者是通过浏览器去访问。在App接入网络,浏览器在做请求的时候,它经过了一些比如云服务,CDN服务,这些服务的性能也会影响到本身服务的表现情况。接着越过防火就到达数据中心里面,在这个前面有一些Web服务器,紧接着是应用服务器,在应用服务器上部署的是核心业务代码。为业务代码提供服务的可能是一些数据库服务,或者说是需要调用其他服务以及NoSQL服务等。
那么整个应用场景下我们会遇到哪些问题,哪些挑战呢?比如说一个App,突然间出现了崩溃,告诉你不可用了,这时候需要重新打开,再一次去找刚才访问的位置,这是体验非常不好的地方。又比如服务无响应,出现了菊花一直在转。除去浏览器、App,还有一些点在哪儿?那就是网络。在国内这种网络环境下,其实有很多问题会出现,在各个地域上,不同的接入方式,不同的服务提供商都可能出现一些问题。
如果说这一切都没有问题,假设网络非常好,App的体验也很不错。但是如果是因为服务器应用代码出现了问题,同样会造成整个服务的不可用。
如何解决这些问题呢?首先需要知道在一个请求里面到底是因为哪个环节出现了问题。以往我们用到的办法是说在浏览器里加自己的代码,在GS、服务端加一些log出来。除了这些,还有一个技术就是APM:Application Perfomance Managemnet,这里我们更多的理解成管理,而不是简单的监控。首先看移动端,这里有几个衡量指标:
崩溃率
ANR (服务无响应)
交互性能
HTTP性能
为了拿到这些指标可以通过一些技术,对于iOS可以通过hook技术。对于安卓,可以通过Dalvik虚拟机将代码植入进去,最终拿到性能数据。
崩溃
下图是微信的崩溃,微信在现在各种场景下是用户黏度最高的一个产品,如果微信出现了崩溃我们可以忍受,一次、两次甚至更多次我们都可以忍受,因为我们没办法把它抛弃掉,没有替代的产品。但是如果是自己开发的一个App并且在市面上有竞争者,用户在使用时出现了连续多次的崩溃,对于用户体验可能是差的,甚至从此这个用户就流失了。
其实大家一定想知道用户在什么场景下出现了崩溃,因为崩溃的问题影响了多少用户,在这一次崩溃里面发现了多少代码的问题,这是我们希望能从崩溃这里得到的信息,有了这些信息我们就可以去修复这些Bug,从而减少用户的流失。
比如说这张图片里可以看到,其中的一个版本里
一段时间的崩溃共发现了69次,影响到了5个用户。通过这个报表可以知道这段时间内,在全量用户里面App出现多少次崩溃,影响了多少用户。根据这些值和月活的数据,可以得出这一段时间内的流失率是多少,用户转化率是多少,这是做产品比较关心的指标。
当然我们也可以看在某一个时间点发生的崩溃,查看里面的堆栈信息。出现了崩溃,我们希望知道的是这个用户在崩溃之前的发生轨迹,比如说他在前两秒里访问了哪些页面,调用了哪些方法,或者执行了哪些事件。这对于我们去定位问题然后去解决这个问题都提供了很好的依据。这是在用户交互时发生崩溃之前的一些轨迹,可以更好的知道到,App里哪些服务,哪些页面存在潜在的问题。
ANR
下图是安卓一个比较典型的ANR (Application Not Responding)的问题。使用App的时候很可能会出现这个情况,弹出一个框告诉你应用无响应,是选择等待还是强制退出,这个也非常影响用户体验。那么我们怎样知道ANR是由什么引起的呢?其实思路都是一样的,我们希望知道它到底是在哪一个交互的过程中,哪一行代码出现了问题。
同样我们可以拿到ANR这种错误的信息,看到ANR当时的一些详情是什么样的,包括哪个应用的版本,SDK版本,哪个手机型号,在哪些运营商里出现了问题。最终通过这些堆栈可以定位出问题的代码行数,根据这些行数回过头来查代码,就可能知道这个ANR是怎么导致的,对解决问题是一个很好的依据。
前面讲的时候,说影响整个服务问题的时候有可能出现两种情况,一个是中断,一个是无响应。中断就是上面所说的App上的崩溃,ANR。或者是浏览器上面的服务器不可用、500。除了刚才讲的比较严重的问题,还有一些是交互慢的问题,这也是影响用户体验的杀手之一。我们如何知道用户在使用应用过程中到底是快还是慢?一个活动平均一段时间内用户平均加载时间是多少,在整个方法执行完之后到底花了多长时间。其实对于性能,慢交互有两种,一个是慢动作。慢动作是用户触发了页面上的按纽,触发了这种慢。还有本身在切换每一个页面的时候出现了慢。对这些慢动作来说,通过这个图片我们可以看到
在这个动作里面由哪些事件组成,在整个动作的生命周期里,执行了onCreate、onStart方法,还有External的访问。也就是说它在点击这个按纽之后,Activity在渲染的时候,除了本身代码执行之外,还访问了其他服务,这也是我们常见的一个情况。比如说我们现在不可能把所有的业务都放在你的App里,更流行的一些做法是通过API的方式,去服务器端请求资源,请求数据来渲染页面,在这个动作里,会看到它有一些Json的处理,这是一个动作所带来的一系列的业务逻辑,产生的一系列慢动作的问题。
接着看交互的问题,在渲染一个活动的过程中,我们会关注CPU的占用,网络的访问,Activity的性能消耗等等指标。除了这些,我们还知道不论是Android还是iOS,除主线程之外还有为主线程提供服务的子线程。在这个图中可以读出,除了main thread,还有thread-12和thread-13为Mainactivity提供服务。我们看到这个服务在网络请求的过程中有很多情况还是比较快的,只有其中一部分出现大范围的拥堵。继续追踪可以看到这部分的网络请求,访问到一个时间消耗了4秒的URL,传输的字节数、响应代码我们都可以看到。这是整个交互过程中的调用堆栈时序图,整个业务逻辑都呈现其中。我们通过这个慢交互的图表,能够清楚看到发生慢交互就是因为其中一个网络服务出现了问题。
对于网络访问,我们希望知道的是访问了哪个IP,哪个域名下某个URL所对应的服务。比如在这个App里绝大部分时间访问这个IP,在这个IP里其中有一个对服务器端HTTP的请求占比非常高
这时候就想知道这个时间到底花在哪里了?是因为在解析DNS,还是首包时间或者建连时间里出现了问题,还是本身为我提供服务的服务器的代码出现了问题,这里先留一个小的悬念,讲到端到端的时候再看,我们怎么知道这一次慢的过程是慢在哪里了。
除了APP端,现在绝大部分的服务还是放在Server端。Server端其实也有一些衡量指标。
应用响应时间
业务性能,吞吐率,成功率
服务性能(SQL,NoSQL,API,外部服务…)
代码效率(追踪,剖析)
代码质量(错误,异常)
我们可以用哪些技术获取这些数据呢?比如说对于Java技术来说,可以通过一些Agent自动嵌码的技术。 Class文件被classloader的时候做嵌入,在开始和结束做开始时间、结束时间的抓取,我们通过Tracer技术把调用链串联起来,就可以知道一个请求经过了哪些组件、服务等方法。
刚才讲了App、Server怎么抓取应用性能的问题,这些性能的问题都是孤立的。想要知道在单一用户场景下,App出现了缓慢,怎么知道是不是因为服务器端响应的慢还是网络慢,这就要说到之前提过的要实现端到端的应用性能管理。如何实现端到端呢?
以现在的服务架构来讲,微服务已经随着容器技术的发展,慢慢被大家推崇。以前使用SOA的架构,现在使用微服务的架构。一个服务不再是孤立的服务,会有很多的子服务来支撑。在服务架构里面越来越多的节点我们怎么去管理?很难知道其中一个节点出现了问题对整个服务的影响。通过一个应用的拓扑
可以知道最终的用户通过了App访问了这样的一个应用,在这个应用里提供给他平均的消耗时间是在0.008秒,在Java应用里有Memcache的服务,平均每分钟514次的调用,平均响应时间大概是8毫秒。除了这些还访问了一些Redis的服务和另外一个页面请求数据。接下来就会出现一个警报的状态,这个NoSQL的服务器访问了很多,这种应用间的拓扑可以在整个提供服务的架构里非常清晰的告诉你服务的架构,逻辑的架构是什么样的。
另外一点是一旦出现问题了怎么样去追踪,怎么样去定位,去划清责任的界限。以前出现问题了,DBA会抛给研发,研发抛回到DBA,另外有一些情况是外包的开发,外包人员很难爽快的说这是代码的问题或是服务的问题,会让你加一些物理设备。但是有了这些数据就可以很好的界定一个问题的责任。这是看到在慢交互时候的一个截图,是iOS的。
在加载过程中,其中网络的访问几乎占了整个服务时间。对运维人员来说很难知道FirstViewController的Network提供了哪个服务,通过慢交互追踪,在这个网络服务例请求了http://192.168.2.204:8022/register.aspx这个URL,包括当时的一些参数。
根据这个URL,在这个图里面会多两个图标,这两个图标的意思是说,可以根据这两个图表跳转到对应的Server端的服务,可以点一次看看到底发生了什么。点了一次之后页面跳转到了trance应用。
这是一个ASP的应用。看到调用堆栈里面,ASP/registry.aspx这样一个服务调用堆栈很简单, DonNet.Execute通过HttpWebRequest访问了其他服务,需要从其他服务那里拿到数据,是因为访问了JavaProject-56的服务,这个服务部署在Ubuntu-201机器上的。在往深一级的钻取。
跳转到了这个Java应用。在整个调用链里面,其中一个方法执行消耗了99%的时间。这个方法到底是什么,执行了什么内容?其实是一个MySQL executeQueue方法,以前如果我们定位了这个问题我们怎么办?我们希望知道这个executeQueue方法执行的是什么SQL,SQL语句到底是什么样的,在代码哪一行里调用了SQL。通过慢应用过程追踪,可以知道它当时的SQL是对article这个表的select,根据这些调用的堆栈知道是因为com.db.mysql.ArticleDao.showarticles这个方法调用了这个SQL,这样我们就实现了端到端问题的追踪与定位。
如果解决了这个SQL查询的问题,前面访问慢的问题也就解决了,用户的转化率就回来了,减少了流失率。