Uber微服务实战经验分享

几个月前,Uber决定退出其基于模块的整体monolithic架构,转向灵活的微服务架构,此后,uber花费了数千个工程小时重写扩展微服务生态系统,使用各种语言和各种框架,这种持续的重构是一项艰巨的任务,在这里讨论一下在Uber采取新技术带来的机会和挑战,使用适合SOA迁移的一套技术堆栈和标准,Uber已经简化了服务的开发。

下面是其原文:
Rewriting Uber Engineering: The Opportunities Micr,大意如下:

启动一个新的服务
在工程规模快速扩展期间,很难跟踪所有的工作行为,这种增长方式迫切呼唤一种机制来防止不同团队的之间重复工作,在Uber,通过要求新服务的作者必须提交一个请求注解(Request for Comment,简称RFC)解决了这个问题。

新服务注解实际提出了对新服务的高层次目标,概述服务的目标,架构和依赖以及其他Uber工程师讨论的实现细节。

RFC服务两个目标:
1).征求反馈意见以提高服务的开发质量。
2).防止重复的工作或重复暴露合作的机会。

几个熟悉该领域的工程师负责审查服务的设计,一旦反馈已被纳入到服务目标,建设这个新服务的“好玩游戏”(比喻具体实施过程是一个有乐趣的游戏)就启动了。

实现一个新服务
tincup是货币和汇率的服务案例,使用微服务实现的,Tincup是一个及时更新现金和汇率数据的接口,它提供两个端点服务:一个是获得货币对象,另外一个是获得该货币汇率(对美元的汇率),这些端点是必要的,因为Uber是一个全球业务,汇率变化经常发生,在Uber可以进行将近有60种货币的交易。


步步为营使用新技术实现微服务
当构建Tincup时,重写货币汇率所有逻辑提供了一次重新评估以前设计决策的机会,因此,使用了一系列新框架 协议和方式实现了Tincup。

MVCS

首先,我们讨论了有关货币汇率代码的整体结构,过去已经多次修改了很多数据持久层,每一个修改都是漫长和繁琐的,从这个过程中,Uber认识到,如果可能的话,最好是将应用程序逻辑从持久层细节中分离出来。

这导致了一个应用程序的开发方法,称为MVCS: 拓展了普通的MVC模式,将服务层包含到MVC中,MVCS中的S就是Service服务的意思,服务层是应用逻辑所在,通过将应用逻辑从其他地方分离到服务中,持久层能够自我演进发展或被替代,这些都不会需要再重构业务逻辑了。


UDR
其次,我们考虑了货币汇率这个系统的持久层,在之前的Tincup中,数据是保存在关系数据库PostgreSQL数据库,使用自增的ID,这种数据存储方式不能允许跨Uber全球多个数据中心复制数据,因为货币汇率需要从全球所有数据中心访问,因此更好持久层到UDR,这是Uber自己的全局复制可扩展伸缩的数据存储。


微服务增长的预关注
在货币汇率系统决定了新的设计变化以后,关注点开始聚焦于微服务在工程产品领域的不断增长上。

Tornado
阻断网络I/O是一个严重问题,可能会导致uWSGI任务的饥饿,如果在Tincup所有向服务的请求都是同步的,一个服务的崩溃风险会引起多个连锁反应,影响所有调用者,因此决定采取Tornado,这是一个基于事件循环的异步Python框架,能够防止堵塞,因为是从过去整体代码库中分离出了很多小代码,因此选择异步框架可以使得很多应用程序逻辑能保持不变。

TChannel
曾经一个API调用可能会波及大量的服务调用,为了促进大型系统中服务发现和单点风险的识别能力,Uber微服务使用了开源的Tchannel而不是Hyperbahn,后者是一个内部的多路RPC框架,而TChannel提供了客户端和服务器端的协议,使用hyperbahn智能路由网连接客服两端,解决了微服务中以下问题:

1). 服务发现。 所有生产者和消费者将自己注册到路由主机。消费者通过名称访问生产者,而不是需要知道对方的主机或端口。

2).容错。 路由主机使用类似故障率和SLA进行测量。可以检测到不健康的主机,并随后将它们从可用主机池中删除。

3).限流和断路器。 这些功能确保坏的请求和缓慢的响应不会引起连锁故障。

Thrift
因为很多服务快速增长,有必要维护良好定义的结构供调用,使用IDL来管理结构,决定使用Thrift,能够强迫服务拥有者发布严格的接口定义,简化服务集成过程,不遵守接口的调用在Thrift级别就被拒绝了,而不是向服务泄漏,并导致代码中更深层次的失败。公开声明你的界面的这一策略强调了向后兼容性的重要性,因为服务的Thrift接口的多个版本可以在任何指定时间使用。服务的作者不能进行断崖式的变化,而只能使非断崖式的延续的兼容的接口定义,直到所有的消费者都准备好弃用。

为Tincup大规模生产使用准备
tincup接近完成时,我们使用一些有用的工具来准备生产环境tincup:

Hailstorm
我们意识到:Uber的流量是随时间变化。能在预期的时间看到巨大的流量峰值,像新年前夕和万圣节,所以我们必须确保服务可以处理这个突然增加的负载,建立了内部Hailstorm服务用于测试tincup端点负载和确定缺陷以及短路点。

uContainer
有效率使用硬件是Uber工程目标,因为Tincup是一个相对轻量的服务,它可以很容易地与其他服务分享共用的同一个机器。分享是重点关心吗?嗯,不总是,我们还是仍然希望确保每个服务独立运行,不影响在同一台机器上运行的其他服务。为了阻止这种问题,我们使用uContainer一种Uber的Docker来部署Tincup,实现资源隔离和限制。

ucontainer利用Linux容器容量和容器化Uber服务。它将一个服务封装到一个孤立的环境中,以保证该服务将始终运行,而不管同一主机上的其他运行过程。ucontainer延伸Dcoker的能力,增加两点:
1)更为灵活的构建功能
2)更多的Docker容器可见性工具。

uDestroy
为了准备不可避免地出现在生产中的中断和网络连接问题,我们使用了一个内部的工具称为udestroy,用来释放服务的调用控制混乱。通过模拟冲突和破坏,获得了系统的弹性的可见性。因为我们定期故意破坏我们的系统,可以发现漏洞并不断努力提高耐久性。

事后教训
通过构建Tincup吸取了拓展SOA的几个教训:

1.迁移消费者(服务调用者)是一个漫长的,缓慢的过程,所以尽量让它变得容易。提向迁移者提供供代码示例以及预算时间。

2.最好在一个小服务中学习一个新的技术栈。tincup的应用逻辑是非常简单的,它可以让开发者专注于学习新的技术栈,而不是详细的迁移业务逻辑。

3.投入时间用于开发额外的单元和集成测试代码。如果在开发环境中完成完成测试,代码的调试问题要容易得多(少有压力!)。

4.负载测试要尽早且经常。没有什么比发现你的系统不能处理高峰流量更坏的事情,尤其你已经花了几个星期或几个月的具体实现以后。


Uber的SOA迁移提供了很多人拥有自己服务的机会,即使是那些只有有限的行业经验的人。拥有一个服务意味着一个很大的责任,但Uber的开放、知识共享文化使学习新的技术和拥有一个代码库变成一种荣耀和宝贵的经验。