⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 AI产品阿颖 「张向前」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言都将得到认真回复。甚至不知道如何读源码也可以请教噢。
  4. 新的源码解析文章实时收到通知。每周更新一篇左右。
  5. 认真的源码交流微信群。

架构有时候挺难捉摸的——人们不断提出新想法,这些想法在没有任何背景或者细微差别的情况下快速成为主流“实现方式”;而迫于寻求架构改进之法的行业则盲目跟进,不假思索地全盘接受。

在这股大有问题的风潮中,微服务正是最新一代弄潮儿。可微服务到底哪里好,我们又为什么要使用微服务?是时候对这个问题追根溯源了。

1.究其根本,微服务号称能带来……

很多好东西!

  • 可扩展性:能把代码拆分成更小的部分,各自独立开发、测试、部署和更新。
  • 专注:让开发者专注于解决业务问题和业务逻辑。
  • 可用性:后端数据必须始终可用于各种设备。
  • 简单性:简化大型企业级应用程序的开发。
  • 响应能力:让分布式应用程序得以扩展,从而响应不断变化的事务负载。
  • 可靠性:复制的服务器分组可在发生故障时继续保持运行,从而消除单点故障。即使发生故障,运行中的应用程序也可恢复至良好状态。

这些听着都挺耳熟,但这六条说法其实各有来历:两条来自微服务文献 (博文、论文等),两条来自20年前的EJB文献,两条来自Oracle Tuxedo——一项四十多年前的技术。各位朋友分得清它们的真实归属吗?

换句话说,咱们这个行业其实一直在用同样的炒作方法,同样的宣传伎俩。

不记得过去的人,注定要重蹈覆辙。

——George Santanyana,《理性的生活》(1905年)[1]

说起微服务这边的炒作,一篇企业博文[2]列出了选择微服务的十大理由:

  1. 能普及大数据最佳实践。微服务天然适用于数据管道架构,符合大数据的收集、摄取、处理和交付方式。数据管道中的各个步骤都会以微服务的形式处理各项小任务。
  2. 相对易于构建和维护。微服务的用途单一,这种设计意味着其能以较小的团队构建和维护。各个团队可以跨职能组建,同时也能专注于解决方案中的微服务子集。
  3. 支持更高代码质量。将整体解决方案模块化为离散组件,有助于应用开发团队每次专注于一小部分内容,从而简化整个编码和测试过程。
  4. 简化了跨团队协调流程。与以往涉及重量级进程间通信协议的传统面向服务架构(SOA)不同,微服务使用事件流技术降低了集成门槛。
  5. 支持实时处理。微服务架构的核心是发布/订阅框架,能够支持实时数据处理以提供即时输出与洞察。
  6. 有助于快速增长。微服务使得代码和数据能够重用模块化架构,轻松部署更多数据驱动的用例和解决方案,借此增加业务价值。
  7. 能带来更多产出。数据通常会以不同方式呈现给不同受众;微服务简化了为各最终用户提取数据的方式。
  8. 易于评估应用程序生命周期中的更新。高级分析环境,包括那些用于机器学习的分析环境,往往需要相应方法来根据新建模型评估原有计算模型。微服务架构中的A/B和多变量测试使用户能够轻松验证自己更新后的模型。
  9. 可实现规模伸缩。可伸缩性不仅有助于提供更多容量,也能轻松凸显出扩展瓶颈,之后在各微服务层级上解决这些瓶颈。
  10. 拥有大量流行工具。大数据世界中的各种技术(包括开源社区成果)在微服务架构中均运行良好。Apache Hadoop、Apache Spark、NoSQL数据库和各类流分析工具皆可用于微服务。

