穿越时空的软件架构之旅:软件架构设计演化史推演
在软件架构演化历程中,每一种风格的架构诞生并非一蹴而就,而是经历了持续的演变和优化。本部分内容主要探讨软件架构设计的演化史以及不同时代的演化过程。
一、原始分布式时代的 Unix 设计哲学下的服务探索
1 、Unix 的分布式设计哲学
Simplicity of both the interface and the implementation are more important than any other attributes of the system — including correctness, consistency, and completeness.
保持接口与实现的简单性,比系统的任何其他属性,包括准确性、一致性和完整性,都来得更加重要。
—— Richard P. Gabriel,The Rise of 'Worse is Better,1991
Unix 的分布式设计哲学奠定了分布式架构演化的基石,正如 Richard P. Gabriel 在 1991 年所言:“保持接口与实现的简单性,比系统的任何其他属性,包括准确性、一致性和完整性,都来得更加重要。”这一观念强调了系统的简洁性,使得接口和实现的简单化成为系统设计的核心。
目标:构建更大型的系统
原始分布式时代的目标是通过多个独立的分布式服务协同工作,构建更大型的系统。当时,计算机硬件的运算能力相对有限,限制了单台计算上信息系统软件所能达到的规模。为了突破这一限制,各个高效、研究机构和软硬件厂商纷纷开始尝试用多台计算机共同协作来支撑软件系统的运行。
探索与研究
原始分布式时代标志着对分布式架构最初步的探索与研究。由于硬件限制,将系统拆分到不同的机器上引发了一系列的问题,包括服务发现、跟踪、通信、容错、隔离、配置、传输、数据一致性和编码复杂度等多方面的挑战。然而,正是这个阶段,许多技术和概念被提出,对 Unix 系统和今天的计算机科学的多个领域产生了深远的影响。
影响与贡献
80 年代初期,惠普提出的网络运算架构(Network Computing Architecture,NCA)成为未来远程服务调用的雏形。
卡内基·梅隆大学提出的 AFS 文件系统(Andrew File System)是分布式文件系统的最早实现。
麻省理工学院提出的 Kerberos 协议,是服务认证和访问控制(ACL)的基础性协议,是分布式服务安全性的重要支撑,目前包括 Windows 和 macOS 在内的众多操作系统的登录、认证等都会利用到这个协议。
2 、挑战与教训
尽管原始分布式时代取得了显著的成就,但由于硬件限制,将系统强行分布到不同机器中导致了许多问题。
在这一时期,计算机科学家 Kyle Brown 回顾道:“这次尝试最大的收获就是对 RPC、DFS 等概念的开创,以及得到了一个价值千金的教训:某个功能能够进行分布式,并不意味着它就应该进行分布式,强行追求透明的分布式操作,只会自寻苦果。”
原始分布式时代为软件架构的后续演化奠定了基础,同时也为我们提供了宝贵的经验教训,为更高层次的架构设计奠定了基础。
二、单体系统时代的架构风格
在软件架构的演进中,单体架构是最早出现、应用最广泛、使用最为普遍、统治历史最长的架构风格之一。
1 、单体架构的地位
首先,我们需要澄清一个思维误区:单体架构并不等同于落后的系统架构,也并非注定会被微服务所取代。在许多关于微服务的文献中,单体架构往往被描绘成反派,但很多时候他们指的是大型的单体系统。
对于小型系统来说,仅需单台机器支撑其运行的情况下,采用单体架构风格是完全合理的选择。这种架构不仅易于开发、易于测试、易于部署,而且由于各功能、模块、方法的调用在同一进程内完成,避免了进程间通信,提高了程序的运行效率,相较于分布式系统更具优势。
在探讨单体架构系统缺陷的时候,是要基于软件系统的性能需求超过了单机,软件研发人员规模明显超过了“2 Pizza Teams”范畴的前提下,这样的单体架构缺陷讨论才更有价值。
2 、两个披萨原则
“两个披萨原则”最初由亚马逊的 CEO 贝索斯提出,他认为如果两个披萨无法满足一个项目团队的需求,那么这个团队可能规模太大。人数过多的项目团队会阻碍决策的形成,而小团队更有利于形成共识,有效推动企业内部的创新。
3 、单体系统的可拆分性
单体系统具备纵向和横向两个方向的拆分能力:
纵向拆分
在过去的工作经历和现代信息系统中,几乎不可能找到一个完全不分层的大型系统。
分层架构是广泛采用的软件设计方法,无论是单体还是微服务,都会对代码进行纵向拆分,外部请求在各层之间以不同形式的数据结构进行流转,最终返回响应。
横向拆分
横向拆分指的是单体架构支持按技术、功能、职责等角度将系统拆分为各种模块,以便于重用和团队管理。
通过在负载均衡之后同时部署多个单体系统的副本,可以达到分摊流量压力的效果,使单体架构在横向上也能够轻松实现扩展。
4 、非独立的单体
然而,单体系统的缺陷并非在于如何拆分,而在于拆分后存在隔离与自治能力的不足。
在单体架构中,所有代码运行在同一个进程空间内,任何一部分代码出现问题都可能影响全局。内存泄漏、线程爆炸、阻塞、死循环等问题难以隔离。此外,由于共享同一进程空间,无法单独停止、更新、升级某一部分代码,导致缺乏动态可维护性。
在单体系统的潜在观念中,每个部件都应尽量可靠,致力于构建一个 7 * 24 小时不间断的可靠系统。在小规模软件上,这种观念运作良好。然而,随着系统规模的增大,构建可靠系统的观念开始从追求尽量不出错转变为正视出错是必然。实际上,这正是微服务架构能够挑战并逐步取代单体架构的根本驱动力。
三、SOA 时代的探索与挑战
在软件架构的历史演进中,Service Oriented Architecture(SOA)时代标志着对分布式服务构建信息系统的广泛应用。
SOA 架构具有完善的理论和工具,被认为解决了分布式系统中的许多关键技术问题。然而,尽管具备一系列成功的理论,SOA 并没有成为一种普适的软件架构,而在实践中也面临了一些挑战。
1 、三种服务拆分架构模式
烟囱式架构(Information Silo Architecture)
信息烟囱,又称为信息孤岛,使用这种架构的系统,也被称为孤岛式信息系统或者烟囱式信息系统。
指系统完全不会跟其他相关的系统之间进行协调工作,各自使用独立的数据库和服务器。
实际中,很难找到在一家公司内完全不交互的系统。
微内核架构(Microkernel Architecture)
亦称为插件式架构(Plug-in Architecture),将不同系统共享的公共数据、服务、资源集中到一个核心系统,业务系统则以插件模块的形式存在。
微内核架构适用于产品型应用系统,支持灵活扩展和增量开发。
事件驱动架构
为了实现子系统之间的通信,通过建立事件队列管道,将系统外部的消息以事件形式发送到管道中。每个消息处理者都是独立解耦的,通过事件管道进行互动。
2 、SOA 架构时代的探索(Service Oriented Architecture,SOA)
在 SOA 时代,很多概念思想已经在微服务中找到了应用,如服务之间的松散耦合、注册、发现、治理、隔离和编排等。
SOA 针对分布式服务的困难进行了多方面的探索,包括具体的技术标准和软件设计原则。
更具体的探索
拥有领导制定技术标准的组织 Open CSA:SOA 不仅仅是一种架构风格了,而是一套软件架构的基础平台
具有清晰的软件设计的指导原则,比如服务的封装性、自治、松耦合、可重用、可组合、无状态等
明确了采用 SOAP 作为远程调用的协议,依靠 SOAP 协议族(WSDL 、 UDDI 和一大堆 WS* 的协议)来完成服务的发布、发现和治理
利用企业服务总线(Enterprise Service Bus,ESB)的消息通道来实现各个子系统的通信,在 ESB 的调度下不需要互相依赖就可以实现通信,既松耦合,又为实现业务流程编排(Business Process Management,BPM)提供基础
使用服务数据对象(Service Data Object,SDO)来访问和表示数据,使用服务组件架构(Service Component Architecture,SCA)来定义服务封装的形式和服务运行的容器
更系统的探索
SOA 最根本的目标是希望总结出一套自上而下的软件研发方法论,解决研发过程中的各种问题,包括需求挖掘、业务能力分解、已有服务的编排、新功能的开发测试等
21 世纪最初十年,有 IBM 为其助威,盛极一时,但最终沉寂
SOA 架构虽然可以实现多个异构大型系统之间的复杂集成交互,但由于过于严格和复杂的规范定义,给架构设计带来了过多的复杂性。
SOA 并非一种具有普适性的架构风格,这在实际应用中变得明显。
此外,SOA 在 21 世纪初的十年中失去了活力,尽管提出了许多理念和思想,但并未在实践中广泛应用。这也让人们开始思考更为灵活和可行的架构模式,为接下来的演进铺平道路。
四、微服务时代的特征与挑战
1 、微服务时代的起源与演变
微服务的起源可以追溯到 2005 年的云计算博览会(Web Services Edge 2005),当时被称为 Micro-Web-Service。作为对 SOA 的轻量级补救方案,微服务最初并没有受到广泛关注,因为它被认为只是对 SOA 的修修补补,难以激发技术人员的兴趣。然而,在最近过去的十年中,微服务经历了思考和演变。
2012 年,Thoughtworks 首席咨询师 James Lewis 在波兰克拉科夫的 33d Degree Conference 上的《Microservices - Java,the Unix Way》主题演讲中,强调了微服务的原则,如单一服务职责、康威定律、自动扩展和领域驱动设计。这标志着微服务开始摆脱对 SOA 的依赖,呼吁重拾 Unix 设计哲学。
真正的崛起发生在 2014 年,由 Martin Fowler 和 James Lewis 合著的文章《Microservices:a definition of this new architectural ter》中,首次明确了微服务的定义。微服务被定义为通过多个小型服务的组合构建单个应用的架构风格,这些服务围绕业务能力而非特定技术标准构建,可以采用不同的编程语言、数据存储技术,运行在不同的进程中。这一定义成为微服务的真正起源。
2 、微服务的核心特征
围绕业务能力构建(Organized around Business Capabilities)
微服务强调康威定律的重要性,即团队的结构、规模和能力将产生对应的产品结构、规模和能力。
团队和产品磨合稳定后,将拥有一致的结构,避免了跨团队沟通和高成本的跨团队边界。
分散治理(Decentralized Governance)
微服务倡导谁负责谁治理,开发团队直接对服务运行质量负责,不受外界干预,可以选择和其他服务异构的技术来实现自己的服务。
强调对技术栈的选择权,并不强迫统一。
通过服务来实现独立自治的组件(Componentization via Services)
强调通过服务而不是类库来构建组件,因为服务是进程外组件,通过远程调用提供功能,带来隔离与自治的能力。
服务的独立性是通过远程调用的代价来实现的。
产品化思维(Products not Projects)
微服务要求将架构看作是一种持续改进和提升的过程,避免将软件研发视为完成某种功能的任务。
开发团队应为整个软件产品的生命周期负责,强调软件的持续改进和用户反馈。
数据去中心化(Decentralized Data Management)
微服务提倡将数据按领域分散管理、更新、维护和存储。
不同服务对同一数据实体的抽象形态可能是不同的,避免了中心化存储导致的服务互相影响和失去独立性的问题。
例:
比如,一个商品,在销售域中关注的是价格,在仓储域中关注的是库存数量,在商品展示域中关注的是商品详情。
如果作为中心化的存储,那这里所有域都必须修改和映射到同一个实体中,就会导致不同的服务之间,可能会互相产生影响,丧失各自的独立性。
轻量级通讯机制(Smart Endpoints and Dumb Pipes)
微服务倡导简单直接的通信方式,强调弱管道(Dumb Pipes)机制机制,反对过于复杂的通信机制,比如 SOAP 、 BPM 等。
通过类似于 Unix 过滤器的简单通信方式,比如 RESTful 风格,实现服务之间的通信。
容错性设计(Design for Failure)
微服务接受服务总会出错的现实,要求在设计中考虑自动故障检测、隔离和服务恢复的机制。
容错性设计的目的是防止一两个服务的崩溃引发雪崩效应。
可靠系统完全可以由会出错的服务来组成,这也是微服务架构最大的价值所在。
演进式设计(Evolutionary Design)
微服务设计应该能够被淘汰,而不是期望服务的长期存在。
良好的设计应该是可演进的,不可更改和无可替代的服务反映了系统设计的脆弱性。
微服务强调独立自治,鼓励设计能够随着需求的变化而演进。
基础设施自动化(Infrastructure Automation)
微服务架构下,基础设施的自动化(如 CI/CD)降低了构建、发布、运维的复杂性。
面对微服务的数量级增长,团队更加依赖基础设施的自动化来应对挑战。
3 、微服务时代的挑战
微服务架构的广泛应用降低了软件研发的复杂性,但也带来了新的挑战。
对于开发者而言,微服务提供了友好的开发体验,但对架构师则提出了史无前例的要求。架构师需要具备广泛的知识面,以便在面临选择时能够权衡利弊。
微服务架构没有统一的规范和约束,各种中间件层出不穷,服务注册发现、负载均衡、认证授权等问题需要根据具体情况选择适当的解决方案。比如,在服务间通信方面,存在多种解决方案如 RMI、Thrift、Dubbo、gRPC、Motan2、Finagle、brpc、Arvo、JSON-RPC、REST 等。对于服务发现,可选的方案有 Eureka、Consul、Nacos、ZooKeeper、etcd、CoreDNS 等。
微服务时代的中间件百花齐放,架构师需要具备对这些工具和框架的熟悉程度,以便做出明智的选择。
总体而言,微服务架构为开发者提供了更灵活的开发和部署方式,但对于架构师来说,需要面对更高的决策难度和挑战。
微服务时代的演进既为软件架构带来了新的活力,也要求从业者不断学习和适应。
五、云原生时代的挑战与演进
1 、云原生时代的背景与挑战
随着微服务架构的普及,云原生时代迅速崛起,跨越了软件与硬件之间的界限。
在微服务架构中,诸如注册发现、跟踪治理、负载均衡、传输通信等问题一直存在,但是否一定要由分布式系统解决呢?
先不看架构层面的解决方案,仅看这些问题本身的解决方案:
系统需要伸缩扩容,一般是采购服务器,多部署几套副本实例来分担压力
系统需要负载均衡,一般是搭建负载均衡器,并选择合适的均衡算法来分流
系统需要安全传输,一般是搭建 TLS 传输链路,配置 CA 证书,保证通信不被窃听篡改
系统需要服务发现,一般设置 DNS 服务器,让服务访问依赖稳定的服务名而非易变的 IP 地址
因此,这些问题基本上都有专门的基础设施来解决。
但在微服务架构中不得不在应用服务层面而不是基础设施层面去解决这些问题,归根到底是因为硬件构成的基础设施,跟不上由软件构成的应用服务的灵活性。
2 、基础设施的演进:容器化技术的巨大贡献
容器化技术,特别是 Docker 的出现,为解决分布式系统特有问题的事后提供了新的途径。
通过虚拟化和容器化技术,基础设施得以发展到多个容器构成的集群,软件和硬件之间的界限变得模糊。
3 、容器生态的历史里程碑:Kubernetes 的崛起
2017 年,Kubernetes 在容器生态中取得了胜利,成为容器编排系统的代表。
Kubernetes 的出现标志着容器生态的发展迈出了重要的一步,使得虚拟化的基础设施从单个服务的容器扩展到多个容器构成的集群。在这个集群中,所有通信、存储设施得以实现,软件和硬件的边界变得模糊。
4 、云原生时代的演进
云原生时代的演进并非只是简单地解决了之前的问题,而是提出了一种新的思维方式。
在云原生时代,软件和硬件的界限不再是绝对的,硬件能够跟得上软件的灵活性。
因此,那些与业务无关的技术问题可以从软件层面剥离出来,在硬件的基础设施内解决掉,使软件能够更专注于业务。
这就是云原生时代追求的目标。
5 、云原生时代的挑战:服务网格的边车代理模式
云原生时代虽然解决了许多问题,但仍然面临着新的挑战。它并不能完美解决一些处于应用系统与基础设施边缘的问题。
例:
图中微服务 A 调用了微服务 B 中发布的两个服务 B1 和 B2,如果 B1 正常,但是 B2 出错,此时正常做法是对 B2 进行熔断,避免出现雪崩效应。但是如果在基础设施层面来做,就会切断 A 到 B 的通路,这样就会影响正常服务 B1 的使用。当然这个问题在 Spring Cloud 的应用代码是很好解决的,但从基础设施层面就很难,因为它是针对整个容器进行管理的,粒度比较粗。而且在服务的监控、认证、授权、安全、负载均衡上均有细化管理的需求。
为了解决这一问题,基础设施进行了第二次演进,即服务网格(Service Mesh)的边车代理模式(Sidecar Proxy),边车即在基础设施中由系统自动地在服务的资源容器(比如 Kubernetes 中的 prod)中注入一个通信代理服务器,用类似网络安全里中间人攻击的方式进行流量劫持,在应用无感知的情况下,接管应用所有对外通信。这个代理除了会实现正常的服务调用以外(数据平面通信),还接手来自控制器的指令(控制平面通信),根据控制平面中的配置,分析数据平面通信的内容,实现熔断、认证、度量、监控、负载均衡等附加功能。
6 、云原生时代的未来展望
云原生时代的未来充满了机遇和挑战。
随着技术的不断演进,云原生架构将继续迭代,解决新的问题,提供更好的开发和部署体验。
同时,从业者需要不断学习和适应,面对云原生时代带来的新挑战。
在这个演进的过程中,软件和硬件之间的关系将更加紧密,为构建更强大、灵活和可靠的系统奠定基础。
六、无服务时代的崭新篇章
1 、无服务时代的兴起
无服务时代的起源可以追溯到 2012 年,当时 iron.io 公司首次提出了无服务(Serverless)的概念。
分布式架构最初的目标是解决单台机器性能瓶颈的问题,但随着技术的发展,新的问题应运而生。而无服务时代的兴起正是为了解决分布式架构中面临的容错能力、技术异构、职责划分等问题。
2 、无服务时代的商业化与认可
2014 年,AWS 发布了 Lambda,成为商业化无服务应用的开创者,逐渐被认可并发展成全球最大的无服务运行平台。
2019 年,阿里云、腾讯云等也相继推出了相应的无服务产品。
这标志着无服务时代取得了商业化的成功。
3 、学术界的预言与验证
UC Berkeley 大学于 2009 年发表的论文《Above the Clouds:A Berkeley View of Cloud Computing》预言了云计算的价值、演进和普及在未来十年逐一被验证。
随后,2019 年发布的《Cloud Programming Simplified:A Berkeley View on Serverless Computing》再次预言无服务将成为未来云计算的主流方式。
4 、无服务的简单之美
无服务的魅力在于其简单性,它仅涉及后端设施(Backend)和函数(Function)两部分。
后端设施:指数据库、消息队列、日志、存储等用于支撑业务逻辑运行,但本身无业务含义的技术组件,都在云上,被称为后端即服务(BaaS)
函数:指业务逻辑代码,运行在云端,无需考虑算力和容量问题(仅技术不考虑,财务还是需要考虑钱的),被称为函数即服务(FaaS)
5 、无服务的优势与便利性
无服务的设计初衷是为了让开发者只需要纯粹地关注业务,不需要考虑技术组件,这些在无服务中都是现成的,可以直接使用,不存在采购、版权和技术选型的烦恼;
同时不需要考虑如何部署,整个部署过程都是托管给云,云端自动完成;
再就是不需要考虑算力,只要你有钱,它背后是整个数据中心的支撑,算力可以默认为无限大;
最后就是不需要运维,系统的稳定性由云服务商来保证,不需要开发者操心。
6 、无服务时代的挑战和局限性
尽管无服务架构的前景看好,但在短期内的发展并非一帆风顺。
与单体架构、微服务不同,无服务具有天生的特点,如冷启动、无状态、运行时间限制等,使得它并非一种普适性的架构模式。
适用场景受到限制,主要在不需要交互的大规模计算、Web 资讯类网站、小程序、公共 API 服务、移动应用服务端等场景。而在一些信息系统、网络游戏应用、业务逻辑服务依赖服务端状态、响应速度要求高、需要长链接的应用场景来说,无服务架构是不太适合的。
结语
架构演进是一个不断推陈出新的过程。
微服务并非终点,无服务也只是演进历史中的一部分。未来,我们期待看到更多新的架构模式的涌现,为构建更强大、灵活和可靠的系统提供更多可能性。
在这个不断变化的技术领域,我们需要持续关注创新,灵活适应新技术的发展,并在实践中不断总结经验教训。
无论是单体、微服务还是无服务,都是为了更好地满足业务需求而服务的工具,选择合适的架构模式需要综合考虑业务特点、团队实力以及技术栈的适配性。
通过对架构演进历史的了解,我们可以更好地应对未来的挑战,构建出更加稳健和创新的系统。
附录
图灵名言
We can only see a short distance ahead,but we can see plenty there that needs to be done.
尽管目光所及之处,只是不远的前方,即使如此,亦然可以看到那里有需要值得去完成的工作在等待我们。
—— Alan Turing,Computing Machinery and Intelligence,1950
问题探讨
Unix 设计哲学说保持接口与实现的简单性,比系统的任何其他属性都更加重要,那在你现在负责和参与的系统架构方案中,是如何看待简单的?
微服务、云原生时代的到来,那在未来的发展中,单体架构会是什么一样的一个发展路径呢?
你现在负责和参与的系统架构方案是微服务的架构模式吗?如果是,它符合九大特征吗?如果不是,那你们适合微服务架构模式吗?
分布式架构到了服务网格的时代,是最好的时代吗?今天的云原生架构模式还有哪些问题呢?
版权声明: 本文为 InfoQ 作者【灸哥漫谈】的原创文章。
原文链接:【http://xie.infoq.cn/article/6b34986361b2a9f8f40cf5191】。文章转载请联系作者。
评论