[关闭]
@liuhui0803 2016-10-15T01:32:12.000000Z 字数 9008 阅读 2136

优步通过2000名工程师,1000个服务和8000个Git代码库学到的经验

Uber 文化 架构 微服务


摘要:

目前优步已落地全球70个国家和地区的400多个城市,员工总数超过6000人,其中2000人是工程师。这些工程师开发了超过1000个微服务,而这些服务存储在超过8000个Git代码库中。如此短的时间里经历十倍以上的疯狂增长,有幸体验过这种经历的公司并不多。面对这种独一无二的扁平式快节奏增长,这种孤注一掷的体验无疑可以带给我们一些全新的,比以往任何时候更为深入的经验。

正文:

通过这则视频的前几秒内容可以很直观地感受到优步业务的飞速增长。这段视频来自优步首席系统架构师,兼Voxer共同创始人Matt Ranney所做的一场名为在将优步扩张至包含1000个服务之前,我希望自己“早知道”的那些事的精彩演讲(幻灯片)。

中国的几个城市道路交通正在不断经历快节奏的波动式增长,其实全球各大城市都在经历这样的增长模式。目前优步已落地全球70个国家和地区的400多个城市,员工总数超过6000人,其中2000人是工程师。仅仅在一年半之前,他们的工程师总数刚满200人。这些工程师开发了超过1000个微服务,而这些服务存储在超过8000个Git代码库中。

如此短的时间里经历十倍以上的疯狂增长,有幸体验过这种经历的公司并不多。面对这种独一无二的扁平式快节奏增长,这种孤注一掷的体验无疑可以带给我们一些全新的,比以往任何时候更为深入的经验。

Matt对这一切已经很熟悉了。作为Voxer的共同创始人,他以前也经历过飞速增长,但这一次与以往有所不同。看过视频就会知道,Matt试图向以往取得的成果让步。

Matt一直以来都是个爱思考的人,在最近的一次采访中他提到:

在QCon和其他类似活动中发言的很多架构师让我感觉自己很不称职,很多人- 例如谷歌的员工 – 可以解决各种对我来说束手无策的问题。

这次讲话似乎将Matt从漩涡中拉远了一点,试图让他的经验更有意义,借此克服各种问题。好在他成功了。

在这段蕴含智慧又包含忏悔的讲话中,Matt说:“很多错误其实早就显现苗头了,”而他学到的教训也源自这里。

这次讲话主要围绕WIWIK(What I Wish I Had Known,希望自己早知道的那些事),而这似乎已经成为近来的网络流行语。他针对一年半之前那个更年轻,也更天真的自己提出了很多宝贵的建议,当然和我们所有人一样,当时的他肯定是听不进去的。

这是我们所有人的通病。近来很多人在批评优步(HackerNewsReddit)。毕竟从数字来看,他们的发展实在是太疯狂了。两千位工程师?八千个代码库?一千个服务?他们的做法肯定在某处出现了大问题,对吧?

也许吧。出人意料的是,对于所有这些事Matt并未采取批评的态度。他所采取的探究方法更多地偏重于质疑和研究,不仅仅是独断专行。他本人对海量代码库似乎也感觉很困惑,但他会对更多或更少代码库的利弊进行权衡,而不光是直接断言哪种做法更好,毕竟考虑到优步的具体情况,又该怎么定义“更好”呢?

优步涉足的是一场全球范围内的倾斜战争,他们需要构建一套全球化规模的系统,借此赢得“赢家通吃”的市场。他们的业务模式就是如此,他们的目标是成为业内最后一家屹立不倒的服务商。这种情况下“更好”意味着什么?

赢家通吃意味着首先需要非常快速地增长。虽然也可以用更有序的方式慢慢增长,但速度太慢肯定会输。因此只能站在风口浪尖上进行权衡并试水,或者可能需要一头扎进混乱的局面中,只有这样才能继续扩张并让业务占领全球。这本就是一条快车道,需要采取孤注一掷全身心投入的战略。还有谁觉得自己能做的更好?真的吗?

