谁的速度快!谁背锅(技术解析)
http://xjjdog.cn 对200+原创文章进行了细致的分类,阅读更流畅,欢迎收藏。
原创:小姐姐味道(微信公众号ID:xjjdog),欢迎分享,转载请保留出处。任何不保留此声明的转载都是抄袭。
温馨提示:如果你没有相关的从业经验,本文将会非常晦涩。
深夜,领导:“你写的接口有问题!赶紧起床瞧瞧”。
Ding!催命软件一响,你就知道,该work了。
可思来想去,觉得不可能啊。我的代码,就是一个简单的redis查询啊,难不成是Redis挂了?
同事把证据全部发到了群里,是你的接口无疑。一个简单的Get查询,平均耗时达到了2秒。jstack,promethus的监控,把问题全部指向到了你的接口!
登录Redis服务器,一切正常。该怎么办?要这么不明不白不清不楚的背个章丘大铁锅么?
1. 快是原罪
这种情况下,要相信自己的直觉。你的接口又快又好,很可能是木秀于林,鹤立鸡群,当了替罪鸟。
在 “某些” "高并发"环境下,由于资源未做隔离,在发生问题的时候,一些日志和工具的表现,会有非常强的迷惑性。
发生问题的,都是速度最快、请求最多的接口,但理论上并不可能。
如上图。这种情况很常见。
大多数请求,通过Tomcat
线程池的调度,进行真正的业务处理。当然线程池是不干这种脏活的,它把请求交给资源处理池去处理,比如:
-
一个数据库连接池,执行 耗时的统计操作
和迅速的查询操作
-
一个Redis连接池,执行 阻塞性的慢查询
和简单的GET SET
-
一个Http连接池(HTTPClient、OkHTTP等),远程调用速度不等的资源
-
...
我们平常的编码中,通常都会共用这样的资源池。因为它写起代码来简单,不需要动脑。
但如果你的服务本身,并没有做好拆分以及隔离,问题就是致命的。比如,你把报表接口
和高并发的C端接口
放在了一个实例上。
这时候,你就有可能被报表接口给坑了。
2. 一个例子
我们以数据库连接池为例,来说明一下这个过程,先看一下以下基础信息:
-
Tomcat的连接池,配置大小为 200
个 -
MySQL的连接池,配置大小为 50
个,算是比较大了 -
接口A需要调用耗时的查询,耗时为 5秒
-
接口B速度非常快,查询数据库响应时间在 200ms
以下
速度快的B接口,请求量是远远大于接口A的,平常情况下相安无事。
有一天,接口A忽然有了大量的查询,由于它的耗时比较长,迅速把数据库的50个连接池给占满了(接口B由于响应快,持有时间短,慢慢连接会被A吃掉)。
这时候,无论是接口A,还是接口B的请求,都需要等待至少5秒钟,才能获取下一条数据库连接,业务才能正常走下去。
不一小会儿,服务的状态就变成这样:
-
数据库连接池50个连接,迅速占满,而且几乎全被慢查询占满 -
Tomcat连接池的200个连接,迅速被占满,其中大部分是速度快的接口B,因为它的请求量大速度快 -
所有接口都Block在Tomcat的线程上。进而造成:哪怕是查询一个非数据库的请求,也要等待5秒左右
一般在遇到这种问题的时候,我们都倾向于使用jstack
打印信息堆栈,或者查看一些内部的监控曲线。可惜的是,这些信息,大部分都是骗人的,你看到的慢查询,并不是真正的慢查询。
从xjjdog上面的分析中,你应该很容易看出问题的症结所在:未隔离的瓶颈资源引起上游资源的连锁反应。
但在平常的工作中,xjjdog不止一次看到有同学对此手忙脚乱。很多证据都指向了一些又快又好的接口,而这些根本和它们一点关系都没有。
他们乐呵呵的截图,@相关人等,嚣张至极。
在遇到这种情况的时候,你可以使用下面的脚本进行初步分析:
$ cat 10271.tdump| grep "waiting to lock " | awk '{print $5}' | sort | uniq -c | sort -k1 -r
26 <0x0000000782e1b590>
18 <0x0000000787b00448>
16 <0x0000000787b38128>
10 <0x0000000787b14558>
上面的例子,我们找到给0x0000000782e1b590上锁的执行栈,可以发现全部是卡在HttpClient的读操作上了。在实际场景中,可以看下排行比较靠前的几个锁地址,找一下共性。
而这些显示信息非常少的堆栈,才是问题的根本原因。
3. 如何解决
增加Tomcat连接池的大小,或者增加连接池的大小,并不能解决问题,大概率还会复现。
最好的解决方式,当然是把耗时的服务和正常的服务拆分开来,比如时下流行的微服务。你的服务查询慢,自己访问超时,和我的服务,一丁点儿关系都没有。
但是,你的服务即然能遇到这种问题,就证明你的公司缺乏这种改造的条件。就只能在单体服务上来做文章。
这种做法,就是隔离。
如上图,我们在同一个工程里,创建了两个MySQL数据库连接池,指向了相同的MySQL地址。使用这种方式,连接池的操作,就能够相对做到互不影响。
但到现在为止,还没完,因为你的Tomcat连接池依然是共享的。
慢查询相关的,从连接池中获取连接的策略,要改一下,不能一直等待,而应该采用FailFast的方式(获取连接短时间的超时也是可以的),否则症状还是一样。
时下流行的熔断概念,也在一定程度上实践这种隔离性。
End
我们还可以联想到类似的场景:
JVM发生STW,停顿期间,受影响最大的,就是那些又快请求又大的接口。而那些耗时接口,由于平常就是那个鸟样,倒没人关注它的异常情况。
一堆接口连接了同一个数据库,当数据库发生抖动,受影响最大的,依然是那些又快请求又大的接口。因为那些耗时的慢查询,一直就是那样表现的,没人会怀疑到它们身上来。
殊不知,只要这些烂接口请求量一上升,就会像一颗老鼠屎,坏了整锅汤,所有的请求都会被拖累。
这有点类似于我们平常的工作:低效的人一增多,就会拖累整个项目的进度。领导一直在纳闷,为什么那么多技术好手,效率那么低呢?
这是因为,他们被拖累了。过于关注个体,最根本性的问题却掩盖在表象之下。
公司内部的研发,从来不应该一视同仁。不同技术追求的员工,也应该做到类似的隔离,宁缺毋滥。
好手组成的团队,交流顺畅,目标一致,效率奇高;而那些擅长拖慢项目的员工,就应该放在低效的团队,将加班进行到底。
说了这么多,问题的关键就在于:并不是每一个人都能了解这个规律,很少有人会关注这背后的根本原因。你要给领导解释你的接口没有问题,需要花费很大的力气。
“老板,我找到原因了。是因为一个MySQL慢查询,把Tomcat的连接池占满了,造成了Redis对应的Http请求响应慢。”这样错综复杂的关系,真的让人很头痛。
“很好”,领导说,“这个问题,就有你牵头来解决一下吧”。
你瞧,做领导的,大多不会关注问题产生的原因,他只关注谁能解决这个问题,哪怕不是你的问题。谁让你代码写得好,需求又做的快呢!
作者简介:小姐姐味道 (xjjdog),一个不允许程序员走弯路的公众号。聚焦基础架构和Linux。十年架构,日百亿流量,与你探讨高并发世界,给你不一样的味道。我的个人微信xjjdog0,欢迎添加好友,进一步交流。
推荐阅读:
一图解千愁,jvm内存从来没有这么简单过!
失联的架构师,只留下一段脚本
架构师写的BUG,非比寻常
nginx工程师,需要上承天命,下召九幽
实力解剖一枚挖矿脚本,风骚操作亮瞎双眼
又一P1故障,锅比脸圆
传统企业的人才们,先别忙着跳“互联网”!
面试官很牛,逼我尿遁
又一批长事务,P0故障谁来背锅?
一天有24个小时?别开玩笑了!
《程序人生》杀机!
可怕的“浏览器指纹”,让你在互联网上,无处可藏
2w字长文,让你瞬间拥有「调用链」开发经验
996的乐趣,你是无法想象的
作为高级Java,你应该了解的Linux知识(非广告)
必看!java后端,亮剑诛仙(最全知识点)
学完这100多技术,能当架构师么?(非广告)
Linux上,最常用的一批命令解析(10年精选)
数百篇「原创」文章,助你完成技术「体系化」
本文分享自微信公众号 - 小姐姐味道(xjjdog)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
在路上:严选服务治理实践
当你面对一个由几百上千个服务高度耦合而成的大型系统,各式各样的问题便会不约而至,而且绝对是一个不小的挑战。熵增原理告诉我们——任何系统都需要治理,本文分享严选在多年的服务治理实践中的一些思考、一些心得。 服务需要治理吗? > 当然需要,你100%会碰上各种需求,各种问题。 古代的农民耕种最关心的问题是气候变化、按时播种、按量灌溉,而如今的现代化规模化种植时代,农民(和粮食集团)还需要去关注一系列其它问题,譬如多作物立体种植、经济效应最大化、环保合规和生态破坏等等。只要生产方式和生产规模不是一成不变,那早晚都会有新的问题诞生。 计算机应用亦然。当服务技术架构还是单体服务(monolithic)的年代我们需要解决问题A、问题B和问题C,到了MVC架构/RPC架构/SOA架构盛行的时代可能会引入问题D、E、F,当业务规模持续扩张促使架构演进到微服务(microservice)阶段,此时你会发现,可能老问题A、B、D、F消失不见了,但C和E依然存在,同时还引入了好几个新问题G、H、I、J和K。 > 存在问题或可能出现问题,因此才需要治理。 IBM在2006年对服务治理的要点做过总结...
- 下一篇
熬夜7天吐血整理,适合Vue用户的React详细教程
我的日常工作中使用的是Vue,对于React只是做过简单的了解,并没有做过深入学习。趁着假期,我决定好好学一学React,今天这篇文章就是我在学习React之后,将React与Vue的用法做的一个对比,通过这个对比,方便使用Vue的小伙伴可以快速将Vue中的写法转换为React的写法。 插槽,在React中没找到?? 在使用Vue的时候,插槽是一个特别常用的功能,通过定义插槽,可以在调用组件的时候将外部的内容传入到组件内部,显示到指定的位置。在Vue中,插槽分为默认插槽,具名插槽和作用域插槽。其实不仅仅Vue,在React中其实也有类似插槽的功能,只是名字不叫做插槽,下面我将通过举例来说明。 默认插槽 现在项目需要开发一个卡片组件,如下图所示,卡片可以指定标题,然后卡片内容可以用户自定义,这时候对于卡片内容来说,就可以使用插槽来实现,下面我们就分别使用Vue和React来实现这个功能 Vue实现 首先实现一个card组件,如下代码所示 <template> <div class="card"> <div class="card__title"> &l...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- SpringBoot2整合Redis,开启缓存,提高访问速度
- Windows10,CentOS7,CentOS8安装Nodejs环境
- MySQL8.0.19开启GTID主从同步CentOS8
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- Hadoop3单机部署,实现最简伪集群
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长
- 设置Eclipse缩进为4个空格,增强代码规范
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果