Service performance 101

Service performance 101

陈天陈天

最近一个月,我和一小撮不专业的美国银行从业人员就「我是不是清白地像于谦那样」(写石灰吟的于谦,不是有三大爱好的那位)反复拉锯,又赶上一大波功能上线和融资相关的事情,忙碌地无心在火车上干活,懒散了,便从周根霞退化成了岳根霞。公众号后台和微信里满满地不理解:刚置顶你丫,你丫就耍流氓(这话似乎几个月前听过)。后来苹果微信开撕,有些小伙伴恍然大悟,连忙安慰:叔,不哭,我左苹果,右三星,华为插腰间。早中晚餐加蛋的事业不会给你断了 —— 这特么把我当什么人了,我一秒钟几十万上下。。。加一个蛋怎么够!

有关 Tubi TV 的 press release 终于出来,华尔街日报techrunch36kr 都报道了我们,该上线的重点功能上线了,绿卡/EAD/AP 这些烦心事该处理的也都处理了,这下再不写点什么有些说不过去了。所以今天我们讲讲和性能(performance)相关的主题。这是我在我的 team 的 all-hands meeting 上讲的一个主题。

谈起 performance,不能不提 netflix 的 performance architect Brendan D. Gregg。Brendan 写了很多很多高质量的文章,在 youtube 上有不少关于 performance 的视频,如果能从他的分享中吸取一二,修为会大大精进(想想风老先生之于令狐冲)。我的这个 slides 里借鉴了他的好几幅图片,其实主要的思想也来源于他的若干视频和文章,在此隔网道谢。

什么时候该严肃地考虑性能问题?

这是很多创业团队关注的问题。其实对于创始人来说,这个问题等同于,什么时候我该找个「真正的」CTO?

我觉得,真实的用户量在百万以下,商业模式还没有构建起来之前,性能还不是首要的问题,有几个靠谱的工程师把服务搭起来,UX 体验好,性能过得去,就 OK,发力点在用户获取,留存和商业模式。在我加入 Tubi TV 以前,我们没有 CTO,没有 VPE,engineering 有很多问题,代码一塌糊涂,API 的 response time 常常在数百 ms 到 10s 巨幅震荡,比 A 股还骇人。但这并不妨碍 Tubi TV 成为一个商业上比较成功的公司。然而,当我们去年 MAU 增长 9 倍以上,用户的体量和设备的体量在数百万和数千万时,性能优化(甚至系统重构)就成为工程师工作的重中之重。

如何衡量和优化服务的性能?

要衡量和优化服务的性能,我们可以从四个步骤入手:测量(measure),分析(analyze),剖析(profile)以及改进(improve)。

测量(measure)

Lord Kelvin 有句话我在很多场合引用:if you can't measure it, you can't improve it.

在开始讨论测量的手段前,我们先对时间有个基本的概念:

1ns 是一个很小的时间单元,1GHz 的 CPU 每个 cycle 是 1ns。CPU 的 L1/L2 cache,都在纳秒级。写代码时,你的每个 if/else/switch,在机器指令级别,都是各种 cmp(compare)之后的 jmp(jump),CPU 会尝试预测分支的走向,一旦预测失败,代码的执行会付出额外 5ns 的时间。

ns 是 CPU 级别的时间概念。

如果指令要访问的数据不在 L1/L2 cache,每次主存的访问,要花上 0.1us。zip 压缩 1kb 内存中的数据 3us,我在 Policy Engine 的前世今生 那篇文章里讲到的我们的 policy engine 的算法,每个视频是否允许在某个国家,某个时间和某个平台下播放的 policy check,在 7us 左右,而在 1Gbps 的本地网络发 1k 的数据,是 10us。

1 us 是内存级别的时间概念,然而顺序读取 1mb 的内存,250us;10 us 是本地高速网络的时间概念,报文在 data center 打个来回,500us。

10 ms 是 disk 级别的时间概念。然而,如果使用 SSD,顺序读取 1mb,1ms 而已,仅仅是内存的 4 倍。