宣传材料看得够多了,下面我们一一对这些说法进行甄别,只是这回要从真实技术的角度出发:

  1. 能普及大数据最佳实践。自70年代以来,管道和过滤器架构就一直是软件工程中的组成部分。当时Unixes提出了几条原则[3]:

    让每个程序做好一件事。如果需要完成一项新工作,别通过添加新“功能”让旧程序复杂化,而应重新构建新程序。

    应将每个程序的输出看作另一个未知程序的输入。不要用无关信息混淆输出。严格避免使用列式或二进制输入格式,也不要坚持使用交互式输入。

  2. 相对易于构建和维护。参见以上Unix原则。

  3. 能支持更高质量的代码。如果一次专注于一小部分有助于提高质量,那请参见以上Unix原则。

  4. 简化了跨团队协调流程。这一点非常有趣,里面提到“面向服务架构”(SOA)……往往涉及重量级进程间通信协议——比如说JSON over HTTP?或者说,这是指一切SOA都需要SOAP、WSDL、XML Schema和WS-完整规范集?讽刺的是,微服务并没有以任何方式阻止使用这些“重量级”协议,某些微服务甚至建议使用gRPC——一种跟IIOP非常相似的二进制协议,它来自CORBA,属于SOAP、WSDL、XML Schema和WS-完整规范集等“重量级”协议的前身。

  5. 支持实时处理。实时处理其实并不是什么新鲜事物,多数此类系统确实需要用发布/订阅或者“事件总线”模型来实现,但跟微服务架构没什么必要联系。

  6. 有助于快速增长。“重用模块化架构”——好吧,大家还记得有多少事物都在以“重用”为卖点吗?编程语言当然是其一(OOP、函数式语言、过程语言),还有库、框架等等……也许有一天,大家会在忍无可忍下表示“去他的重用,我们不在乎”。

  7. 能带来更多产出。“数据集通常会以不同方式呈现给不同受众”,这说的不是SAP的Crystal Reports吗?

  8. 易于评估应用程序生命周期中的更新。机器学习和高级分析环境需要“根据新建模型评估现有计算模型”……听着挺热门的,但其实并没表达出什么实质内容。

  9. 可实现规模伸缩。这就有点好笑了,EJB、事务性中间件处理(比如Tuxedo)和大型机,谁不能实现规模伸缩?

  10. 拥有大量流行工具。我得说一句,行业中的每次重大炒作都少不了工具的捧场,特别是在实在没什么可宣传的时候。可能大部分读者朋友没经历过CASE的时代,但至少对UML有所耳闻吧?

而且聪明的朋友可能已经注意到,上面大概一半的观点都在强调共通的主题——创建和维护更小、更独立的代码和数据“块”,让各块间相互版本化,使用共同的输入和输出以实现整体系统集成。这就好像……

2.究其根本,我们在微服务中发现的是……

模块。

没错,大量的低级“模块”。这个诞生自1970年代的概念,一直是大部分编程语言的核心。也可能更早,只是很多旧语言并没有把相同的设计理念概括成“模块”这个表达。

比如在CLR(C#、F#、Visual Basic等)中,它被称为“程序集”;在JVM(Java、Kotlin、Clojure、Scala、Groovy等)上,则被称为“JAR”或“包”;在我们熟悉的操作系统动态链接库上也有它的身影(比如Windows上的DLL,Unix上的so或a,还有MacOS那隐藏在/Library目录下的Frameworks)。

无论如何,它们都服务于相同的目标:创造一个独立构建、管理、版本控制和部署的代码单元,以供重复使用。

考虑到模块的这一实际定义,我们引用一篇计算机科学基础论文中的表述:

项目工作的明确定义与细分,保障了系统的模块化。每个任务都将形成一个独立的、不同于其他的程序模块。在实现时,每个模块及其输入/输出都经过明确定义,与其他系统的预期接口不会混淆。在检查开始前,先对各个任务进行同步以避免调度问题。最终,系统会以模块化形式进行维护;系统错误和缺陷可被追溯至特定的系统模块,从而限制错误搜索的具体范围。

以上内容来自David Parnas撰写的开创性论文《On the Criteria To Be Used in Decomposing Systems into Modules[4]》,文章是在50多年前的1971年写成的。这里提出的明确定义“独立的、不同于其他的程序模块”已经涵盖了微服务的约半数优势,就是说同样的方法我们已经用了50年之久。

所以,何必要为微服务而激动不已?

毕竟微服务的实质跟微服务、服务甚至是分布式系统,根本就没有关系。

3.微服务的实质,在于……

组织清晰度。

亚马逊是最早公开讨论微服务概念的企业之一,但他们自己并没有特别积极地运用这种新架构。毕竟为什么要瞎折腾呢,DBA团队得做相应的架构变更、QA需要开发新测试来发现bug、基础设施团队得采购新一批服务器、UX团队则要为演示创建原型。这些都是额外的负担,也代表着现实阻力。

在这种情况下,开发团队根本就没法顺利推进自己预想的架构调整。而以上提到的各个部门,只是普通IT团队中各类职能(分析、开发、设计、测试、数据管理、部署、管控等)的小缩影。

要想实际推动,则要么保证现有团队能找到不同的技术组合,要么硬性要求每位团队成员都具备完整的技能集(即所谓「全栈开发者」),可这无疑会大大增加招聘难度。另外,团队还需要对自己的生产中断事件负责,每位成员都得随叫随到——这些都会体现在薪酬待遇和法律影响上。当然,如果真能打通这些关节要害,那各个团队确实能够独立构建自己的工件,于是生产力水平将一路飙升、再无阻碍。

可这种只有理论可行性的事情,真能实现吗?

4.在微服务当中,我们还发现了……

分布式计算谬误。

有些朋友可能不太熟悉,这里的谬误来自Peter Deutsch在80年代给Sun的同事们做的演讲。1994年,Ann Wolrath和Jim Waldo的开创性论文《A Note on Distributed Computing[5]》则再次提及。

“无论如何定义「正确」,分布式系统都很难在性能、可靠性和可扩展性方面真正正确。”(松散解释)

当我们将系统拆分成运行在单一操作系统节点上的内存模块时,即使是在五十年前,跨进程或库边界进行数据传递的成本也可以忽略不计。但当我们开始跨网络线路传递数据时(也是目前大多数微服务的通行作法),则通信的延迟会瞬间提升五到七个数量级。这不仅仅要求我们向网络添加更多节点来“扩展”,同时也令整个系统快速恶化。

没错,通过将微服务托管在同一台机器上、加载到运行各独立微服务容器化镜像的虚拟机集群内,确实能降低不同服务间的相互影响。(例如使用Docker Compose或Kubernetes托管一组Docker容器。)但这种方式会增加虚拟机进程边界间的延迟(因为我们必须遵循七层模型规则,即使其中某些层可以完全模拟,产生的延迟仍无法忽视),而且会在各独立节点上引发运行可靠性问题。

更糟糕的是,还没等我们切实解决分布式计算谬误,那边又冒出了与之相关但又独立存在的新麻烦:企业计算谬误[6]。

5.探究微服务的核心,我们最好能……

重新思考自己的需求。

真有必要把问题拆分成一个个独立实体吗?用托管在Docker容器里的独立进程就能做到这一点,或者也可以在遵循标准化API约定或者其他选项的应用程序服务器中引入独立模块。

这是个有成熟解的问题,过去20年来已经有无数种技术能够实现,包括servlet、ASP.NET、Ruby、Python、C++,甚至是闻者头痛的Perl。其中的关键,是如何建立起易于理解的集成和通信约定的通用架构基础。

我们要不要减少开发团队面对的依赖项?认真观察这些依赖项,再跟合作伙伴共同确定可以把其中哪些做做整合。如果组织不想打破结构体系中“以技能为中心”的指导思路(就是把员工明确划分成「数据库组」、「基础设施组」、「QA组」和「开发组」等),那也至少要尝试建立一个边界相对模糊的报告结构,把每位成员视为团队“矩阵”里的一个元素。

而且最重要的是,一定要保证团队对自己正在构建的内容拥有清晰认知,保证他们能轻松向他人解释自己服务/微服务/模块的核心意义。关键是给团队以方向和目标、达成目标的自主权,同时辅以必要的鼓励和引导。

这就是事情的真相。你想要微服务?不,你想要的只是模块。

相关链接:

  1. https://americanart.si.edu/artwork/those-who-cannot-remember-past-are-condemned-repeat-it-george-santayana-life-reason-1905
  2. https://www.integrella.com/expertise/microservices/
  3. https://en.wikipedia.org/wiki/Unix_philosophy
  4. https://www.win.tue.nl/~wstomv/edu/2ip30/references/criteria_for_modularization.pdf
  5. https://scholar.harvard.edu/files/waldo/files/waldo-94.pdf
  6. http://blogs.newardassociates.com/blog/2016/enterprise-computing-fallacies.html
文章目录
  1. 1. 1.究其根本,微服务号称能带来……
  2. 2. 2.究其根本,我们在微服务中发现的是……
  3. 3. 3.微服务的实质,在于……
  4. 4. 4.在微服务当中,我们还发现了……
  5. 5. 5.探究微服务的核心,我们最好能……