微服务是一个比较大的话题,基于我的过往经历,本文将以 Netflix 为例,分享一个大型互联网公司如何从一个 Monolithic 的 APP 成功转型到微服务。
文章主要涉及微服务的产生历史,应用场景,与单片服务区别,微服务带来的技术、企业组织结构等方面挑战,以及如何合理地选择单片服务构架和微服务构架等内容。
如下图,是微服务在 Google 的搜索结果:
自 2014 年以来,微服务开始被关注,搜索的人越来越多,并在 2016 年左右达到顶峰。从地域来看,很多国家都在关注,如印度,欧洲等等,并且很多公司在使用微服务构架。以下以 Netflix 为例来分享微服务的演变过程以及带来的挑战。
我 2012 年加入Netflix,从中了解到:
Netflix 从 2008 到 2009 年就开始在自有数据中心做单片的 Web 应用,那个时期是很庞大的 Java 包,无数的程序写在其中,造成很多问题。
2010 年开始把重量级的部分转出。
2013 到 2014 年,其他的模块也陆续转出,并发布很多 Open Source 的微服务工具,在硅谷及全球受到很多人的关注。
直至 2015 年左右,Netflix 基本完成向微服务的转型,也彻底从自有数据中心,转移到亚马逊的云平台。
如下,是微服务示意图:
微服务看上去很复杂,其实它是一个个服务器组成的,这些服务器之间相互连接。
如下图,是 Netflix 在微服务方面的使用情况:
从时间点看,Netflix 是硅谷采用微服务比较早的公司,在采用过程中也受到很多质疑,特别是传统公司,从数据中心迁到云上,需要时间来慢慢接受。
如下,是很普遍的 Monolithic APP 示意图:
从 Browser 到各种公司的 Apache,形成一个包含各种功能的 WAR 包,最后是一个 MySQL 的存储层。这样的方式,比较易于测试,部署方面也很简单。
Monolithic APP 的优点如下:
易于开发。很多 IDE 和框架都支持,比如 Sprint MVC、Ruby Rails、Python Django 等。
易于测试。可以通过简单启动应用程序并使用 Selenium 测试 UI 来实现端到端测试。
易于部署。只需将打包的应用程序复制到服务器。
易于扩展。通过在负载平衡器后运行多个副本,可以轻松地水平扩展。
DevOps 比较简单。一支专门 DevOps 团队负责即可。
Monolithic APP 的缺点如下:
应用程序太大且复杂,很难完全理解并快速正确地进行更改。
应用程序会越变越大,可能会减慢启动时间。
必须在每次更新时重新部署整个应用程序。
如果代码库有新的变化,变化的影响通常不是很清楚,这导致广泛的手动测试。
连续部署很困难。
当不同模块具有冲突的资源需求时,单片应用也可能难以扩展。
可靠性差。任何模块中的错误(例如内存泄漏)都可能会导致整个网站宕机。此外,由于应用程序的所有实例是相同的,该错误将影响整个应用程序的可用性。
采用新技术或框架很困难。由于框架或语言的变化将影响整个应用程序,因此在时间和成本上都是非常昂贵的。
随着代码库,组件和团队规模增长,各种问题相继出现。
主要概括为如下几点:
随着组织的成长,功能的增多以及技术栈的瓶颈出现,需要有新的变革。但面对如此庞大的视频网站,有的程序都是用的 Java 包,自有数据中心,当时还没有微服务的概念,但已经有了把内容拆分出来的意识。
“微”是指团队、代码行数或 API 端口的数目等指标的大小?都不是,不同人对微服务有不同定义。
个人比较赞同这个描述:Loosely coupled service orientedarchitecture with bounded contexts。
关键是 LOOSELY COUPLED和BOUNDED TEXT,LOOSELY COUPLED 意味着每个服务可以独立更新,BOUNDED TEXT 意味着一个服务只要做自己的事情,外界以API等接口。
也就是微服务要实现独立部署,拥有独立技术栈、界定上下文,明确的所有权等特点。
如下,是微服务与单片服务很形象的对比图:
单片服务是把所有的东西放在一个大盒子里,这个大盒子里什么都有。微服务更像是车箱,每个箱子里包含特定的功能模块和物品,所有东西可以很灵活地拆分出来。
也就是说,在 Monolithic APP 中,所有的部件都在一个巨大的软件包中。在微服务的构建下,有很多独立存在的小服务,通过 API 接口连接成大的系统。
如下图,是 Monolithic APP 的架构:
Netflix 会支撑很多设备,最初是所有设备通过一个负载均衡器到一个硕大的、什么都包含的程序中,最后会成为一个硕大的 Oracle 数据中心。这样一来,会产生很大问题,大家都很反感。
如下图,是微服务的架构:
上图可以看出每个服务都可拆分,自有数据源,不一定是 Oracle,可根据业务场景用不同的数据库,完全由各个团队自己决定。
综上是从技术角度分析 Netflix 为什么选择微服务,从商业角度来看 Netflix 选择微服务的原因如下。
有三点原因:
可用性(Availability)。24 x 7 防止单点失败(single point of failure)。在巨大的 CODEBASE 情况下,经常一个小小的错误比如代码中多加了一个冒号就会导致整个程序编译不了甚至引起整个网站宕机。
对于大型互联网公司而言,一定要避免单片服务导致的宕机。
可拓展性。Netflix 当时流量占到美国三分之一,超过 9000W 的付费用户且增长非常快。一旦某部件达到瓶颈时要有迅速可拓展的能力,一般就是添加新机器让它运转。但在传统单片服务上,整个部分绑定在一起,扩展非常困难。
速度。对互联网公司,特别是 ToC 需要快速推出,速度很重要。速度在互联网时代是致胜的关键。
大型互联网公司推行微服务,一旦需要新功能,可立即新开一个微服务,或在几个有限的微服务中进行改动,不需要像原来基于巨大的数据库来改动。
软件构架从单片服务向微服务转型过程中带来了很大的技术挑战。下面选取自认为比较关键的内容进行分享。
主要涉及以下几方面的挑战:
面对这些方面的挑战,分享一些关键技术,如 Service discovery、服务注册、服务注册模式、瓶颈/热点、熔断器和测试等。
对用户而言,最难抉择的是去哪个服务器上取数据,解决方案有客户端发现和服务器端发现两种。
如下图,是客户端发现:
客户端发现,就是客户端布设 Service Instance,存放所有地址、各种信息。客户端接收到数据之后,可自行判断去哪台服务器上获取信息。
如下图,是服务端发现:
客户端不需要写很多程序,而是通过 Load Balancer 把信息转到某个服务器。
服务注册表是服务发现的关键部分,是一个包含服务实例的网络位置的数据库,需要高度可用且是最新的。
服务注册一定不能宕机,一旦出现问题,恢复非常困难。服务注册和发现部分,Netflix 采用的是自研 Eureka 组件。
服务注册模式分为自注册和第三方注册两种:
自注册模式。这种方法的一个很好的例子是 NetflixOSS Eureka 客户端。Eureka 客户端处理服务实例注册和注销的所有方面。
Spring Cloud 项目实现了各种模式,包括服务发现,可以轻松地使用 Eureka 自动注册服务实例,只需使用 @EnableEurekaClient 注释注释您的 Java 配置类。
服务注册模式相对简单,并且不需要任何其他系统组件。但它将服务实例耦合到服务注册表。您必须在您的服务使用的每种编程语言和框架中实现注册码。
如下图,是第三方注册模式:
开源注册器项目—经 Registrator,会自动注册和注销部署为 Docker 容器的服务实例,支持多个服务注册表,包括 etcd 和 Consul。
NetflixOSS Prana 主要用于以非 JVM 语言编写的服务,它是与服务实例并行运行的边路应用程序。 Prana 使用 Netflix Eureka 注册和注销服务实例。
Registrator 的优点是服务与服务注册表断开连接,不需要为开发人员使用的每种编程语言和框架实现服务注册逻辑。
相反,在专用服务内以集中方式处理服务实例注册。缺点是除非它内置在部署环境中,它是另一个高可用性的系统组件,需要设置和管理。
如下图所示,单片服务里请求只有一个,而微服务里很多时候客户端必须通过不同的微服务器才能把数据全部收集起来,请求繁多。
如下图所示,解决的方法就是 Cache:
尽量把已经拥有的数据 Cache 起来,当访问时,优先于 Cache,没有再选择其他部分。
如下图,当遇到整个服务中某个变成瓶颈的情况,就会调用 X,X 要从用户帐户里拿数据。X 调用 Y,Y 也要从用户帐户里拿数据。
这样一来,用户服务会变成一个大大的瓶颈,一旦宕机,最前端的APP一定拿不到数据。
处理方法如下图所示:
Netflix 用的比较多的方法是针对一些共用的数据不进行反复调用,可采用 HTTP HEADER 传递数据。
微服务并不一定能保证可用性,甚至有时微服务做不好更容易宕机,所以一定要采用一些好的容错机制。
所谓容错,原则上说就是当错误发生,尽可能让一台服务器宕机。常见的解决方式有 Time out、Circuit breaker(熔断器)和 Bulkheads (舱壁)-Reject new request 三种。
熔断器在 Netflix 用的比较多,如下两图所示,为熔断流程:
熔断器主要应用在当一个服务去它的下游服务拿数据时,不应该直接拿,要通过一个熔断程序。
当下游服务出现错误,或延时很长时间,熔断器就停止再到下游服务拿数据,直接返回。熔断器也会不断进行判断,服务是否恢复,恢复之后才会继续拿取数据。
这样一来,问题就会在某个地方被阻挡,不会出现服务接连问题,导致微服务出现大规模崩溃的现象。
测试是比较头疼的环节,特别是微服务架构端到端变得特别困难,主要原因是几百个服务隶属于不同的团队。
个人比较推荐单元测试(Unit Test)、服务测试(Service Test)。而端对端测试(End to End Test)要尽量避免,可以通过一些监控工具来完成。
还有 Netflix 采用 ChaosMonkey 工具来对延迟和服务可靠性进行测试。这里值得注意的是,每个微服务至少要有三个实时备份,以免宕机后无法恢复。
之所以分享微服务带来的企业组织结构挑战,是因为归根结底还是人在做事。微服务是去中心化,让每个服务有独立权,这样会导致组织结构发生很大的变化。
企业的组织构架往往会反映在技术构架中,微服务在企业内部是否能够成功,很大程度上取决于企业的组织构架和技术构架是否能够匹配。
以 Netflix 为例,分享在向微服务构架转变的过程中对团队和企业构架带来的挑战。
Netflix 在考量以速度还是以效率为优化核心,最终选择了速度。因为速度是赢得市场最重要的因素,速度意味着了解客户需求,并以比竞争对手更快的速度给予他们想要的东西。在竞争对手准备跟进的时候,已经转到下一组改进。
速度和效率是什么关系呢?强调效率通常意味着试图控制开发过程的整体流程,以消除重复的工作并避免错误的同时注意降低成本。
常见的结果是,注重节流,而不是开源。如果你说“我这样做是因为它更有效率”,那么这个意想不到的结果就是让你慢下来。
这不是鼓励浪费和重复开发,但是应该先优化速度,效率成为次要。提高效率不是一个企业的终极目标,提高效率要以业务增长更快为结果。
尽量增加每个微服务团队的自由度,如开发工具,开发流程等方面,然后以结果为导向。
Netflix 有三个框架,其中 Java 占主流,每个团队可根据自身情况选择技术构架,甚至数据库也可选择 MySQL 或者 NoSQL。
尽量减少流程,为什么有流程呢?流程往往是对过去的事情的总结,如对一些错误,经验总结,之后用这个进行流程控制。
但过多的流程会减慢对新生事物和突发情况的反映速度。还有要明确每个团队的目标,减少互相依赖关系。
如下图,是传统公司的产品开发流程:
大多数软件开发团队呈孤岛状,他们之间没有人员重叠。软件开发项目的标准过程从与用户体验和开发组的产品经理召开会议开始,一块讨论新功能的想法。
在代码中实现该思想之后,代码被传递给质量保证(QA)和数据库管理团队,经常需要进行很多沟通。与系统、网络和SAN管理员的沟通往往是通过内部的 TICKET 系统,导致整个过程非常缓慢。
有些公司试图用初创公司的形式开发产品,但初创公司开发团队并不一定是微服务开发团队。公司虽会有很多个小的初创公司形式的小团队,但是每个团队内部结构还是和传统公司的团队结构一样。
如下图,是微服务产品研发团队:
微服务产品研发团队没有不同的产品经理、UX 经理、开发经理等,在其孤岛中向下管理。
每个产品功能(实现为微服务)都有一名经理,他负责监督一个团队,从构思到部署来处理各个方面微服务的软件开发。
平台团队提供产品团队通过 API 访问的基础架构支持,在整个过程中,都采用 DeVop 形式发布和维护产品。
并不是所有的场景都适合微服务,要根据实际情况,选择微服务或单片服务:
LinkedIn 资深软件工程经理
前乐视美国视频平台技术总监以及Netflix 视频内容平台技术负责人。有 15 年以上互联网公司(LinkedIn、乐视、Netflix 以及 PayPal)技术开发、构架以及团队管理的经验。主要负责的领域是高并发后端服务构架、微服务构架、大数据平台构架等,以及端对端的整个产品开发。感兴趣的领域是视频,支付,互联网金融以及电商领域。