时下流行devops关键词:分布式架构、一体化架构和微服务架构

技术趋势关键词:分布式架构+微服务架构(针对移动互联网)+一体式架构(前两者结合+UI等敏捷开发)

【译者的话】otto.de是德国的一家网上购物网站,本篇前半部分主要介绍了几个系统架构以及它们的优缺点,后半部分主要讲解otto.de的微服务架构。

在我们开始开发otto.de网上商店时,我们选择了分布式垂直架构。之前的工作经验告诉我们,一体化架构(monolithic architecture)不能够满足不断增长的需求。爆发式增长的数据,持续提高的负载和对系统的扩展,所有的这些强迫我们去重新思考网站的架构。

这篇文章将会描述我们的解决办法,还有我们这么做的原因。

一体化(Monoliths)

在项目刚开始的时候,团队通常会考虑使用什么编程语言和合适的架构。当谈到服务端应用时,Java和Spring框架,Ruby on rails或者类似的框架通常会成为团队的选择。

选择了语言和框架后,经过一段时间的开发,一个简单的应用诞生了。与此同时,一体式架构(macro-architecture)毫无争议的成为了团队的选择。但是,这种架构的缺点也渐渐地浮出了水面:

  • 它导致了重量级微架构(a heavyweight Micro Architecture)
  • 负载均衡限制了应用的可扩展性
  • 系统的可维护性受到影响,尤其是那些大型应用
  • 零停机部署(Zero downtime deployment)变得非常的困难,尤其是那些有状态的应用(stateful application)
  • 多个团队开发效率低,并且需要额外的协调

当然,这并不是说一个新的应用一开始就变得巨大而混乱。在最开始的时候,新应用结构清晰,简单易懂,可扩展性也高,它能够很轻松的解决需求问题。在接下来的一段时间中,越来越多的代码被编写。为了应对日益增长的复杂度,系统被分层,抽象,模块,服务和框架被引入到系统中,最终变成了我们看到的样子。

即便是中型的应用(比如说一个50000的Java应用),一体化架构也会渐渐地变得令人讨厌,更不用说那些对扩展性要求较高的应用了。

最终,曾经轻量简洁的应用将会变成下一代开发者的噩梦。

分而治之

问题的关键在于,如何避免这种类型的开发,并且将轻量应用好的那部分保留下来。换句话说,我们如何能够获得一个可持续发展的架构,这个架构在多年之后依旧能够让开发者保持高效开发。

在软件的开发过程中,有许多关于结构化代码的概念:函数,方法,类,库,框架等等。这些概念并不是程序运行所必须的,发明他们的原因是为了帮助开发者更好的理解他们的应用。

目前软件开发者已经理解了这些概念,一个问题接踵而来:为什么这些概念仅仅被应用于一个软件?是什么阻碍我们将应用拆分成多个低耦合的部分?

有三件事情我们需要牢记在心:

  • 康威定律:软件开发最开始时仅有一个团队,根据康威定律,因此会产生一个应用。(译者注:可以参考 图片 理解)
  • 初始消耗:部署应用,并让它运行起来似乎是一个非常简单的任务。实际上,你需要建立并管理VCS代码库,编译文件,构建管道,用于部署的程序,硬件,虚拟机,日志文件,监控软件等等。所有的这些都需要花费一定的时间去处理。
  • 操作的复杂性:大型分布式系统比一个小型的负载均衡集群更难操作。

如果我们放手不管,由多个小型模块组成的系统并不会出现,一个巨大而混乱的系统将会取而代之。这时候,致命的问题已然出现,然而悔之晚矣。

正常情况下,一个系统是否需要被扩展,是否需要处理巨大的代码库在初期是非常清楚的。然后当你遇到以上提到的障碍,你要么没尝试解决他们,要么只能沉沦在无尽的苦果中。

在OTTO,在最开始的时候就花费了大量时间去建立4个跨职能的团队,根据前面提到的康威定律,一个项目属于4个团队,最终就能产生一个由4模块组成的应用,这样就避免了一体化应用的诞生。