报文在美国西海岸和欧洲之间打个来回,150ms 就轻易耗出去了,所以无论你服务的性能多好,哪怕能优化到 1ms 内,roundtrip 的次数越多,性能就越差。这便是我们静态内容使用 CDN,动态内容(如 API)将服务器部署到离大部分用户近的 data center 的原因。

从时间的量级来看,如果你的服务的响应时间已经优化在 100ms 的量级,那么,优化网络要比继续优化服务本身更有效率。

有了时间的概念,我们看看有哪些测量的手段。

系统级:

Branden 的图表详细到把系统的每一层,每一个组件都标注上了相应的工具。这里面,80% 的工具我没用用过,只是听过而已,但这个列表的好处是当需要时,你可以进行非常细致的,有目的性的指标的收集。

VM 级我们可以收集 VM 自己的统计数据,包括但不限于 VM 的 CPU utilization,GC metrics,VM allocator metrics,scheduler metrics,IO metrics。这个因语言而异。

app 级则跟具体的应用有关,但无非是事件,消息和关键函数调用的计数,错误和错误率,服务的响应时间等等指标。这些指标的收集需要花心思设计和实现。

之前我有篇文章 测量,虽然和本文关系不大,但也很有意思,可以翻出来看看。

分析(analyze)

系统级别的分析可以看这幅图:

VM 级别的工具千差万别,对于 erlang VM,我们可以用 etop,pman,observer,wombatOAM 等等工具。

app 级我们可以使用很多第三方的工具,比如:datadog,sentry,crashlytics,periscope 等。

对收集的数据(metrics)的分析,可视化是非常重要的一环,收集到了数据却没做好可视化,会严重影响分析的效率,然而可视化也是一把双刃剑,同样的数据使用不同的方式去可视化(实际上是一种预先的解读),那么可能会得出完全相反的分析结论。

剖析(profile)

在对性能进行了基本的测量,对系统的瓶颈有个概念之后,我们可以着手改进。很多时候,性能分析的结果可以为我们进行性能调优直接提供指引,然而有些时候,我们知道系统的某部分性能出了问题,但还是不太清楚具体该怎么入手,这时,profiling 可以帮助你定位出那些耗时长的路径或者函数,可以逐一破解之。

换句话说,profiling 解决「我想优化这个路径,请告诉我在整个调用栈上,各个相关的子路径的占比」这样的问题。

Profile 的另一个应用场景是对系统的某个部分有更清楚的认知。通过 profiling,我们可以了解到某个关键路径的完整调用关系,哪些函数被调用了多次(超出想象),哪些函数的执行时间颠覆了我们的猜测等等。

常见的 profiling 工具,有:gprof,fprof,systemtap 等,erlang VM 有 cprof,eprof,fprof,lcnt,percept 等等。

说句题外话,有时候选语言也是选工具链,在这点上,erlang 真心是业界良心啊,几乎你所熟悉的 OS level 的工具,erlang 都能找到对应的工具,比如 etop 之于 top,inet:i() 之于 netstat,dbg:trace 之于 strace,等等。而且 erlang 做 profile 太酸爽,如果你在 C 代码的 code base 做过,你会大呼竟然 erlang 的 fprof 不用像 C 的 gprof 那样,动辄需要重新编译相关的部分来 inject code;如果你在 OSX 尝试过对 nodejs 做 profiling,你会羡慕死 erlang 两三句话就可以完成 profiling,进而生成 flame graph。

flame graph,火焰图,是 profiling 结果的一种可视化,下图是我做一个 elixir service 时分析其关键路径生成的火焰图:

通过它,我们可以很容易了解一条路径走下去,哪些调用占用的 CPU 多,然后深入分析这些调用并优化之。

同样的,我们也可以生成 call graph:


改进(improve)

抓到要优化的点之后,其实性能优化之路已经完成90%,接下来改进有很多方法。这是我 slide 的截图,很好理解,我就不解释了:


先写这么多。本文是篇启发性的文章,而非知识性的文章,是个引子,希望它能引发你对性能的重视,以及对性能提升的方法有一个基本的认知。更详细的内容请戳 这里,观看我的 sldies(和内部分享相比有删减)。

「真诚赞赏,手留余香」
1 人赞赏
李夏驰
文章被以下专栏收录
2 条评论
推荐阅读