架构与微服务本质论
为应对如今无线优先和全渠道用户体验的需求和挑战,我们该如何设计灵活的面向体验的微服务架构?它有哪些模式和最佳实践?携程,Netflix和SoundCloud这些知名互联网公司是如何实践面向体验的微服务架构的?在过去的时间里,大牛马丁福勒对微服务有哪些新的观点?
微服务各家玩法不尽相同,我发现一些术语的叫法各公司也是不同的,可以说微服务目前仍在激烈的演化中,这个领域还未成熟和标准化,所以今天的分享主要是我个人的视角和总结,目的是抛砖引玉,激发大家进一步探索和实践。
本次分享的主题包括:
微服务架构原理
用户体验适配层(Backend For Frontend)
携程无线微服务案例分享
Netflix微服务架构分析
SoundCloud微服务架构分析
微服务架构不是免费的午餐
总结
本次分享也是应架构群内一些朋友的要求,将之前零散分享的内容总结成文,本次分享过程中会贴出相关内容参考链接(其中有些来自SlideShare和Netflix techblog的链接需要FQ访问),供有兴趣的朋友进一步学习。
一、微服务架构原理
微服务是个新概念,但它没有一个明确的定义,各家对微服务的描述不尽相同,本人更倾向于用一些架构原理来描述它,因为架构原理是相对抽象和稳定的,而具体实现可以千差万别。微服务原理和软件工程,面向对象设计中的基本原理相通,体现如下:
单一职责(Single Responsibility),一个服务应当承担尽可能单一的职责,服务应基于有界的上下文(bounded context,通常是边界清晰的业务领域)构建,服务理想应当只有一个变更的理由(类似Robert C. Martin讲的:A class should have only one reason to change),当一个服务承担过多职责,就会产生各种耦合性问题,需要进一步拆分使其尽可能职责单一化。
关注分离(Separation of Concerns),跨横切面逻辑,例如日志分析、监控、限流、安全等等,尽可能与具体的业务逻辑相互分离,让开发人员能专注于业务逻辑的开发,减轻他们的思考负担,这个也是有界上下文(bounded context)的一个体现。
模块化(Modularity)和分而治之(Divide & Conquer),这个是解决复杂性问题的一般性方法,将大问题(如单块架构)大而化小(模块化和微服务化),然后分而治之。
微服务架构同时还是一个组织原理的体现,这个原理就是康威定律(Conway’s Law),Melvin Conway在1968年指出:“Any organization that design a system(defined broadly) will produce a design whose structure is a copy of the organization’s communication structure”,翻译成中文就是:设计系统的组织,其产生的设计和架构等价于组织间的沟通结构。Dan North对此还补充说:“Those system then constrain the options for organization change”,简言之,这些系统在建成之后反过来还会约束和限制组织的改变。下面两个图进一步解释了这一组织原理:
Fig 1,团队结构和系统架构不匹配
一般企业刚起步时,业务规模小,开发团队规模也小,所以通常开发出来的系统是单块的;随着业务的扩大,团队规模也会随之扩大,这个时候多团队组织架构和单块系统架构之间就会产生不匹配问题(沟通协调成本增加,交付效率低下等等),如果不对单块架构进行解耦和调整以适应新的团队沟通结构,就会制约组织生产力和创新速度。
Fig 2,团队结构和系统架构匹配
把单块架构按照业务和团队边界进行拆分,重新调整为模块化分散式架构(微服务架构是一种方案),那么组织团队沟通结构和系统架构之间又能匹配起来,各个团队才能够独立自治地演化各自的子系统(微服务),这种架构的解耦和调整可以解放组织生产力和提升创新速度。
二、用户体验适配层(Backend For Frontend)
众所周知,随着无线技术的发展和各种智能设备的兴起,互联网应用已经从单一Web浏览器时代演进到以API驱动的无线优先(Mobile First)和面向全渠道体验(omni-channel experience oriented)的时代,如下图所示:
Fig 3,从单一网站到API驱动的全渠道体验
应用架构的新挑战是:
用户接入形式的多样性,无线(手机、Pad),Web,互联网电视,第三方合作应用等等,各种用户设备的屏幕大小,操控体验方式各不相同,例如,手机设备的屏幕较小,能够展示的数据量小,交互方式以触控为主,也可通过条形码扫描器;
有些用户设备的带宽受限,同时应用的UI一般宿主在客户端,有些页面需要组合好几个后台业务服务的数据和功能,如果直接在客户端发起对多个后台服务的调用,势必造成大量网络开销影响性能,这个有点类似数据库查询中的n+1问题。
BFF(Backend For Frontend)是应对上述应用架构挑战的一种模式和最佳实践,2015年底,ThoughtWorks在其网站上刊登了一篇称为BFF@SoundCloud(SoundCloud是一个类似音频YouTube的网站)的文章[附录1],讲述SoundCloud如何利用BFF模式逐步将其单块Rails应用迁移改造为支持无线等多种用户体验的微服务架构。同期,ThoughtWorks的顾问Sam Newman,也就是《Building Microservices》那本书的作者,在SoundCloud等业界实践的基础上,写了一篇博客总结了BFF模式的原理、场景和用法[附录2],建议大家阅读。
BFF本质上是一个后端中间层,但是它的作用主要是适配前端不同用户体验(无线,桌面,智能终端等等),所以称为用户体验适配层,它的适配作用主要是:
裁剪和格式化,对后台的通用数据模型进行适当的裁剪和格式化,以适应不同的用户体验展示的需要;
聚合编排,对后台服务数据进行编排和预聚合,这样可以有效简化客户端逻辑和减少网络调用开销。
下图展示了一个无线BFF:
Sam推荐理想情况下针对每种用户体验类型需要一个BFF(one BFF per user experience),例如Mobile BFF,Desktop BFF,这可以做到职责单一和关注分离(遵循有界上下文原则),但是BFF过多也会造成代码逻辑重复冗余的问题,需要权衡。UI和BFF理想是同一个团队负责,这样可以减少沟通协调成本,加快变更迭代速度,这是遵循康威定律的体现。下图展示了一种BFF和团队职责边界划分方案。
Sam还指出,对于一些跨横切面的关注点(cross cutting concerns),例如路由,安全认证,日志监控分析,限流等等,通常可由独立的网关(Gateway)层负责(如Fig 6所示),由独立基础设施团队运维,置于BFF之前,这样在架构上可以做到职责单一和关注分离,让BFF开发人员专注于聚合裁剪等业务功能,无需考虑跨横切面功能。但是如果对运维成本和调用性能有额外考虑,跨横切面的功能也可以直接做在BFF一层。
Fig 6,独立网关层负责跨横切面功能
三、携程无线微服务案例分享
携程网是一家旅游行业的互联网公司,其内部有约十几个大小不同的事业部(也称战略事业单位,Strategic Business Unit,SBU)组成,例如机票,酒店,度假等等。三四年前,为迎合无线互联网的趋势,和很多其它互联网公司一样,公司成立了一个独立的无线事业部(也是一个SBU),统一为整个公司开发无线应用。下面两个图分别是携程无线H5应用的首页(Fig 7),和最初的无线架构(Fig 8):
Fig 7,携程无线H5首页
Fig 8,最初的携程无线架构
架构底层是企业传统的SOA/ESB/DB服务,架构上层是用户的无线设备,中间是用户体验适配层BFF。携程针对两类不同的用户体验分别做了两个BFF:
Mobile App BFF: 针对iOS,Android等Native和Hybrid应用场景,采用定制的TCP协议和二进制消息以提升网络传输性能,
H5 BFF:针对HTML5浏览器应用场景,采用标准REST/JSON协议通讯。
最初,携程无线BFF是单块的(Monolithic),BFF由无线事业部集中开发,涵盖其它所有SBU(酒店、机票、度假等)的聚合裁剪逻辑。刚开始,携程无线应用相对简单,单块BFF有优势,例如开发、测试和部署集中简单,运维和集群扩容也比较方便。
但是随着时间的推移,特别是近两年,使用无线应用的用户数成倍增长,无线应用的功能也变得越来越复杂,康威定律逐渐发挥作用,无线SBU和其它业务SBU之间的沟通协调成本越来越高,相互之间还常常因沟通不畅而产生摩擦,交付效率越来越低。同时,单块BFF还具有代码逻辑耦合臃肿,集群故障概率高,技术栈绑死,阻碍快速创新等单块架构固有的缺陷。
2014年,携程对组织架构进行了一次调整,将原无线事业部拆分,将无线团队打散并安排到各个SBU业务事业部中。为配合组织架构的调整,公司的技术架构团队也将原来的单块BFF架构升级到如下的(Fig 9)面向体验的微服务架构:
新架构中,原来的单块BFF被拆分到各个SBU独立开发、测试、部署和运维。新架构中引入了一个API网关层(API Gateway),是服务解耦自治的关键,主要负责服务反向路由,同时负责限流容错、安全、日志、监控等跨横切面的公共逻辑。BFF解耦之后,携程微服务架构和组织业务架构进一步对齐,职责更明确,交付效率和创新速度明显加快。
四、Netflix微服务架构分析
Netflix是一家美国的在线影像租赁服务提供商,早在2012-2013年左右,Netflix就已经建立起了比较成熟的微服务架构体系。值得一提的是,Netflix还把它的整个微服务技术栈开源出来贡献给了社区,参考[附录3],其中包括知名的开源服务网关Zuul,服务注册发现框架Eureka,服务端框架Karyon,客户端框架Ribbon,容错组件Hystrix等等,可以说Netflix对微服务架构的发展起了重要的推动作用。下图展示了Netflix的微服务技术栈,来自Netflix参考应用rss reader[附录10],其中带粉红色标注的组件和Zuul都是开源的:
下图是我对Netflix微服务技术栈的一个简化和抽象,可见整个微服务体系的骨架:
Netflix的微服务体系可以简化为两层服务:
边界服务层(Edge Service Layer),本质上就是BFF,适配前端各种用户体验的API层,
中间层服务(Middle Tier Service),Netflix后端的各种微服务的统称。
Netflix的微服务体系由两个重要的基础设施支撑:
服务网关(API Gateway),是Netflix微服务的总入口,负责反向路由,安全,限流容错,日志监控等跨横切面的功能,
服务注册表(Service Registry),负责网关到边界服务,边界服务到中间层服务,以及后台服务之间的软路由和软负载。
关于Netflix微服务和API架构的更多内容,推荐参考SlideShare上的两个ppt:
Transforming the Netflix API[附录12]
Netflix’s Global Edge Architecture[附录13]
下面的一些图片是从上面两个ppt中截取出来的。
Netflix需要针对超过一千种的设备提供服务,这对他们的API层(也就是边界服务层或者BFF层)的设计提出了很大的挑战,为了应对这种挑战,Netflix的UI团队和API团队通力协作,由API团队提供通用的API运行时平台(有点类似PaaS for API的概念),UI团队则在API运行时平台上针对不同设备利用动态脚本开发不同的API端点,这种模式最大化了UI团队的效率和灵活性,如下图所示:
Fig 12,UI和API团队协同开发API
Netflix的API运行时平台内部细节和运作方式如下图所示:
Fig 13,动态脚本平台
Netflix的API运行时平台由API团队负责开发和运维,其中内置支持:
通用的调用后台服务的SDK;
支持并发调用多个后台服务的异步服务层(基于RxJava);
容错组件Hystrix。
UI工程师利用Groovy脚本根据前端设备展示的需要开发API脚本,通过SDK调用后台服务,对后台服务和数据进行聚合裁剪,开发完成的脚本通过端点管理器上传到API运行时平台上,最后激活该脚本则对应的API端点就能生效对外提供服务。Netflix API运行时平台也称为动态脚本平台(Dynamic Scripting Platform),更多细节可参考[附录4]和[附录11]。
Netflix应用采用前后分离架构,页面等静态资源置于CDN上,用户设备从CDN直接加载页面,交互时页面直接从后台边界服务层获取数据,如下图所示:
下图是Netflix API在AWS上的部署架构:
注:最新的Netflix开源项目Falcor[附录5]表明Netflix同时也采用基于Node/JS技术的裁剪适配层,目的是给前端UI团队更大灵活性和自主权。Facebook有一个类似的项目GraphQL[附录16]。
五、SoundCloud微服务架构分析
SoundCloud是一家音频分享网站,有点类似音频界的YouTube,最近SoundCloud在SlideShare上分享了他们的微服务架构和实践。
Fig 16,SoundCloud微服务架构一
上图是从SoundCloud的一个ppt截取的微服务层次结构图,和Netflix/携程类似,两个主要层次是:
边界服务层(Edge Layer),相当于BFF,针对不同场景体验的适配层,例如第三方集成的Public API,嵌入页面场景的Api-embedded,无线场景的Api-mobile和桌面应用场景的Api-v2。
微服务层(Microservices),SoundCloud后台微服务的统称,例如messages、stats、likes服务等等。
下图是从SoundCloud另一个PPT截取的微服务架构图。
Fig 17,SoundCloud微服务架构二
有两点值得关注:
SoundCloud将Geoip、限流、安全认证等跨横切面功能和BFF做在同一层,没有像Netflix/携程一样做在独立的网关层,SoundCloud的这一做法有性能优势,但同时也增加了BFF层的复杂性;
SoundCloud将后台微服务又分为两层,最底层的基础服务层(Foundation Service Layer)和中间的增值服务层(Value-added Service Layer),这种分层方式是SoundCloud根据自己的需要提出的一种逻辑划分。
关于SoundCloud微服务架构的更多内容,请参考SlideShare上的两个PPT:
BFF Pattern in Action: SoundCloud’s Microservices[附录14]
Microservices@SoundCloud[附录15]
六、微服务架构不是免费的午餐
上面分享的三家公司都是体量比较大的互联网公司,他们的业务量和团队规模决定他们很难不采用微服务架构,但是对中小型规模的公司来说,这三家的架构未必是可以直接照搬的,个人认为,解决问题的scope不同,所采用的架构一般也不同,不能盲目照搬。
在业界对微服务架构热情高涨之际,马丁福勒在2015年陆续写了几篇文章,让人们更客观理性地看待微服务,建议大家进一步阅读:
微服务架构先决条件[附录7]
马丁说,你必须长足够高,才可以考虑使用微服务,这里的高就是指基本的研发能力,包括:
快速的环境提供(Rapid Provisioning)能力,理想有基于云的环境提供能力。
基本的监控 (Basic Monitoring)能力。
快速的应用发布(Rapid Application Deployment)能力。
DevOps文化。
在没有建立起这些能力之前,勿轻易跟风采用微服务架构,上面分享的三家公司,都是在具备这些基础研发能力的基础上才开展微服务的。
微服务的附加成本[附录8]
马丁指出,当业务不复杂,团队规模不大的时候,单块架构比微服务架构具有更高的生产率(productivity),原因在于建立微服务架构需要额外的开销来支持和管理微服务,从而降低生产率;但是随着业务复杂性的增加和团队规模的扩大,单块架构比微服务架构的生产率下降更趋明显,当复杂性达到一个点,微服务架构的生产率会优于单块架构,原因在于微服务的松散耦合自治特性减缓了生产率的下降趋势。
注:马丁在上图的右下角提出了一个很有意思的观点,团队的技能是比单块或者微服务架构的选择更重要的因素。说白了,如果团队能力不行,不管用单块还是微服务,还是难于管理复杂性。
反过来,如果团队能力强,不管用单块还是微服务,都能找到好的管理复杂性的手段,所以说团队的技能才是管理复杂性的关键。
单块优先(Monolith First)[附录9]
马丁说,他不建议企业应用一开始就直接上微服务架构,原因一方面是支持微服务架构需要额外的开销来管理分布式复杂性,另一方面是刚开始系统复杂度和领域边界是不清晰的,你不知道该如何正确的切分微服务,所以这种方案常常具有很高的失败风险。
马丁建议企业应用走单块优先的架构思路,先轻装上阵,赢得时间探索系统的复杂性和领域边界,当复杂性增加时,拆出部分微服务,随着团队对服务边界更加清晰和服务管理能力的提升,持续拆分出更多微服务,最终演化出微服务架构。
上面分享的案例中,像携程/SoundCloud都是从单块架构起步,随着业务和团队规模的增长不断调整其架构,最终演化出微服务架构,SoundCloud从单块到微服务演化经历可参考[附录1]。
七、总结
1、微服务架构是一种支持演化的自适应的架构,微服务架构本身也是演化的结果,架构演化的主要驱动因子是:
经济达尔文:从长远看,只有那些能更好满足客户需求的企业才能生存。简单讲就是业务驱动,业务要求架构灵活易于变更和扩展,易于升级替换,发布快速,高可用能应对不可预测的流量模式,能支持多样的用户设备和体验,这样才能加快业务创新的速度,赢得客户和竞争优势;
无线、智能设备和云等技术的进步和发展;
康威定律,即企业的业务、组织和系统边界要尽可能对齐,以业务领域和微服务为边界的产品型跨职能团队能更敏捷地响应市场需求。
2、系统架构的首要目标是管理复杂性,遵循良好的架构原理,如单一职责、有界上下文、关注分离、模块化和分而治之,是管理复杂性的有效手段。在企业应用成长到一定规模以后,微服务架构是管理复杂性的一种行之有效的架构风格。
3、企业要不要用微服务,取决于你的业务复杂度和团队规模,一般Monolith First。业界大型互联网企业的微服务架构可以参考和学习,但是不能照搬,解决问题的scope不同,采用的架构也不同。
4、BFF(Backend For Frontend)是应对当前多种用户体验的一种模式和最佳实践,BFF的主要作用是针对不同的用户体验对后台服务和数据做聚合裁剪适配。用户体验适配层,BFF,API Orchestration Layer,Edge Service Layer,Device Wrapper Layer是相似概念。
5、Client -> API Gateway -> BFF -> Downstream Microservices,是面向体验的微服务的标准参考架构。
6、2016年的参考应用架构如下图所示,引用自ThoughtWorks的文章<技术栈复杂度飙升给管理者们的启示>>[附录6],特点:
企业后台采用微服务架构,微服务可以采用不同的编程语言和不同的存储机制;
企业前台采用BFF模式对不同的用户体验(如桌面浏览器,Native App,平板响应式Web)进行适配;
企业后台采集各种数据,集中存储,再进行大数据建模、分析和预测,计算出来的数据再以微服务方式反哺给前台页面(例如商品推荐)。
附录
很多不错的文章,有兴趣的同学可以参与翻译。
1、BFF@SoundCloud https://www.thoughtworks.com/insights/blog/bff-soundcloud
2、Pattern: Backends For Frontends http://samnewman.io/patterns/architectural/bff/
3、Netflix Open Source Software Center http://netflix.github.io/
4、Netflix Dynamic Scripting Platform http://techblog.netflix.com/2014/03/the-netflix-dynamic-scripting-platform.html
5、Falcor, A Javascript library for efficient data fetching https://github.com/Netflix/falcor
6、技术栈复杂度飙升给管理者们的启示 http://www.neucloud.cn/article/1399_The-inspiration-of-the-technology-stack-complexity-surge-to-managers.html
7、微服务架构先决条件 http://martinfowler.com/bliki/MicroservicePrerequisites.html
8、微服务的附加成本 http://martinfowler.com/bliki/MicroservicePremium.html
9、单块优先(Monolith First) http://martinfowler.com/bliki/MonolithFirst.html
10、Introducing the first NetflixOSS Recipe: RSS Reader http://techblog.netflix.com/2013/03/introducing-first-netflixoss-recipe-rss.html
11、Optimizing the Netflix API http://techblog.netflix.com/2013/01/optimizing-netflix-api.html
12、Transforming the Netflix API http://www.slideshare.net/benjaminschmaus/bschmaus-apiworldtransformingnetflixapi
13、Netflix’s Global Edge Architecture http://www.slideshare.net/MikeyCohen1/edge-architecture-ieee-international-conference-on-cloud-engineering-32240146
14、BFF Pattern in Action: SoundCloud’s Microservices http://www.slideshare.net/grandbora/bff-pattern-in-action-soundclouds-microservices
15、Microservices @ SoundCloud http://www.slideshare.net/grandbora/microservices-soundcloud
16、GraphQL A data query language and runtime http://graphql.org/
互动问答
问题:微服务与SOA的区别是什么?
我觉得两者体现相通的架构原理:单一职责,有界上下文,关注分离,分而治之。区别在于微服务粒度更细,同时融入了近几年一线互联网公司的一些最佳实践,是服务化的新提法。
问题:API网关的反向路由的设计思路能具体说明一下吗?
携程的API网关基于Netflix的Zuul开源组件。对外暴露一个域名api.ctrip.com,根据第一级目录做反向路由。
api.ctrip.com/hotel,api.ctrip.com/flight,api.ctrip.com/vacation。每一级目录,如hotel, flight, vacation对应一个后端BFF集群的域名,也就是说Gateway里头有一张映射表,这张表示是可以动态配置的,可以动态路由,灰度,蓝绿部署都可以通过这张映射表做出来。
问题:分布式数据一致性问题在实践中是怎么解决的?最终一致性方案中的event-driven和event sourcing或其他方案实践中是怎么选型的?有没有推荐的参考框架或方案?
event-driven和event sourcing是Chris Richardson推崇的,他最近还到DaoCloud做演讲。推荐阅读:
微服务系列之「事件驱动型微服务」
http://blog.daocloud.io/chris-richardson-1/
Chris Richardson 微服务系列之「事件溯源(Event Sourcing)型微服务」
http://blog.daocloud.io/chris-richardson-2/
Chris Richardson 微服务系列之「最佳实例:Eventuate」
http://blog.daocloud.io/chris-richardson-3/
Chris Richardson在他的GitHub上还做了一个event-sourcing的样例,值得参考:
https://github.com/cer/event-sourcing-examples
我本人觉得还是复杂了,如果简单场景实时性要求不高,简单队列订阅解决分布式一致性问题。
问题:微服务的分布式事务该怎么做?如果做二次提交怎么处理回滚?
分布式事务尽量避免。《如何用消息系统避免分布式事务?》这篇文章的方法简单可以参考:http://www.cnblogs.com/LBSer/p/4715395.html
问题:微服务感觉就像是服务的模块化,那么如果我一个业务需要多个服务那该在哪里调用这些服务,这么理解对吗?
微服务本质就是模块化,分布式模块化。“如果我一个业务需要多个服务那该在哪里调用这些服务”:BFF,或者中间层的聚合编排服务。
问题:1、微服务架构下,数据库该如何部署呢?有的说一定要每个服务单独部署,有的说要服务共享数据库,您的意见是怎样的? 2、服务在拆分,部署的时候有什么设计原则吗?
1、要看情况,不能绝对的。2、拆分理想按照领域边界,团队边界拆,这个也是不断演化的。
问题:微服务如果变多了有什么办法管理呢?极端点如果有一千个怎么办?
我当时在eBay维护Trading API,也就是eBay最大的一个API,有差不多160多个API。这个维护成本已经很高了,也是跟eBay的体量相对应的。服务拆分到多细粒度,要根据具体情况的,原则虽然是职责单一,但也要看你的团队和资源的限制。目前我所在公司40多号人维护一个网站,我估计最多也就拆出20~30个服务,再多维护不过来。原则是一种指导,但是具体实施要看上下文。
企业真的到了一定体量,按业务要求服务越来越多,Netflix内部微服务应该超过千个,阿里巴巴也超过千个,这个时候要靠基础设施和自动化了,需要微服务基础设施,监控,DevOps和持续交付平台支撑成千上万的微服务,我认为管理复杂性主要靠三个支柱:
1.基础设施平台自动化
2.人才密度
3.遵循好的架构原则和最佳实践
最后给大家分享一些我自己收集的架构资料,关注我,私信发送“JAVA”即可获取以下资料: