本文出自Nginx官网,是对原文的翻译,受益匪浅,希望能在本月底之前将剩余六篇文章一并翻译了,能作为一次内部分享的材料~。
原文地址:https://www.nginx.com/blog/introduction-to-microservices/
1. 概述
近年来,微服务引起了很多关注,大家讨论的很热烈,也有不少争议,有人认为微服务不是新鲜事,只是对SOA架构的细化。显然宣传、怀疑和争论还会持续下去,但是微服务架构在敏捷开发、复杂应用交付上的能力毋庸置疑。
本篇文章是七篇系列文章的第一篇,通过这七篇文章,读者能了解到微服务的设计、构建和部署,能了解到迁移到微服务的方法、微服务和传统单体应用的差异,以及微服务架构其他方面的内容。你将学习到微服务架构的好处和缺点,从而帮助你决定是否在项目中使用微服务,以及如何实施。
2. 构建单体应用
假设我们要构建一个全新的叫车应用去跟Uber竞争,经过初步的需求调研之后,你可能会手动或者使用集成开发工具建一个新应用。新应用类似下面的模块化六边体:
核心是模块化的业务逻辑,外围是应用与外部世界交互的各种适配器:包括数据库访问模块、消息模块、Web模块等。
尽管架构是模块化的,但整个应用还是作为单一整体进行打包和部署,具体形式依赖于开发语言和使用的框架。例如许多java应用会打成war包部署在tomcat之类的web容器上、有些客户端应用会打成包含容器的可执行jar包、而类似Rails和Node.js应用被打包成目录层次结构。
很多应用都是这样开发和部署的,有许多集成开发工具和其他工具可以使用。这些应用很容易测试和部署,在负载均衡后面部署多个应用就能实现横向扩展。在早期阶段,这样的单体应用完全能满足需求。
3. 单体应用的地狱
不幸的是,上面简单构建应用的方法有局限性。成功的应用都会随着时间不断扩展从而变的巨大。在每次小的功能扩展中,开发团队都会增加一些代码进去,几年之后,原本简单的应用会变成复杂的巨型应用。笔者曾遇见的极端例子是:开发人员需要写工具分析几千个jar包之间的依赖关系,整个应用有几百万行代码。
一旦你的应用变大,开发的组织就会变得相当痛苦。任何敏捷开发和快速部署的努力都会遭遇特别大的挑战,显而易见的问题是应用太大、太复杂了,没有哪一个开发人员能完全理解,修正缺陷和增加新功能就会变得特别困难;更麻烦的是,这会形成螺旋向下的趋势:因为代码难理解,修改更容易出错,最终项目会变成一堆巨大的、无法理解的烂泥巴。
单体应用的规模太大还会降低开发效率。应用越大启动速度越慢,笔者曾见过需要12分钟才能启动起来的应用,还听说过需要40分钟启动的应用,这是对开发人员时间的极大浪费。
巨大、复杂单体应用带来的另外一个问题时不能实现持续性部署。今天SaaS应用支持每天向生产环境推送多个更新,对于单体应用而言,如果更新任何一个模块都要重新部署整个应用,这种持续性的更新根本不可能实现。另外,由于不能清晰了解变化可能的影响,团队需要会花费很多精力进行手工测试,从而没有精力考虑持续性部署。
当单体应用的不同模块有相互冲突的资源需求时,会对扩展带来困难,比如有个模块需要对计算进行优化,而有个模块需要对内存进行优化,因为它们作为整体部署,你将不得不进行妥协,从而无法发挥硬件的最大性能。
单体应用的另外一个问题是可靠性。由于所有的模块都在一个系统进程中运行,任何模块的bug都可能会导致整个应用的崩溃。
最后一个问题是单体应用会导致应用新框架、新技术变得非常困难。假设你有个两百万行代码的应用,你要使用新框架重写几乎不可能,这样你就被项目一开始采用的技术和框架绑定,无法在项目中应用效率更高的新技术。
总结一下:单体应用变得巨大,没有开发人员能完全了解;它使用的技术落后,无法应用新技术,找不到优秀的开发人员;扩展困难、不可靠。最终,不可能实现敏捷开发和持续性部署。
那,我们应该怎么做?
4.使用微服务应对复杂性
许多公司(像Amazon、eBay、Netflix等)已采用微服务架构解决了复杂单体应用的问题,其核心思想是将单体应用拆分成许多小的、互相通信的服务。
每一个服务实现特定的功能,像订单管理服务、客户管理服务等;每个服务都是一个小型应用,包含业务逻辑和各种适配器的六面体(像之前描述的叫车应用)。一些服务暴露供其他服务或客户端调用的API,另外一些服务可能实现Web UI;在运行时,每个服务可能是一台虚拟机或者一个Docker容器。可能的服务拆分见下图:
现在,应用的每个功能区域由它自己的微服务实现,此外,大的Web应用被拆分成了多个小Web应用的集合,这就可以根据不同的用户、不同的应用场景进行特定的部署。
每个后台服务都会发布REST API,大多数服务都会调用其他服务提供的API。例如,司机管理服务使用通知服务,去通知一个司机有新订单了;UI服务调用其他服务完成页面渲染。
一些REST服务也会暴露给司机和乘客使用的App,但是,一般App不会直接访问后台服务,中间会经过API网关。API网关提供负载均衡、缓存、访问控制、监控等功能。
微服务架构对应于可伸缩立方体(如上图)的Y轴(可伸缩立方体是《The Art of Scalability》提出的3D可伸缩模型),X轴对应于在负载均衡背后的多个应用副本,Z轴对应于数据分区(比如根据请求的某个属性将请求路由到某个特定的服务器)。
应用一般同时使用三种类型的伸缩功能:Y轴伸缩将应用拆分成多个微服务,X轴伸缩在负载均衡后面运行多个微服务实例以提高吞吐量和可用性,Z轴伸缩将服务分区。以下图展示了行程服务管理在Docker中的部署:
运行时,行程管理服务由多个服务实例组成,每个服务实例都是个Docker容器,为了提高可用性,每个Docker容器都会运行在一个单独云主机上。在这些服务实例前部署负载均衡,实现请求到每个服务实例的分发;同时还可实现缓存、访问控制等其他功能。
微服务架构显著影响应用和数据库的关系。每个微服务都有自己的数据库schema,这会和应用完整的数据模型冲突,另外一方面会导致一些数据的冗余存储;但每个微服务拥有自己的数据库schema是你获得微服务好处的关键,因为只有这样才能确保服务间的松耦合。下图展示了叫车软件的数据架构:
每个微服务拥有自己的数据库,特别是每个服务可以使用最适合自己的数据库,这就是所谓的多语言持久化架构。例如,能发现周围潜在乘客的驾驶员管理服务使用的数据库要支持高效的基于地理位置的查询。
表面看起来,微服务架构和SOA相似,两者都包含服务的集合;从某种意义上来说,微服务像是没有网络服务标准和ESB包袱的SOA。基于微服务的应用更喜欢简单的、轻量级的协议,像REST,而不是WS-*;他们也尽量避免使用ESB,取而代之的是在微服务中实现类似ESB的功能。
5.微服务的好处
微服务架构有许多好处,首先是它能应付复杂性。微服务架构将复杂的单体应用拆分成服务的集合,每个服务都有以RPC或者消息驱动Api定义的良好边界。微服务架构强制实现一定程度的模块化,这在单一代码库上几乎不可能实现。因此,每个服务可以更快的开发,也更容易理解和维护。
其次,微服务架构允许专注于该服务的团队独立开发,只要符合API要求,开发团队可以自由选择任何有意义的技术。当然,技术选择不会那么自由,大多数公司都会避免在技术选择上的无政府状态,会将可选技术限定在有限的范围内;但微服务架构带来的实实在在的好处是,大家不必受限于项目一开始选择的,可能已经落后的技术;编写每一个新的服务,就是一次选择最新最合理技术的机会;更重要的是,既然服务没有那么庞大,使用新技术重写服务就耗费不大,因此微服务架构能方便新技术的应用。
第三,微服务架构下,服务支持独立部署,而不需要协调其他相关服务。只要服务自身测试通过就可以部署,这为持续性部署提供了可能。
最后,微服务架构允许服务独立伸缩。你能根据访问量和可用性的要求,为每个服务部署不同数量的实例;你还可以根据服务的不同需求选择不同的硬件,比如可以根据服务的实际需求,选择计算优化云主机或者是内存优化云主机。
6.微服务的缺点
像其他技术一样,微服务架构有自身的缺点。第一个缺点是它的名字,它的名字过于强调服务的规模了,确实有一些开发者提倡构建10-100行代码的细粒度服务。片面强调服务规模不是好事情,因为虽然我们总体倾向于小的服务,可更重要的是需要认识到这是我们达到目标的途径而不是目标本身。我们的目标是高效拆分应用后,实现敏捷开发和持续性部署。
第二个缺点是由微服务架构作为分布式系统而导致的复杂度。开发人员需要选择并实现基于消息或者RPC的进程间通信机制,还要写代码去处理由于目标服务不可达或者反应慢导致的部分失败;微服务架构比起单体应用复杂多了。
另外一个挑战来自于分区数据库架构。实际应用中,在一个事务中更新多个业务实体很常见,这对单体应用是小菜一碟,因为所有业务实体都在一个数据库中;而在微服务架构下,你需要更新多个服务的多个数据库。在这种情况下,一般不能使用分布式事务,因为当今高度可扩展的NoSQL数据库和消息代理根本不支持分布式事务,你只能使用最终一致性来保证事务一致性,这对开发人员是个巨大挑战。
测试工作变复杂了。在单体应用中,使用类似Spring
Boot的框架写一个测试类,启动Web应用,测试它的REST接口很简单;而在微服务架构下,你除了需要装载测试服务外,还需要装载所有它依赖的其他服务,这不是一件容易的事情。
还有个挑战是一个变化涉及到多个服务都需要改动时。假设需要改动A、B、C三个服务,而A依赖于B,B依赖于C。在单体应用中,不用考虑依赖关系,三个模块修改后,打包发布就好了;而在微服务架构下,需要小心计划新版本更迭的顺序:C先更新,B次之,A最后。所幸在设计良好的微服务架构下,一个变更涉及多个服务比较少见。
部署微服务应用更复杂。部署单体应用时,只要将应用部署到负载均衡后面的服务器上,绑定IP地址和端口号就可以了;而微服务架构的应用由大量的服务构成(像Hailo有160个不同的服务,而Netflix有超过600个服务),每个服务都有多个运行时的实例。这就意味着需要配置、部署、伸缩、监控的内容复杂多了,除此之外,还需要实现服务发现机制。到这种规模,单靠人工已经不能应付,需要较高水平的自动化部署。
一种自动部署的方式是应用现成的PaaS平台,像Cloud
Foundry。PaaS为开发者提供部署和管理微服务的简单方式,帮助开发人员消除配置IT资源的麻烦,同时,PaaS提供者的专业知识能保证他们提供的服务总是基于最佳实践。另外一种自动部署的方式就是编写属于自己的PaaS平台,一种常用的做法是结合像Kubernetes的集群方案和Docker来实现。
7. 总结
构建复杂应用从本质上说是困难的,单体应用只对简单、轻量级的应用有意义,如果你用它构建复杂应用肯定会痛苦无比。尽管微服务架构有很多缺点和实现上的挑战,可它对复杂的、不断变化的应用仍是更好的选择。
在后续的文章中,我们将深入微服务架构的各个方面,讨论服务发现、服务部署选项、将应用重构成微服务的策略等主题。
敬请关注~