读Martin Fowler关于微服务架构的文章
最近读到Martin大神关于微服务的一篇经典文章。原文链接
原文是英文的,啃起来比较费劲,于是将文中主要观点总结如下,方便未来反复阅读思考。
微服务是一种软件工程的风格。它由传统的单体程序演化而来,将一个大型的应用拆解为一系列独立的小服务,服务间通过轻量级的协议进行通信(常见的是HTTP),每一个小服务都能独立完成一块功能,有自己独立的数据存储,有明确的边界,可以独立部署。
传统的单体程序的主要问题在于不够灵活。当系统中一个小的功能发生调整时,整个项目都要重新编译、发布、部署。而微服务可以独立地进行发布、部署,可以充分的利用系统资源,尤其适合云上部署。结合已有的实践,作者总结了微服务化架构的9大特点。
1,服务化组件
我们一直希望像物理世界的制造业一样,将软件模块标准化。这里提到的组件指代的便是可以被独立替换和升级的软件单元。在曾经的单体程序中,软件的模块化是通过库(library)的封装来完成的,但这有两个缺点:1),如果一个库函数发生了变化,意味着整个工程需要重新编译部署;2),库函数的很多内部方法被调用,导致组件间紧耦合。微服务则以服务则为标准化组件,独立部署的特性使得不同组件间的边界更加清晰,调整一个组件不需要影响到其他组件。
使用服务来代替库作为组件也有一个坏处,那就是RPC调用的开销通常大于进程内的函数调用。这也要求我们在划定服务边界是尽量做到高内聚。
2,围绕商业能力组织服务
传统的软件架构分层通常是根据技术分层的,比如UI、后端、DB等等。于是每一个技术团队对应了一层技术栈,每一个需求都需要跨部门的协调。实际上软件架构往往也反应了组织的架构。
微服务的划分则是围绕商业能力展开的。每一个服务都针对了一个独立的功能,并且围绕这个功能涉及的技术组织团队。上述提到的UI、后端、DB的相关技术人员会结合成一个团队来增强服务内的沟通效率。
3,产品而非项目
传统的软件项目都有一个明确的产品周期,目标是在确定的时间交付一个商用的软件。比如IBM的IPD流程。这种体系下开发人员的目标围绕着交付一系列功能展开,当版本交付之后,软件也就被视为完成态,随后转移到专门的维护部管理。原来的开发团队可能会解散或者投入新的项目。
但微服务的实践视图打破这种传统。每一个微服务组件都应被视为一个产品,开发人员要为产品的整个生命周期负责。Amazon提出的“you build,you run it”就是这样的观念。这使得开发人员也要负责产品上线的部署和维护,并且接触到真实的客户。
虽然理论上,传统的单体程序也能做到这一点。但是在功能解耦的微服务模式下,开发者更容易对一个产品有一个整体上的了解并且和用户建立联系。
4,smart endpoint,and dumb pipes
不知道如何翻译,总之就是强调高内聚,低耦合。组件逻辑完整,通信协议傻瓜。
在进程间通信这个问题上,很多软件都设计了精巧的通信机制来保证消息的路由、转换和应用各种规则。微服务设计则倾向于将系统划分为高内聚的各个组件,然后通过简单而标准的协议来通信。每一个服务都像filter一样,接受请求,输出响应,仅此而已。每个服务都专注于自己的一块业务逻辑,而标准通信协议使得服务的调用、扩展和运维非常简单。
常见的两种通信协议是RESTFUL API(HTTP)和消息队列(RabbitMQ)。将私有通信协议替换为标准的通信协议也是从单体复杂系统拆分为微服务架构的一大难点。
5,去中心化的技术管理
传统的中心化的产品管理风格倾向于为整个项目规划一个统一的技术栈,但这么做是有局限性的。因为每一种编程语言,每一种技术都有其擅长的领域,我们应当针对问题的特点选择使用的技术。
微服务化实践则鼓励不同的服务使用不同的技术标准。你可以用C++重写对性能要求较高的功能,也可以选择非关系型数据库以应对读多写少的应用场景。定义适合自己的技术标准,分享最佳实践和通用的工具或库,在这样的模式下更有利于创新和提高效率。
当然,这并不是说我们不需要服务的规约,我们只是换了一种方式来管理这些规约(Patterns like Tolerant Reader and Consumer-Driven Contracts are often applied to microservices)。每个服务的开发者都要为自己的服务的全生命周期负责,为了每天晚上能睡个好觉,开发们也一定会在写代码时格外重视质量。
6,去中心化的数据管理
去中心化的数据管理体现在多个方面。在最抽象的层次,意味着每个系统的领域模型是不同的。在数据持久化层,意味在不同的系统使用不同的数据库系统(可能是相同数据库的不同实例,也可能是不同类型的数据库)。这样的改变也对传统软件架构中的数据一致性问题带来了新的挑战。
在关系型数据库时代,我们通常使用事务来处理数据一致性问题。但在分布式场景下,事务的保证是极为困难的。因而在很多场景下,我们可能要转而保证数据的最终一致性。这种抉择往往取决于商业利益,也即为了可用性牺牲部分一致性是否是值得的。
7,自动化的基础设施
尽管这篇文章的重点不是讲持续交付,但在这里还是要强调自动化CI/CD的重要性。微服务化使得产品的迭代速度进一步提升,特别是在云计算大行其道的今天,生产环境的部署与维护变的越来越高效。为了保证软件稳定的快速迭代,我们需要像之前生产单体系统一样,投资一条自动化的流水线,自动完成软件编译、单元测试、集成测试、UAT测试以及性能测试并最终自动化地部署到生产环境。作者想在这里强调的事,传统的为一个复杂的大型程序建设的CI/CD流水线对于微服务的程序依然有效。我们无需重新制造轮子。
8,容错机制
微服务化的一个缺点是在程序设计过程中,需要考虑组件间调用失败的情况。这给客户端的实现带来了额外的复杂度,开发人员需要时刻考虑系统出现故障时如何尽量减少对用户体验的影响。著名的Netflix公司的Simian Army便会随机地在数据中心内制造故障来检验系统的健壮性以及运维监控是否全面。
对于微服务架构的系统来说,监控体系的完善至关重要。运维人员必须对整个系统的关键指标可视化,甚至更高明的,当系统关键指标出现异常时,监控系统应当能够自动将故障的服务降级并尝试自动恢复。Monitor系统又是一个很宏大的话题,未来写到SRE相关内容时我们再展开讨论。
9,不断演化的设计
微服务架构的提出是为了适应如今软件快速迭代快速上线的市场需求。当我们想要拆分一个系统的时候,我们需要时刻想着系统中的每一个组件都是可被独立替换和更新的。更进一步:软件应当是可以报废的,而不一定要长久发展。另一方面,当我们在一段时间内发现两个服务总是同时变更时,那么就该考虑将两个服务进行合并了。
微服务架构的系统要适应快速迭代,一个很大的担忧时,如何保证对一个服务的一次变更不会影响线上其他已有的服务。传统的软件设计是依靠版本号来确定依赖关系的,但在微服务的世界中,我们更希望客户端的设计可以尽量兼容不同版本的服务端程序,而版本号只是作为最后的信息通告手段。
微服务会是软件架构发展的未来么?
作者在文章的最后并没有给出一个断言。甚至有相当多的人认为在打造系统最小功能集的时候,将所有功能集中到一个单体程序中能够降低复杂度,提升开发效率。这些争论都是存在的,作者也没有在文中给出明确的判断。但对于微服务架构,作者是持有谨慎乐观的态度的,特别是结合已有的一些成功的实践,作者相信微服务化绝对是一个值得企业应用认真考虑的设计风格。