前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >什么不要做?关于失败和优化

什么不要做?关于失败和优化

作者头像
一乐骑摩托
发布2020-04-23 16:06:01
3990
发布2020-04-23 16:06:01
举报
文章被收录于专栏:一乐来了

图片 Person of interest,源自网络。

上周面试一个人,聊到了服务性能优化。

他:线上的推送服务使用的单机单线程,性能撑不住。于是就改成了多机,还加了线程池。 问:撑不住是什么表现?什么原因? 他:因为是短信验证码服务,短信通道堵塞,所有请求都堵在vm里,引发了OOM。 问:看起来缓存使用内存不够,跟线程池有什么关系? 他:呃...正好当时在试用一个线程池库,就用了。

当时的对话没这么简单,因为并不是所有人都能说明遇到的问题,更不是所有人都能坦诚面对自己的折腾,况且我还要考虑提问的方式和方法。但情况大抵如此,在服务研发中最不愿看到的场景,恰恰是最经常发生的。

A:线上服务有bug,我hotfix一下。唔,正好还优化了xxx,一起上线了。 B:这次增加了一个新功能,顺手也把vm参数改了,上去看看效果。 C:感觉请求处理的地方有点问题,我改了下,应该会起作用......

每次听到“正好、“顺手”、“感觉”这样的词,我都得浑身一个激灵。天上要掉馅饼了?

真实情况往往相反,天上掉下来的是石头。这说起来也是不幸,要不墨菲不会那么出名。

要控制问题域

其实互联网服务做久了,线上问题处理早已变成了家常便饭。因为用户不能忍受宕机,所以你有问题查起来基本是不眠不休。个中滋味,只能自己慢慢体味。

这问题有时候是新问题,你刚改的代码有个bug,有时候是老问题,但是是暴露了老代码里的bug。多数情况下,都可以通过回滚代码来解决。个别情况下,你需要增加一个fix版本重新上线。这些都简单,难的地方在于定位问题。

定位问题最怕问题域太大。因为你得一步步分析,将可能发生问题的范围缩小。从现象出发分析,从改动入手检查算是一个捷径。如果改动太大,无疑会增加思考的成本和定位的难度。

而紧跟着的问题就是,如果作为一个fix版本上线,是否可以带入其他改动。我的回答是不行,因为也有太多次的失败来源于这个hotfix。如果再次出了问题,你需要首先判断是不是老问题没被解决还是引入了的新问题,这又涉及到整个请求处理链的重头分析。

我们不怕优化失败,但是怕优化出来的失败。优化失败最多还是老样子,但优化出的失败很可能是一次雪崩。

有些优化不要做

回到最开始的那个例子,他的问题表现出来是盲目引入新库,做法与我们常规的处理方式相背。根源则在于对问题的分析不足,不清楚问题的原因,当然可怕的地方在于(也是不可避免的),还使用了试试看的方式来修复问题。

应该做哪些优化呢,简单来说就一句话,做该做的优化。谈这个问题很多人都有经验,就算刚才说的“试试看”,在某种程度上也算。不过我今天想说的是不应该做的,一些经常被忽视的事情。

1. 不要用不需要用的东西

第一个事是不要用不该用的东西。一般来讲,互联网服务的峰值会很明显,峰值压力会是平时的十倍甚至百倍。应对请求洪峰,简单的方法就是用队列削峰。但是很多人用惯了队列后,只要设计服务,都会使用队列分离。但队列一旦进来,不仅会增加服务的复杂度,而且割裂了前后端之间的联系。

由于已经跟前端解耦,很多时候在处理请求的时候,前端已经返回了。这时候发生错误的话,已经没办法通知用户,导致服务只能偷偷失败。这是很容易气坏用户的处理方式。

2. 可以失败不要犹豫

另一个事是可以失败不要犹豫。很多时候服务都是分层的,外层服务访问内层服务,需要应对各种失败,有环境问题像网络闪断机器故障等,有服务内部故障像空指针虚拟机崩溃等。那么问题来了,内层服务失败了怎么办?举例来说,如果用户注册时数据库出现问题,该返回错误还是重试?如果重试次数设多少?如果是超时怎么办?超时要设多久?

