查看原文
其他

【图文直播全文记录】微博在大规模、高负载系统问题排查方法

2015-05-22 秦迪 高可用架构

此文根据【QCON高可用架构群】分享内容,由群内【编辑组】志愿整理,转发请注明出处。


秦迪,微博平台及大数据技术专家,13年加入微博,负责微博平台通讯系统的设计和研发、微博平台基础工具的开发和维护,并负责微博平台的架构改进工作,在工作中擅长排查复杂系统的各类疑难杂症。爱折腾,喜欢研究从内核到前端的所有方向,近几年重点关注大规模系统的架构设计和性能优化,重度代码洁癖:以code review为己任,重度工具控:有现成工具的问题就用工具解决,没有工具能解决的问题就写个工具解决。业余时间喜欢偶尔换个语言写代码放松一下。


一、业务背景


微博主要面对的是大数据量、高负载的业务压力,并且会伴随着突发的请求峰值。请参考 : http://www.infoq.com/cn/presentations/typical-problems-of-weibo-in-large-scale-high-load-system



二、技术背景



大规模的基于linux系统的集群,使用java作为主要语言,一些外部的框架比如tomcat、storm、hbase等,一些自研的系统比如rpc框架motan、服务发现config service等。不同企业和行业采用的方案可能有区别,但从问题排查这个角度来说都是类似的。


三、排查方法及线索



问题排查相对于设计系统或者编码来说是一个反向推导的过程,这个过程往往比理解原因或者解决问题复杂。


例: 邻居家的小孩很调皮,有一天拿弹弓玩,把我家玻璃打破了,这是个正向的推导逻辑,很好理解。但是反过来,我到回家,看到玻璃破了,想知道原因,这个过程就要复杂的多。


对于影响线上系统可用性的问题,都可以总结成这样一个模式:一个根本原因,经过一条或几条传播路径,最后表现出某些现象。


例: 由于内存泄露,导致操作系统吃swap了,导致处理变慢,导致依赖它的服务超时,导致处理线程堆积,导致服务不可用。内存泄露是根本原因,服务不可用是现象,其它是传播路径。


但原因、路径和现象不是一一对应的,我在以往排查问题的过程中,遇到的绝大多数都不是完全相同的问题,比如表现相同的问题,原因和路径完全不一样;或者相同的根本原因,通过不同路径表现出不同的现象。


例: 应用吃swap的问题,可以是因为堆外内存泄露,可以是机器上启动了其它消耗内存的程序,还可以是numa配置引发的;同样,堆外内存泄露可能导致吃swap,也可能导致oom,还可能什么现象都没有。


所以单纯的看一些案例,了解“A会引发B”,对于今后问题排查来说有一些帮助,但是帮助不大,实际排查的过程中很多案例不能直接通过现象得出原因。更进一步,可以通过某个案例去了解“在出现B现象时,我可以通过某某手段去分析”,这种学习手段的办法会好一些,但是还不够。


随着新技术的使用和越来越高的访问峰值,新的问题层出不穷,如果技术上的倾向激进一些,必然会碰到无法通过现有案例解释的问题,此时更多是学习一些解决问题的思路。


还是回到今天的分享,关于排查问题。

我理解的排查就是一步步的收集线索、分析线索最终定位原因的过程,今天要讲的是如何更有效的发现和利用线索。


1、known-known

就是你想要知道,并且已经获取到的一些信息。比如日志,或者业务表现,或者监控图上反应出来的信息。 一般来说,比较容易获取的信息大概有这些:


  • 服务表现:问题的具体表现(出错、超时等)、应用日志、依赖服务的状态等

  • 系统状态:操作系统指标(系统管理的各种资源的状态、系统日志等)、vm指标(主要是gc)

  • 硬件指标:cpu、内存、网络、硬盘是否达到瓶颈


我们主要是通过框架定制+自研/开源工具的方式来获取上面说的几类信息,比如业务相关的指标是通过框架层输出日志+ELK/graphite之类生成图形。


graphite做的业务监控图


系统的监控用的是新浪内部的监控系统,也可以用开源的工具比如Cacti/Zabbix,运维这方面我不专业,就不展开了。