考虑到优步的目标,微服务是一种非常适合的方法。无论是否接受,这就是一种康威定律(Conway's Law,译注——康威定律:人力组织的架构往往决定了设计的层次,详见:http://www.infoq.com/cn/articles/every-architect-should-study-conway-law),开发那么多服务是因为,只有这样才能雇佣那么多人并让大家都有活干。

服务多,不是技术原因导致的;代码库多,也不是技术原因造成的。这都是人的问题。Mranney的总结很犀利:

流量的扩张不是问题所在。团队和产品功能发布速度的扩张才是主要原因。

这类探讨通常有一个亘古不变的主题:这个或者那个很棒,可是需要权衡,但令人惊异的是,这样的权衡通常只能在达到一定规模之后才会体验到。我从他们的讨论中产生的两个最大的想法也正是基于此:

作为比文字更有力的交流方式,只有在看过视频之后才能更好地理解这些观点。不过我对这些视频的总结依然还是值得一看的 :-)

目前状态(2016年4月)

优步已经落地全球400多个城市。
涉足70个国家和地区。
员工总数超过6000。
工程师总数2000人(一年半之前刚刚达到200人)。
1000个服务(大概值)

Git中创建了超过8000个代码库。

微服务

将整体式的系统拆分为多个小组件是种不错的做法。甚至“整体式”这名字听着就很糟,但微服务也有不好的一面。

对系统进行改动的时候最容易出错。优步的服务通常在周末最稳定,因为工程师都在休息,尽管周末也是业务最忙的时段,但服务始终很稳定。

每次进行任何更改都需要承担一定的风险。那么微服务一旦上线就再也不要修改是否是一种更好的做法?也许吧。

好处

显性成本

不那么明显的成本

使用大量不同语言所产生的成本

历史回顾:最开始优步的开发工作是全部外包的。从技术上来看这算不得什么问题,于是他们找人开发了第一版移动应用和后端系统。

后续处理:开发工作转为由公司内部人员接手后,最开始他们使用了Node.js,现在已换为Go。

核心服务:系统的其他部分最初用Python开发,现在也已换为Go。

地图服务最终也转为内部人员接手,相关团队使用了Python和Java。

数据工程团队使用Python和Java写代码。

内部衡量系统使用Go编写。

看到了吧,他们使用了多种语言,这一切都要归功于微服务。

不同团队可以使用不同的语言来开发,而不同语言编写的服务依然可以相互通信。有效,但也需要付出代价:

希望自己早知道:使用不同语言会导致企业文化的碎片化。全面拥抱微服务将导致各自为营。有Node营,有Go营,还有其他营。人们会本能地出于共同特征聚集在一起,但多语言战略是需要付出代价的。

RPC的成本

团队之间可以通过RPC相互通信。

当越来越多的人在大规模环境中非常快速地联合在一起之后,HTTP的薄弱之处开始显现。例如状态代码是什么?标头是什么?查询字符串里要放些什么?是RESTful的吗?用什么方法?

这一切在浏览器编程领域显得很酷,但在服务器编程领域会变得极为复杂。

你真正想要的只是运行相关功能并获得需要的结果。但是在HTTP/REST体系中你会遇到各种不易察觉的解释问题,这一切都需要付出不菲的代价。

JSON是很棒,无需额外的工具就可以直接读取,但如果没有恰当的类型,那也只是一堆杂乱无章的代码。先别急,下文还将详细讨论。当有人改了一些服务,而下游还有其他服务需要通过这些服务对空字符串或Null值进行解释,或者某些类型要求必须使用某种语言而不能用其他语言,这就会造成极大的混乱,可能要用很长时间才能理顺。通过不同接口的类型应该可以解决所有此类问题。

RPC的速度远远比过程调用更慢。

希望自己早知道:服务器并不是浏览器。

代码库