除去数据问题,这里面经常被人忽视的却是性能问题。假设正常情况下10ms返回的请求,超时是1s,那么遇到超时的时候,请求处理速度其实下降了100倍,而如果还有重试,那就会继续降低。性能下降的直接结果是导致后续请求要么堆积要么丢弃。堆积会占内存,很多情况下会引发服务本身OOM退出,让服务质量雪上加霜。

其实开始的时候直接失败,也就没有后续这么多的失败了。或者超时短一些,性能也不至于下降太多。看起来设计很完善的错误处理策略,很可能不过是往背篓里加了无用的石头而已。

3. 不要修不该修的东西

还有个事就是不要修不该修的东西。Wikipedia里一直有句提醒,“If it ain’t broke, don’t fix it”,但是没说原因。这里说两个例子。

一个是虚拟机调优,不管是Java还是Erlang都会涉及。Java里多的是GC参数,Erlang里一般都搞调度器,情况差不多。如果哪一天你翻看参数说明,发现某个GC算法看起来更好或者新生代比例好像不太合适,或者发现CPU利用率一直不高而调度器线程池貌似可以调大,千万不要手一抖改了线上服务。因为线上服务实际运行情况复杂,很可能因为你的调整效果反而下降。GC调完引来了虚拟机OOM,调度器调完了请求超时不断。

另一个是数据一致性问题。举一个最近的例子。Mnesia是Erlang自带的数据库,速度非常快,跟OTP集成度高,但它在事务处理的时候会出现死等情况。由于两个节点通讯是请求应答模式,请求节点在发出请求后等待,等待的过程会定时检查对端节点的存活情况。问题出在这个定时检查上,它假定对端只要在线就肯定会回应。但如果对端节点在这个检查中间死而复活,那么它即使在检查的时候活着,也不会再回应了,请求节点就因此一直等下去。同时由于所有的消息都是在一个事务管理进城内,后续的所有请求都无法处理了。要不了多久,这个节点就会因此挂掉。

解决这个死等问题,一个想当然的方案是增加节点启停时间记录,检查的时候多检查一些东西。这也是我们开始时候想的方案。但是然后呢,这个请求该怎么处理?想想刚才讲的失败策略,该丢还是该继续呢?如果丢了,数据一致性怎么保证?如果你的场景像我们一样不希望丢,那么你很可能把原来已经可以常规处理的问题,变成了一个新问题。我们暂时是没有处理这个问题,还因为暂时还没想好要维护一份自己的Mnesia代码。

这三件事只是典型情况,还有很多情况,需要在做之前全面思考。毕竟成果是以产出来衡量,而不是付出,要看你解决了多少问题,而不是做了多少事情。

很多时候,做得少就是做得多。

图片来源:Behavioral Economics: Why More Is Not Better,

https://stevensonfinancialmarketing.wordpress.com/2012/10/30/behavioral-economics-why-more-is-not-better/

给小团队的特别建议

小团队的普遍现象在于人力紧张,不管是在创业公司还是在大公司内。对于不写代码就手痒的技术人员,如果再在技术上有点儿完美主义情节,那真是可以为代码鞠躬尽瘁的。稍微一整理,事情恨不得已经排到一年后。但是大公司有绩效压力,小公司有生存压力,怎么办?这个事儿就是优先级处理,处理不好,就会变成目标管理,然后慢慢你就跟真的工地搬砖无二,天天还得受工头的鞭子。

如果你像我一样喜欢轻松的自组织团队,喜欢以一当十的挑战,那你更需要注意这点。如果你只是埋头做一件事,很可能错过了系统改进的时间窗口,等系统压力上来或者问题到来的时候措手不及。你只能过四处救火火不断,缝缝补补又一年的生活。而所有的补丁,最后又变成了你彻底修复时要处理的历史包袱。

你最需要的可能是坐下来,想想该做什么,想想先做什么,把有限的精力用在最值得用的地方。

有些时候,做得对才可能做得少。

原文发表于2015年4月12日,原载于 Eric Liang's Notebook

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2016-02-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 一乐来了 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
短信
腾讯云短信(Short Message Service,SMS)可为广大企业级用户提供稳定可靠,安全合规的短信触达服务。用户可快速接入,调用 API / SDK 或者通过控制台即可发送,支持发送验证码、通知类短信和营销短信。国内验证短信秒级触达,99%到达率;国际/港澳台短信覆盖全球200+国家/地区,全球多服务站点,稳定可靠。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档