因为我们之前操作过大型的一体化应用,操作的复杂性对于我们似乎是一个可以被解决的问题---操作200个一体化应用和操作200个更小型的系统没有太大的区别。

初始消耗可以通过标准化和自动化来克服。因为我们没有提到云服务,你还需要做相关的基础操作来启动自动化服务。虽然有些麻烦,但做过一次后,一切将自动化,你会获得巨大的便利。

可扩展性

如何将一体化应用转变为由多个小模块组成的应用?首先,让我们仔细想想一个应用能够从哪些维度进行扩展。

纵向分解(Vertical Decomposition)

纵向分解是一个非常自然而通用的方法,以至于常常被开发者所忽略。相比于把所有的功能集中到单一的应用中,我们将应用分解成了多个小模块,它们相互独立,互不影响。

我们可以根据业务域来分解系统。举个例子来说,在otto.de我们就讲网上商城分解成了11个不同的垂直模块:后勤办公室,产品,订单等等。

每一个垂直模块属于一个单一团队,它们有独立的前端,后端和数据存储。在模块之间共享代码是严令禁止的。当然,在特殊的情况下,如果我们需要分享代码,我们会建立一个开源的项目来解决该问题。

因此,每个垂直模块是一个独立自主的系统,就像Stefan Tikov在Substainable Architecture中提到的那样。

分布式计算

一个垂直模块依旧可能成为一个相对大型的一体化应用,因此我们需要继续对垂直模块进行拆分。一种方法是将一个垂直模块分解成更多的垂直模块,另外一种方法是通过分布式计算将系统分解成多个模块,不同的是这些模块运行在他们自己的进程中,并且通过REST来传递信息。

在这种情况下,应用不仅仅被垂直分解,同时还会被水平分解。这种架构中,请求到达应用后,对请求的处理会被分布于多个模块中,然后每一模块产生的结果汇总成一个响应,发送回请求者。

这些模块并不会共享一个数据库架构,因为这样做会导致模块间的紧密耦合:数据结构的改变会使得一个模块不能够被独立的部署。

分片

当系统需要处理大量的数据,或者当一个分布式的应用被操作时,分片是很恰当的选择。比如说,分片非常适合向全球范围提供服务的应用。

因为我们暂时没有利用到分片这个概念,在这篇文章就不再详细描述了。

负载均衡

每当服务器承受不了巨大的负载压力时,负载均衡就会容重登场。通过对一个应用拷贝多次,同时利用负载均衡器将负载分解来缓解压力。

在负载均衡中,不同实例的应用通常会分时使用同一个数据库,数据库因此成为了系统的瓶颈,但是我们可以通过制定良好的扩展策略来避免这个问题。相比于关系数据库,NoSQL能够很好的处理扩展性的问题,这也就是NoSQL能够在软件世界中占据一亩三分地的原因之一。

最大的可扩展性

所有以上提到的办法能够组合在一起,能够达到任何级别的可扩展性。

当你没有相应的需求时,组合的结果会变得有点太复杂。幸运地是,开发者并不需要在一开始的时候就制定庞大的计划,相反,他们可以循序渐进,一步步朝着目标架构前进。

举个例子来说,在otto.de,架构一开始是4个垂直模块加上负载均衡,在过去的三年里,产生了更多的垂直模块。在此期间,某些模块变得非常的巨大笨重。因此,我们引入了微服务架构,同时通过扩展垂直模块来建立分布式计算。

微服务(Microservices)

微服务最近变得非常的流行。微服务是一种架构风格,它能够根据业务域将系统分解成多个细粒度,独立的模块。

在这种情况下,微服务可以是一个小的垂直模块,或者是分布式计算机构中的一个服务。与传统方法的不同之处在于应用的大小:一个微服务仅仅实现了一个业务域中的几个功能,它结构清晰,一个开发者能够很轻松的掌握它。

一个微服务非常的小,因此多个微服务能够运行在单一的服务器上。我们对“Fat JARs”有丰富的经验,通常能通过执行java –jar <file>来执行它们。如果需要的话,也能开启一个Jetty或者类似的服务器。

为了简化不同微服务的部署和操作问题,每一个服务器运行在独立的Docker容器中。