一般的监控系统应该可以提供上面这些信息,而对于分布式的系统来说,除了上面说的线索,还有非常重要的线索就是已知数据的定量分析和归类,比如:

  • 重现概率

  • 时间点

  • 问题的共同特征

这些线索非常有很可能因为描述的不够清楚而被忽略,但在实际排查中很可能很有价值。


比如“不是全部请求都出错”,或者“刚才数据库和web服务都出问题了”之类,如果换成“线上请求有1/3出错,都是电信用户的请求”或者“web服务18:12:11开始报异常,数据库18:12:30开始出现慢请求”,效果是不一样的。


例: 以前出现过一次线上未读数偶发清不掉的问题。出现问题时没有发现明显的异常,日志也没有问题,当时我们重启了前端服务,但是没有效果。之后我们统计了一下1分钟内出现问题的请求占正常请求的比例,大约是1/8,而这个服务依赖的另一个服务正好是8个实例,于是我们进一步统计出问题的请求,果然都调用到了同一个实例,于是当时下掉了这个可能有问题的实例,服务恢复了。


在之后通过进一步分析出问题的实例,找到了原因,那台机器在tcp超时后没有断开连接,而是直接把连接返回了连接池,导致下一次使用这个连接的时会取得上一次请求的返回值,对于计数这个场景,相当于每次返回的都是别人的数据。


总结一下关于known-known类的线索,主要是在不丢失信息的情况下,对信息做定量和归类分析,定位进一步的排查方向。


2、known-unknown


这个是指你想要知道但目前还不知道的信息,一般指不能直接看到的信息。可能你会疑惑为什么会有这个跟上面有什么区别。


例:应用出现了慢请求问题,业务逻辑的处理时间好像没问题,假设这个时候我记起看到过tcp文章里说socket有个接收队列,我隐约感觉这里可能有问题,应该看看有没有堆积,但是不知道怎么看,这个时候要怎么办?


一个很自然的想法是,那就上网查一下怎么看就是了,用不了10分钟就能解决。但是在实际排查问题的过程中,绝大多数情况下我并不会花5分钟去查研究怎么看队列长度,更有可能的做法是去花1分钟查一下gc情况,花1分钟看看系统日志,再花2分钟去确认一下应用日志是不是看错了。


造成这个现象的原因大致有两个:


首先是因为在排查问题的过程中,收集到的线索更多是用来排除可能性,而不是用来证明可行性,在得到线索内容之前,我很难说某个线索比另一个线索更有价值,这时会很自然的倾向于那些时间成本更低的工作。


其次,心理学上有个现象,叫做“熟悉偏好”,指的是人们在熟悉的事情和不熟悉的事情之间更喜欢选择熟悉的,回避陌生的事情。在排查问题,尤其是线上问题的过程中,表现的尤其明显。当出现了问题之后,很多同学更倾向于利用已有经验排查问题。


以上两个原因跟技术关系不大,但是造成了虽然知道应该去获取某些信息,但实际上总会拖到实在没招的时候再去想办法查看“我觉着应该知道的信息”,实际浪费的时间远远大于新知识的学习时间。


如何获取这些隐藏的信息,我个人的经验是使用工具,不管是系统还是应用都可以给我们提供很多工具,在qcon的演讲里也提到了一些。无论是系统提供的,还是第三方的,还是自己开发的都可以辅助我们发现更多的线索。


但是就像刚才说的,问题排查过程中,工具的学习和使用成本是需要考虑非常重要的因素。我列举几点经常用到的信息和我期望能达到的效率,供大家参考。


  • 30秒获取整体服务情况:请求量、响应时间分布、错误码分布。这里主要是用业务的监控系统。

  • 3分钟了解某台机器的负载情况:最耗cpu的线程和函数(cpu)、tcp连接状态统计和buffer堆积状态(网络)、程序的内存分布,最耗内存的对象(内存)、当前是哪个程序在占用磁盘io、gc情况。这里主要是linux和java的一些辅助工具:top/perf/netstat/iftop/jmap/jstat等等。

  • 3分钟了解请求的链路情况,网络传输、系统调用、库函数调用、应用层的函数调用的调用链、输入、输出、时长。这里也有linux和java的一些辅助工具,tcpdump/strace/ltrace/btrace/housemd等。最近我们也在完善用于集群的调用链分析工具trace,希望在足够完善之后能开源出来。

  • 3分钟检索当前系统的快照情况:线程栈情况、某个变量的值、存储或者缓存里的某个值是什么。同样是系统和java提供的一些已有工具,jmap/jstack/gdb/pmap等。