代码库数量以多少为宜?也许很多人并不赞同,但他认为只使用一个是最好的。

很多人认为同时使用多个代码库是最好的做法,至少每个项目一个,或者每个项目多个。

使用多个代码库,这种做法复合“使用多个小模块”的行业趋势。小模块更易于开源或更换。

只使用一个代码库也很好,因为这样可以更容易地应用交叉改动。如果想要进行改动,可以很容易找到所有需要修改的代码。此外这样也可以更方便地浏览所有代码。

这样做的劣势在于会对构建系统造成较大压力,并影响到在代码中进行导航的能力。确保交叉改动的恰当应用也是一个痛苦的过程。

另一个劣势在于代码库会逐渐变得极为庞大,以至于除非预先搭建一套非常精巧的系统,否则根本无法构建甚至签出自己的软件。在缺乏特殊工具的情况下,单一代码库的做法也不现实。谷歌就使用了单一代码库的方法,但他们会通过一种虚拟文件系统让用户觉得自己可以将整个代码库签出。

超过8000个Git代码库,一个月前才只有7000个。

代码库的数量实在是太大了。

运维问题

出错之后怎么办?在大型的微服务部署中会遇到一些令人吃惊的问题。

如果其他团队被你的服务影响而该服务还没有准备好发布,是否可以由其他团队为你的服务打补丁并发布?

小规模团队是一种好做法,大家都可以更快速地前进,所有功能都可以非常迅速地发布,但有时候你必须明白,整个系统实际上是一个相互连接的有机整体,在将整个系统拆解为大量微服务的过程中往往很难意识到这一点。

性能问题

鉴于相互独立的微服务层出不穷,迟早会遇到性能问题。

RPC成本很高,尤其是在使用多种语言的情况下,此时该如何理解性能问题将完全取决于各种不同的语言和工具。

你让大家使用自己惯用的语言编程,现在应该已经意识到跨越不同编程语言的性能问题是个真正的挑战。

可以试着通过Flame Graphs让所有语言使用一种通用的仿形格式(Profiling format)。

在你想要理解系统性能的时候,找出性能问题根源的最大阻力在于工具之间的差异之处。

大家都有仪表盘,但如果仪表盘并不是自动生成的,只是由不同团队将自己认为重要的东西放在仪表盘上,在你想要确定问题根源时可能会发现,每个团队的仪表盘上显示的内容竟然截然不同。

每个服务在创建时都应该有一个标准的仪表盘,显示同一套有用的数据。任何人都应该能轻松创建仪表盘。这样在你查看其他团队的服务时就可以看到相同的内容。

希望自己早知道:一流性能并非必须的,但你需要知道自己的实际情况。

Fanout问题 – 追踪

Fanout会造成大量性能问题。

假设有个典型的服务,99%的时间可以在1ms内响应,1%的时间可以在1秒内响应。还不算太糟。用户只会在1%的时间里感觉到慢。

假设这个服务遇到了严重的Fanout,需要调用大量其他服务。响应速度变慢的几率很快将大幅增加。如果使用100个服务,63%的时间里响应速度将至少为1秒钟(1.0 - 0.99^100 = 63.4%)。

你可以通过分布式追踪机制追踪Fanout问题。但如果无法理解请求在整个架构中行进的细节,Fanout问题也会显得非常难以追踪。

可以通过一个棘手的案例来体会一下。顶层针对同一个服务产生了大量Fanout。单看这个服务本身很正常,每个请求都快速且一致,问题在于这个顶层服务会获得一个ID清单,并针对每个ID调用该服务。就算调用会并发进行,这个过程也需要很长时间。使用批处理命令吧。如果不进行追踪可能很难发现这个问题。

另外还有个例子:某个服务需要发出数千个服务调用。虽然每次调用都很快速,但大量调用导致该服务运行缓慢。最终发现在遍历列表更改属性时,这些操作很神奇地变成了数据库请求。虽然数据库团队称数据库运行正常,因为每个操作速度都很快,但大家奇怪的是为什么会执行这么多操作。

追踪工作的庞大开销也会影响到结果。追踪需要做很多工作,方法之一是不要追踪所有的请求,只要追踪请求中具备统计学意义的部分即可。优步只追踪了1%的请求。

希望自己早知道:追踪需要跨语言的上下文传播(Context propagation)。

日志

考虑到有不同语言,大量团队,以及大量新人,有超过半数的工程师团队成立时间都不超过6个月,可能大家都会趋向于通过不同的方式记录日志。

“强制(Mandate)”这个词很诡异,但却是我们真正需要的,我们可以强制使用统一的日志记录方法。但更易于让人接受的做法是提供简单易用的工具,让人们无须考虑使用其他方式就能获得一致的结构化日志。

多种语言的使用会让日志工作更困难。

遇到问题后,因为记录了太多信息,日志本身也可能让问题变得更严重。日志满载后必须能删除日志项,希望系统能早点具备这样的功能。

希望自己早知道:有种说法认为需要统计日志消息的大小,这样才能知道过多的日志数据是谁生成的。

负载测试

希望将服务发布至生产环境之前进行负载测试,但无法构建出与生产环境同等规模的测试环境。

生成能对系统所有组件进行测试所需的现实测试数据,这本身也会遇到一些问题。

解决方案:在非峰值时间通过生产系统进行测试。

这会造成大量问题。

所有衡量指标都会被这种测试放大。

希望自己早知道:我们真正希望做到的,是在所有时间对所有服务进行负载测试,因为很多问题只会在流量峰值的时候出现。

失败测试

希望自己早知道:并非所有人都喜欢失败测试,例如Chaos Monkey(译注——“混世魔猴”,Netflix自行设计的一个软件,可随机模仿基于云的系统发生的故障,以了解这些系统的健壮性),尤其是如果需要在事后添加这样的功能时。

迁移

所有东西都在“老化(Legacy)”,重点在于迁移。大部分负责存储的人,他们唯一的工作就是将数据从一个老化的系统转移到另一个老化程度不那么高的系统中。

有些人始终在将某些东西迁移到另一个地方。无论开会的时候具体怎么说,大家其实都在这样做。

老化的东西需要继续工作,业务需要继续运转。维护窗口这个概念已经不存在,剩下的只有“可接受的停机时间”。

随着业务走向全球,已经不存在非峰值时段。任何时候,总有某个地方处于峰值时段。

希望自己早知道:强制迁移是一种糟糕的做法。

开源

“自建还是外购”这种权衡已经没人买账了。什么时候需要自建?什么时候又需要外购?

任何基础架构组件,任何可能是平台组件而非产品组件的东西,在某种程度上已经逐渐开始成为稀松平常的市售产品。亚马逊(或其他厂商)会将这些东西以服务的方式提供给你。

最终你投入大量时间精力维护的东西,别人可以用更低廉的成本以更好的效果获得。

希望自己早知道:如果有人在负责平台类型的某种功能,突然听到亚马逊将这种东西以服务的方式提供,此时肯定会感觉不妙。

权术

将一切拆解为小的服务,这样大家就可以玩弄权术了。

当你做出的决策依次伤害到公司 > 团队 > 自身的权益时,权术就产生了。

权术不仅仅是那些你不喜欢的决策。

为了接纳高度模块化的快速开发方法,需要非常快速地招募人员,并尽可能以最高速度发布功能,这些动机会导致你开始伤害其他对象权益。

当你以“通过多个小规模的成果塑造大规模的成就”这种方式实现自身价值时,此时将很难确定怎样做对公司而言才是最好的。让人吃惊的权衡,对吧。

权衡

一切皆须权衡。

希望自己早知道:如何特意做出更好的权衡。

相关文章

作者:Todd Hoff阅读英文原文:Lessons Learned From Scaling Uber To 2000 Engineers, 1000 Services, And 8000 Git Repositories

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注