REST和微服务架构是一个很好的组合,它适合于构建大型的系统。一个微服务可以负责提供REST资源,超媒体(hypermedia)可以用来解决服务发现的问题,在涉及到接口的版本控制,服务部署独立性的情况下,媒体类型(media type)有很大的帮助。

总而言之,微服务架构有许多的好处,比如说:

  • 在微服务架构下进行开发是非常有趣的:每几周或者几个月,你就可能开始一个新的开发项目。
  • 由于微服务非常小,微服务架构不需要重量级框架和过多的模板代码。
  • 他们能够被独立的部署。因此持续交付或者持续部署变得非常的简单。
  • 微服务架构能够支持多个独立的团体同时开发。
  • 开发者能够为每一个服务选择最恰当的开发语言。不用担心对项目产生影响,开发者可以尝试新的语言或者框架。但是需要注意,这并不意味这你能够随意行使这项权利。
  • 因为微服务足够小,只需消耗较少的资源就能将他们替换成新的项目。
  • 这种架构的可扩展性相比于一体化架构显得非常的好,每一个服务都能被独立的扩展。

微服务架构遵守敏捷开发的原则。一个不能完全满足用户的新特性不仅可以被迅速的创建,而且还能够被快速的销毁。

宏架构和微架构(Macro- and Micro-Architecture)

在微服务架构中,哪一部分将难以改变?内部模块的扩展已经不再是关键问题,最难以改变的事情是有关微服务架构的决定,比如说如何将微服务整合到系统的方法,或者模块间传输信息协议的选择。

因此,otto.de严格区分了微架构(micro-architecture)和宏架构(macro-architecture)。微架构都是关于垂直架构或者微服务架构的内部结构,并且全部交由各自的团队全权处理。

但是,明确宏架构的大体方向是有价值的:

  1. 垂直分解:系统被分割成多个垂直模块,每一个模块完全属于一个特定的团队。模块与模块之间的信息传递禁止在用户请求的过程中进行,而必须在后台执行。
  2. RESTful架构:不同服务之间的信息传递和整合只通过REST来执行。
  3. 零分享架构:服务间不会通过共享可变状态(mutable state)来进行信息交换或者分享信息。没有HTTP sessions,没有中央数据存储中心,没有共享代码。但是多个服务的实例之间有可能共享一个数据库。
  4. 数据管理:对于每一个数据节点,只有一个系统负责管理。其他的系统只能通过REST API读取数据,然后将需要的数据拷贝回自己的数据库。

我们的架构已经熬过了一轮软件开发周期,与此同时,我们开始标准化微服务使用的方式。

集成

目前为止,我已经详细说明了许多有关系统分解的内容。但是,用户体验是我们的系统的最终目标,我们希望我们的应用保持一致性,感觉就像是一个整体。

因此,问题来了:我们如何能够集成一个分布式的系统,同时让用户意识不到我们架构的分布特性。

超链接

对于前端集成,最简单的办法就是使用超链接。

每一个服务负责不同的页面,页面的导航通过链接来实现。

AJAX

使用AJAX的目的也很明显,它能够通过Javascript重新加载页面的不同内容,并且将他们整合在特定的页面。

主要注意的是,服务之间涉及到的依赖非常的小,服务互相之间需要对使用的URL和媒体类型(media type)保持一致性。

资源服务器(Asset Server)

当然,图片在不同页面的显示也需要保持一致性。除此之外,分布式的服务需要对他们各自的Javascript库和版本保持一致。

为了保持一致性,在我们的系统中,静态资源,比如说CSS,JS和图片都是通过一个中央资源服务器来进行传输。

在垂直架构,共享资源的部署和版本控制是一个完全不同的话题,这需要一篇独立的文章来详细解释。在这里我们不做过多解释,我们只需要记住,共享资源的同时保持服务独立是具有非常大的挑战性。

Edge-Side Includes

有一种不太知名的方法,它能整合不同服务的资源到同一个站点。这种方法我们称之为Server-Side Includes或者是Edge-Side Includes。大多数的Web服务器或者反向代理都只支持这个功能。

这项技术非常的简单:一个服务插入插入一条带有URL的包含语句(include statement)。然后这个URL会被web服务器或者反向代理解析,代理根据URL获取到一个响应,然后用这个响应代替页面中的包含语句。

在我们的商城中,每一个页面都包含了来自搜索&导航服务(SAN)的导航信息:

<html>

...

<esi:include src=/san/...“ />

...

</html> 

反向代理(我们使用了Varnish)解析了页面,然后将URL分解出来,SAN根据URL提供相应的HTML片段。

然后Varnish代理用这个HTML片段取代包含语句,并将重新生成的页面发送回用户。

在这种方式下,用户根本意识不到页面是由多个来自不同服务的片段组成的。

数据拷贝(Data Replication)

以上提到的技术仅仅解决了前端集成问题,现在我们来谈谈服务端集成。不同的服务之间需要共同的数据,但是他们又不能共享同一个数据库,因此我们想了一些办法来处理这个问题。

数据拷贝就是一个办法。比如说,其他的服务需要关于产品的数据,它们就会定期的向负责产品数据的垂直模块(Product)请求数据,这样产品数据的更新能够很迅速的被其他服务检测到。

我们没有使用任何的消息队列来向客户端推送(push)数据。相反的,每当服务需要更新数据时,它们会轮询(poll)Atom Feed。

值得一提的是,某些不好的事情必须牺牲服务的可用性来避免,我们在开发过程中不得不面对这个矛盾。

没有远程服务调用(NO Remote Service Calls)

理论上来说,在某些情况下,我们可以避免数据的拷贝,这样服务就能够同步使用其他的服务了。一个购物篮并不需要保存额外的产品信息,相反它可以直接向产品模块请求数据。

我们并没有这么做,为什么呢?

  • 当一个系统的主要功能依赖于其它系统时,系统的可测试性受到影响。
  • 一个缓慢的服务会影响到其它系统的请求,当请求越多,雪球越滚越大,最终影响到了整个系统的可用性。
  • 系统的可扩展性受到了限制。
  • 独立的服务部署变得非常的困难。

我们长期和垂直架构打交道,我们非常明确的知道,在早期的时候,严格的界限会使得微服务的开发,测试,迁移变得更加独立,方便。

经验教训

按照以上罗列的办法,经过三年的工作后,我们变得经验丰富。

回顾往事,如果我早点做这些事情,我们的一体化系统会变得更加的精细。现在,otto.de的未来属于微服务。

原文链接: On Monoliths and Microservices 

有关微服务架构的争论:更简单还是更复杂?

随着DevOps、持续交付等理念的深入人心,微服务(Microservices)架构开始走进我们的视野。微服务是用一组小服务的方式来构建一个应用,服务独立运行在不同的进程中,服务之间通过轻量的通讯机制(如RESTful接口)来交互,并且服务可以通过自动化部署方式独立部署。正因为微服务架构中的服务之间是相互独立的,所以不同的服务可以使用不同的语言来开发,或者根据业务的需求使用不同类型的数据库。3月底,来自ThoughtWorks的James Lewis和Martin Fowler分享了他们对微服务架构的理解以及看法。文章中作者详细介绍了微服务的特点以及相对于传统架构的微服务架构的优势。

在James看来,传统的整体风格的架构在构建部署和扩展伸缩方面有很大的局限性,传统的整体(monolithic)风格的架构一般分为三部分:客户端用户界面(HTML页面和JavaScript程序)、数据库、服务端应用程序。服务端程序负责处理HTTP请求、执行业务逻辑、加载和更新数据库数据、查询和填充HTML页面,这个服务端应用就像是一块铁板,笨重且不可拆分,系统中任何程序的改变都需要整个应用重新构建和部署新版本。另外传统的整体风格的架构在进行水平扩展时也只能整个系统扩展,而不能针对某一个功能模块进行扩展。而微服务架构可以完美的解决统一风格架构所遇到的种种问题。微服务架构将系统以组件化的方式分解为多个服务,服务之间相对独立且松耦合,单一功能的改变只需要重新构建部署相应的服务即可。