可能大家觉着这个指标定的有些低,有不少信息都是一个命令就可以看到了,这里特别强调一下,我指的是从头脑中有要看的想法,到看到并理解实际信息的时间。google一下命令的用法、安装工具之类的时间也要算进去:P


刚才说到关键点是如何提高效率,熟练工是一个方法,但是降低工具的使用成本更有效一些。我们做了一些排查问题相关的工具和系统,相信很多公司也有类似的内部系统。


之前qcon上展示过监控之类的大型系统,今天展示几个日常用的小玩意。

视频

查询具体的业务数据,比如查询某个id当前的消息计数:

视频

分析当前机器上的jvm的线程、堆或者其它信息:


第一个视频是我们的内部后台,我要查询一个uid的计数情况,只需要输入查询关键字和用户昵称就可以把多个计数系统的结果汇总展示出来

第二个视频是shell脚本,通过一个命令安装之后,之后可以对jvm按照步骤做自定义的分析,并把每一步的输出打印到日志里。


总之,关于known-unknown只强调一点:如果要做用于排查问题的工具,如果一次查询的时间超过3分钟,那实际排查的过程中很有可能就没法发挥这工具的价值。


3、unkonwn-unknown


我个人有个体会,高负载系统中出现的问题中,有很大一部分问题产生的原因是我原先根本不了解的,比如jvm里某个bug,或者一些内核在实现中有一些特殊的机制,之前听都没听过。


这里有个误区,超出知识体系并不意味着不能分析问题。但是在遇到一些棘手的问题,超出了以往的知识体系,这时很有可能会产生焦虑感,认为这个问题不科学、无法解决,并且有很可能会做一些没有价值的事情,比如反复检查日志、反复重启、甚至开始论证这个问题不可能发生之类。


这个时候其实就没有很具体的方法了,不过还是一些思路给大家分享一下:对于这类场景可以做减法,尽可能缩小范围,当范围可控之后,再去了解相应的原理。


这里有几个具体方式:

  • 尝试重现问题,修改变量尝试是否能复现

  • 对比正常系统和异常系统的不同,找出异同点

  • 通过已有知识剔除异常中正常的部分,缩小异常范围

  • 看书或者教程,了解原理;看源码,了解实现机制


例:前几天分析的一个tomcat突然请求变慢的问题,日志里无异常、性能没有瓶颈、线程数正常、通过工具也没找到什么新线索,还是不知道慢在哪了。


当时我的做法是先尝试复现问题,不管是测试环境压测、tcpcopy还是保留现场等等都可以用于复现现场,qcon上讲过这里也不重复了。


问题复现之后尝试缩小范围:一次http请求的过程包括tcp三次握手、应用accept连接、接收request、应用层处理、发送response、关闭连接这个过程,于是我用tcpdump和strace直接跟踪了网络包状况和系统调用,梳理了某一次调用的时间轴,发现时间浪费在三次握手和accept之间。


之后通过accept关键字在tomcat源代码中查找,之后分析了accept相关的代码,找到了tomcat的一个bug:bio方式下如果应用有stackoverflow,那么线程会退出,但是连接计数没有减掉,导致新请求不能被accept。


特别说一下,这个bug是7.0.42的,后面的版本修复了这个问题。


四、总结



以上是关于known-known,known-unknown,unkonwn-unknown,我的一些理解。在排查的时候可能会交替的遇到这几种场景,不过只要掌握诀窍,逐个击破就好。


下面给大家一些系统设计方面的建议,其实在上面的内容中已经体现了,我在这里只是总结一下。


首先,问题排查是复杂的,不可控的,所以不要把排查和解决混在一起,尽量先解决、再排查。解决的方式基本上都是那么几板斧:重启、回滚、扩容、降级、迁移,具体方案这里就不展开了。


其次,系统要尽可能的对外暴露内部状态和干预手段,比如说少打了一句日志,没把变量输出出来,那么出现问题的时候就不得不使用某些复杂的工具去查询这个变量,而且很有可能还要多绕一个大圈。


再次,系统是不稳定的,所以对于高可用架构设计来说,隔离是必须的,不管是何种依赖方式,都需要考虑“实在不行了”的情况。qcon上讲过,这里也不多说了。


最后,问题的原因、传播路径和现象不是一一对应的。同一个问题,这次的表现是多打了一行WARN日志,下次可能就是一次系统雪崩。墨菲定律,如果有可能出问题,那一定会出问题。


之前qcon的演讲地址,如果没看过,可以跟今天说的内容对比着看一下。 http://www.infoq.com/cn/presentations/typical-problems-of-weibo-in-large-scale-high-load-system


五、Q&A



Q1:刚查了一下,那个wtool工具是自己写的吗? 基于哪些工具实现的?

A1:这个是自己写的,主要是基于脚本和现有工具的整合。github上有个开源版: https://github.com/qdaxb/wtool


Q2:在线jvm的信息排查,是在开源工具上进行的封装,还是自己写的工具?

A2:主要都是开源的,一小部分是自己写的。


Q3:抗峰时的分流以及日常架构上的分流设计方案(包括应用端和数据库DAO层设计思路)

A3:这个和本次分享的主题关系不是很大,稍后单独沟通吧。


Q4:第一个业务查询工具如何做到能够持续好用?

A4:一是把框架和逻辑分离,增加逻辑时不用改代码,写一个脚本就可以简单的完成二是尽可能的优化工具的效率,这个是问题排查工具的核心价值。


Q5:系统出错了,你们排除问题时,怎么保证不影响线上呢?

A5:首先是要解决问题,通过运维的一些介入手段把服务恢复,同时尽量保留现场(比如保留一台出问题的机器只摘除不重启) 其次是在通过监控或者日志初步定位原因之后在线下复现问题,这时候排查就没有什么心理负担了。


Q6:不同语言如php/java的应用,还有不同的层面如网络/操作系统/数据库的问题的排查,都有什么模式和异同?

A6:我理解思路上都是类似的:找线索、推测原因、再找线索证明,区别主要是问题的原因具体用到的工具可能不一样,现象基本上都是那么几种:慢了、死了、处理出错了。


Q7:业务系统的日志,是通过外挂工具收集,还是要侵入到业务里面写日志?如果侵入的业务里面,有没有一些日志方案推荐?

A7:有一部分是外挂工具,还有一部分是业务里面,不过业务里面指的也是业务的框架层去集中输出这些日志,具体写业务的人不用管。 日志方案我们主要用的scribe,也有一部分用logstash,运行的都挺好。


Q8:线上出现请求block或请求慢时,一般会保留哪些现场数据?如果线下难以重现,线上问题的时间窗口也滑过去了,是不是可能会变成无解问题?

A8:一般来说,不重启是最重要的; 其次,java会把jstack/jmap/jstat之类都来一遍,其它类型的linux程序主要会留gcore和各种指标类的数据,top/perf/strace。


Q9:请教老师,做监控的话一些metric的阈值,你们是怎么设置的啊,是靠人工观察经验得出,还是使用了一些自动化比如机器学习的方案

A9:一般根据请求量和监控系统的处理能力决定,一般来说只要请求量够大,采样率设多少没什么太大区别。


Q10:java在请求无法响应的时候,这时候jdump需要很长的时间,线上无法服务,有没有更好,更快速的方法保留现场?

A10:我们在dump的时候这台节点已经从线上摘掉了,所以慢不是问题。 如果不能摘,可以考虑用btrace,housemd这类工具直接挂到进程上分析,不过btrace有可能导致应用假死,几率几十分之一,慎用。


Q11:业务出问题后是多个部门一起查找问题么?有些问题既要懂业务又要懂技术细节,在微博有多少人能达到您的排查问题水平,每次出问题都需要您出马么?有没有自动诊断问题工具?

A10:问题自动诊断我也很想要,最近也想继续改进工具。不过更多的可能还是有工具自动把一些现象把帮我汇总出来,分析感觉还是做不到自动化。


感谢 心灵的旅程 的记录与整理,以及Carson的公众号编辑,其他多位编辑组志愿者对本文亦有贡献。


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存