得益于 2013 年 Docker 的诞生,微服务概念及架构的推广和落地变得更加的可靠和方便。在 2016 年及之前,微服务架构的讨论更多的是活跃于互联网企业及社区。现如今,随着 Docker 和微服务架构组件与 Docker 等相关技术的逐步成熟,微服务架构已然步入传统企业及传统行业。
但是,程序员作为一个理性消费的群体,需要冷静地思考,避免挖个大坑把自己给埋了。所以,我们需要冷静地搞清楚:微服务(架构)是什么?它有什么优势劣势?我们为什么需要采用微服务架构?如何让老板接受这一新技术?如何落地?如何升级维护?等等……
我们在过去 7 年智慧城市的建设过程中,研发和交付了很多的大型项目,踩过很多的坑,趟过很多的雷,深受传统建设方法之苦,也深深被微服务架构带来的好处所感动,我们也将在微服务架构这条路的继续前行。在这里,将我们研发过程中的一些思考和心得分享给大家,供大家参考。
也许,在不久的将来,软件开发只需要组装,不再需要从头开发。
什么是微服务架构?形像一点来说,微服务架构就像搭积木,每个微服务都是一个零件,并使用这些零件组装出不同的形状。通俗来说,微服务架构就是把一个大系统按业务功能分解成多个职责单一的小系统,并利用简单的方法使多个小系统相互协作,组合成一个大系统。
如果学科派一点,微服务架构就是把因相同原因而变化的功能聚合到一起,而把因不同原因而变化的功能分离开,并利用轻量化机制(通常为 HTTP RESTful API)实现通信。
追本溯源,Martin Folwer 对微服务架构的定义是:
微服务架构是一种架构模式,它提倡将单一应用程序划分成一组小的服务,服务之间互相协调、互相配合,为用户提供最终价值。每个服务运行在其独立的进程中,服务与服务间采用轻量级的通信机制互相协作(通常是基于 HTTP 协议的 RESTful API)。每个服务都围绕着具体业务进行构建,并且能够被独立的部署到生产环境、类生产环境等。另外,对具体的服务而言,应根据业务上下文,选择合适的语言、工具对其进行构建 。(摘自王磊先生的《微服务架构与实践》)
对于我个人,我更喜欢一种延续性的解释,微服务架构 ≈ 模块化开发 + 分布式计算。不管微服务架构的定义怎么样,都是在描述一个核心思想:把大系统拆分成小型系统,把大事化小,以降低系统的复杂性,从而大幅降低系统建设、升级、运维的风险和成本。
顺带提一下,亚马逊创始人 Jeff Bezos 在 2002 年就说过:所有团队的模块都要以 Service Interface 的方式将数据和功能开放出来。不这样做的人会被炒鱿鱼。这才是思路超前的大牛。
需要注意的是“微服务”与“微服务架构”是有本质区别的。“微服务”强调的是服务的大小,它关注的是某一个点。而“微服务架构”则是一种架构思想,需要从整体上对软件系统进行通盘的考虑。
Chris Richardson 说:“微服务”是一个很糟糕的名字,它导致开发人员创建了许多粒度很小的服务,每个服务拥有一个单独的 REST 端点。不仅如此,这个名字还暗示了微服务在开发者心目中的重要位置。例如,人们会说“我们可以用微服务来解决这个问题”;我也看到了越来越多的“某某微服务框架”,而实际上,这些框架跟微服务架构不一定有太多联系,它们只是简单的 Web 框架。使用“微服务架构”这个名字会更恰当些。它是一种架构风格,它把一系列协作的服务组织成一个系统来支撑业务。
常见的微服务组件及概念服务注册,服务提供方将自己调用地址注册到服务注册中心,让服务调用方能够方便地找到自己。
服务发现,服务调用方从服务注册中心找到自己需要调用的服务的地址。
负载均衡,服务提供方一般以多实例的形式提供服务,负载均衡功能能够让服务调用方连接到合适的服务节点。并且,节点选择的工作对服务调用方来说是透明的。
服务网关,服务网关是服务调用的唯一入口,可以在这个组件是实现用户鉴权、动态路由、灰度发布、A/B 测试、负载限流等功能。
配置中心,将本地化的配置信息(properties, xml, yaml 等)注册到配置中心,实现程序包在开发、测试、生产环境的无差别性,方便程序包的迁移。
API 管理,以方便的形式编写及更新 API 文档,并以方便的形式供调用者查看和测试。
集成框架,微服务组件都以职责单一的程序包对外提供服务,集成框架以配置的形式将所有微服务组件(特别是管理端组件)集成到统一的界面框架下,让用户能够在统一的界面中使用系统。
分布式事务,对于重要的业务,需要通过分布式事务技术(TCC、高可用消息服务、最大努力通知)保证数据的一致性。
调用链,记录完成一个业务逻辑时调用到的微服务,并将这种串行或并行的调用关系展示出来。在系统出错时,可以方便地找到出错点。
支撑平台,系统微服务化后,系统变得更加碎片化,系统的部署、运维、监控等都比单体架构更加复杂,那么,就需要将大部分的工作自动化。现在,可以通过 Docker 等工具来中和这些微服务架构带来的弊端。 例如持续集成、蓝绿发布、健康检查、性能健康等等。严重点,以我们两年的实践经验,可以这么说,如果没有合适的支撑平台或工具,就不要使用微服务架构。
一般情况下,如果希望快速地体会微服务架构带来的好处,使用 Spring Cloud 提供的 服务注册(Eureka)、服务发现(Ribbon)、服务网关(Zuul) 三个组件即可以快速入门。其它组件则需要根据自身的业务选择性使用。
微服务架构有哪些优势劣势?要谈优势,就一定要有对比,我们可以尝试着从 两个维度 来对比。
一、微服务架构与单体架构的对比S/N | 对比点 | 微服务架构 | 单体架构 | 结论 |
---|---|---|---|---|
1 | 上手难度 | API 接口调用 | 数据库共享或本地程序调用 | 单体架构胜 |
2.1 | 开发效率(简单项目) | 早期设计和沟通的工作量加大,随着项目规模和时间的推移,效率变化不大 | 早期工作量小,随着项目规模和时间的推移,效率大幅度下降 | 单体架构胜 |
2.2 | 开发效率(复杂项目) | 早期设计和沟通的工作量加大,随着项目规模和时间的推移,效率变化不大 | 早期工作量小,随着项目规模和时间的推移,效率大幅度下降 | 微服务架构胜 |
3 | 系统设计(高内聚低耦合) | 每个业务单独包装成一个微服务,数据和代码都从物理上隔离开来,实现高内聚低耦合相对容易 | 以包的形式对代码进行模块划分,控制得当即可实现高内聚。但最终都是在数据层面将整个系统耦合在一起 | 微服务架构胜 |
4 | 系统设计(扩展性) | 独立开发新模块,通过 API 与现有模块交互 | 在现有系统上修改,与现存业务逻辑高度耦合 | 微服务架构胜 |
5 | 需求变更响应速度 | 各个微服务组件独立变更,容易实施敏捷开发方法 | 需要了解整个系统才可以正确修改,容易导致不相关模块的意外失败 | 微服务架构胜 |
6 | 系统升级效率 | 各个微服务组件独立升级,上手和开发效率高,影响面小 | 需要了解整个系统才可以正确修改,容易导致不相关模块的意外失败 | 微服务架构胜 |
7 | 运维效率 | 大系统被拆分为多个小系统,部署和运维难度加大,但可以利用 DevOps 等方式将运维工作自动化 | 简单直接 | 单体架构胜 |
8 | 知识积累 | 微服务组件可以在新项目中直接复用,包括前端页面 | 一般以共享库的形式复用后台代码 | 微服务架构胜 |
9.1 | 硬件需求(简单项目) | 一个系统需部署多个微服务,需要启动多个运行容器 | 整个系统只需要一个运行容器 | 单体架构胜 |
9.2 | 硬件需求(高要求项目) | 按需为不同业务模块伸缩资源节点 | 为整个系统分配资源,导致冗余 | 微服务架构胜 |
10.1 | 项目成本(简单系统) | 项目早期和后期,成本变化曲线平缓 | 项目早期成本低,后期成本大 | 单体架构胜 |
10.2 | 项目成本(复杂系统) | 项目早期和后期,成本变化曲线平缓 | 项目早期成本低,后期成本大 | 微服务架构胜 |
11 | 非功能需求 | 为单独的微服务按需调优,甚至更换实现方式和程序语言 | 为整个系统调优,牵一发而动全身 | 微服务架构胜 |
12 | 职责、成就感 | 拥有明确的职责划分,主人翁意识和成就感加强,容易形成自组织型团队 | 职责不明确,容易产生扯皮行为 | 微服务架构胜 |
13 | 风险 | 大系统被拆分为小系统,风险可被控制在小系统内,但也引入了各小系统之间的交互风险 | 系统是一个整体,一荣俱荣,一损俱损 | 微服务架构胜 |
结论:
对于 简单项目 来说,单体架构 5 胜 8 败。
(优势项:开发效率、上手难度、运维效率、硬件需求、项目成本;劣势项:系统设计(高内聚低耦合)、系统设计(扩展性)、需求变更响应速度、系统升级效率、知识积累、非功能需求、职责、成就感、风险)
对于 复杂项目 来说,单体架构 2 胜 11 败。
(优势项:上手难度、运维效率;劣势项:硬件需求、项目成本、开发效率、系统设计(高内聚低耦合)、系统设计(扩展性)、需求变更响应速度、系统升级效率、知识积累、非功能需求、职责、成就感、风险;)
二、微服务与共享库的对比注:这里以使用方自行安装微服务为场景来比较。这里的共享库指的是像 Java 中的公共 jar 依赖。
S/N | 对比点 | 微服务 | 共享库 | 结论 |
---|---|---|---|---|
1 | 易用性 | 整体安装 + API 调用 | 共享库引用 + 本地调用 | 平手 |
2 | 程序耦合度 | 微服务为完整的业务逻辑单元,通过 API 的形式为其它模块提供服务 | 在使用方的源代码中引用共享库的类和方法 | 平手 |
3 | 版本升级 | 单独升级,其它模块无感知 | 修改源代码,升级使用方的代码版本,例如 pom.xml, build.gradle | 微服务架构胜 |
4 | Bug 修复 | 单独升级,自动生效 | 修改源代码,升级使用方的代码版本,例如 pom.xml, build.gradle | 微服务架构胜 |
5 | 非功能需求 | 为单独的微服务优化或扩缩容;在需求更高的情况下,可以重新设计或使用不同的程序语言 | 为整个业务系统优化或扩缩容,共享库的程序语言必须和业务系统的程序语言相同 | 微服务架构胜 |
6 | 复用程度 | 可以复用从前端页面到后台数据库的整个业务逻辑和代码 | 可以复用后台代码和数据库,但程序语言需要和业务系统保持一致 | 微服务架构胜 |
看了上面的比较,微服务架构可以说是以压倒性的优势胜过单体架构和共享库,会让人产生一种错觉,微服务架构就是软件开发中的银弹。
但是,正如大家所了解的,软件研发是一个系统工程,它没有银弹,不能够一招鲜吃遍天。正如当年的 CMMI 和敏捷方法一样,敏捷虽好,但它不一定能适用于所有的场景,它对组织环境、团队氛围、沟通方式、技术能力这些都是有一些要求的,如果用不好,反而会带来一些负面影响。
那么,我们什么时候需要采用微服务呢?从我个人的经验来看,我认为有三种场景可以考虑使用微服务。
规模大(团队超过 10 人)
业务复杂度高(系统超过 5 个子模块)
需要长期演进(项目开发和维护周期超过半年)
这里借一张图来说明:
横轴是复杂度,纵轴是生产效率。从生产效率的角度来讲,在两条曲线的交叉点之前,单体架构是占优势的,过了交叉点之后,单体架构的生产效率将大幅度下降。
所以很多专家和同行朋友都说,我们可以在开始的时候先使用单体架构,当业务发展到一定程度的时候,再重构成微服务架构。对于这一点,我是持保留意见的,因为在实践中,架构改造的难度还是很大的,会有一些问题,例如:
客户或业务部门是否给我们这样的时间窗口?
这段时间的研发经费是否有出处?
项目负责人或技术团队是否有主动的意愿进行架构升级?
项目负责人或技术团队是否愿意为架构升级带来的不稳定风险负责?
我们常常听到的一句话是:暂时先这样,等我们没这么忙的时候,再来优化一下。但是,绝大多数情况下,这一天从来没有出现过。
再想想年初,我们的私有云平台经过 2 年多的发展,已经包含了容器服务平台(PaaS)、API 网关、监控平台、定时任务平台、数据库管理、用户权限管理等等十多个基础模块,也包含了一些为上层应用服务提供的日志服务、缓存服务、消息服务等等。并且,部署到了二十多个客户的生产环境里。可悲的是,我们支撑了很多的业务系统的微服务化,但平台本身任然是一个单体系统。
我们也深深地感受到了平台往前发展的阻力:
很多时候,客户需要的不是一个大而全的平台,他们希望按他们的意愿采购需要的模块。
新人进入团队后,从熟悉到动手产出的时间偏长。
其它研发团队有一些比较好的组件能满足平台产品的需求,却不能直接拿来用。
两个不同的模块之间产生了不该出现的耦合关系,导致意想不到的 Bug。
所以,春节过后,大家开了一个会,决定将平台微服务化。而带来的代价就是要说服老板给我们两个月时间来重构。
幸运的是,我们很快得到了老板的支持,并且重构工作比较顺利;不幸的是,那二十多个客户的生产环境的升级非常麻烦,每升级一个客户都得花上一周左右的时间,至今也才升级了一小部分。
所以,理想的情况下,我建议在项目初期的时候就从上面提到的三点做好评估,到底采用哪种架构形式是符合项目具体情况的。
当然,如果真的有朋友想将历史悠久的单体架构升级到微服务架构,我建议先从边缘逻辑开始,逐步逐步地将业务逻辑从单体系统里剥离出来。我没有这方面的经验,但可以想象,这将是一个非常长期和痛苦的过程。
作者介绍苏槐,微信号 Sulaohuai,青柳云研发总监,现服务于神州数码青柳云团队,曾就职于 Oracle,新加坡电信等企业。擅长容器技术、微服务架构、敏捷开发及技术管理。