传统的开发模式在分工时往往以技术为单位,比如UI团队、服务端团队和数据库团队,这样的分工可能会导致任何功能上的改变都需要跨团队沟通和协调。而微服务则倡导围绕服务来分工,不同的服务可以采用不同的技术来实现,一个团队中应该包含开发所需的所有技能,比如用户体验、数据库、项目管理。

传统的软件开发常常以项目为周期,一旦项目开发完成即交付给运维部门,而在微服务架构中,一个团队应该去把控产品的整个生命周期,而不只是开发,这其实也是DevOps的核心理念,所以微服务中的每一个服务都是一个产品,而不是一个项目。

微服务架构抛弃了ESB复杂的业务规则编排、消息路由等功能,微服务架构中服务是高内聚的,每个服务都会处理相应的业务,所有的业务逻辑应该尽量在服务内部处理,且服务间的通信尽可能的轻量化,比如使用Restful的方式。

传统的软件开发中经常会使用同一个技术平台来解决所有的问题,而经验表明使用合适的工具做合适的事情会让开发变得事半功倍。微服务架构天生就具有这样的特性,我们可以使用Node.js来开发一个简单的报表页面,使用C++来编写一个实时聊天组件。

微服务架构的引入为多样化持久保存数据提供可能,持久层可以使用传统关系数据库和NoSQL。不同于传统的应用,微服务架构中,我们可以为每个服务选择一个新的适合业务逻辑的数据库系统,比如MongoDB、PostgreSQL。这样做的好处是显而易见的,首先我们可以根据业务类型(读多还是写多等)来决定使用哪种类型的数据库,其次这样可以减小单个数据库的负载。

就James的文章来看,微服务架构主要有以下几个优点:

  1. 每个服务只需要做好一件事,更加专注和简单
  2. 用合适的工具来做合适的事情
  3. 服务之间是松耦合的
  4. 服务的团队之间是相互独立的

James的文章在社区引起了广泛的讨论,近日,Contino公司的CTO Benjamin Wootton在highscalability上撰文表示微服务并没有想象中的那么好,并建议开发者在选用此架构时一定要慎重。Benjamin认为微服务架构时可能会面临下面一些挑战:

  1. 运营开销。更多的服务也就意味着更多的运营,产品团队需要保证所有的相关服务都有完善的监控等基础设施,传统的架构开发者只需要保证一个应用正常运行,而现在却需要保证几十甚至上百道工序高效运转,这是一个艰巨的任务。
  2. DevOps要求。使用微服务架构后,开发团队需要保证一个Tomcat集群可用,保证一个数据库可用,这就意味着团队需要高品质的DevOps和自动化技术。而现在,这样的全栈式人才很少。
  3. 隐式接口。服务和服务之间通过接口来“联系”,当某一个服务更改接口格式时,可能涉及到此接口的所有服务都需要做调整。
  4. 重复劳动。在很多的服务中可能都会使用到同一个功能,而这一功能点没有足够大到提供一个服务的程度,这个时候可能不同的服务团队都会单独开发这一功能,重复的业务逻辑,这违背了良好的软件工程中的很多原则。
  5. 分布式系统的复杂性。微服务通过REST API或消息来将不同的服务联系起来,这在之前可能只是一个简单的远程过程调用。分布式系统也就意味着开发者需要考虑网络延迟、容错、消息序列化、不可靠的网络、异步、版本控制、负载等,而面对如此多的微服务都需要分布式时,整个产品需要有一整套完整的机制来保证各个服务可以正常运转。
  6. 事务、异步、测试面临挑战。跨进程之间的事务、大量的异步处理、多个微服务之间的整体测试都需要有一整套的解决方案,而现在看起来,这些技术并没有成熟。

每一种架构都有其优缺点,当然微服务也包括在内。我们需要根据项目业务和团队情况来选择合适的架构,微服务才刚刚开始发展,这也是顺应PaaS、持续交付、DevOps等新技术理念下的产物,期待微服务架构能得到更广泛的应用。关于微服务,如果您有新的见解,欢迎与我们分享。


感谢杨赛对本文的审校。


  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值