周志明的软件架构课笔记(一)架构演变

755 阅读20分钟

《周志明的软件架构课》学习笔记,整理备忘。


一、远古时代的分布式探索

20 世纪 70 年代末到 80 年代初,美国各机构为弥补计算机的算力不足开发了诸多分布式相关协议,为现代分布式架构的再度崛起奠定了基础。

  • 网络计算系统 ( NCS ) 是网络计算架构 (NCA) 的实现。它是在 20世纪70年代末、80年代初由阿波罗电脑公司 (Apollo Computer, Inc.) 创建的。它包含一组用于实现分布式软件应用程序或分布式计算的工具(惠普——阿波罗)
  • 安德鲁文件系统(英语:Andrew File System,缩写为AFS),一种分布式文件系统,由卡内基美隆大学开发的文件系统(卡内基美隆大学)
  • Kerberos(/ˈkərbərəs/)是一种计算机网络授权协议,用来在非安全网络中,对个人通信以安全的手段进行身份认证,可以用于实现操作系统的登录、认证功能。(麻省理工学院)

OSF 牵头厂商共同制订了“分布式运算环境”(Distributed Computing Environment,DCE)的分布式技术体系。包括但不限于:

  • 远程服务调用规范RPC(Remote Procedure Call)
  • 分布式文件系统规范DFS(Distributed File System)
  • 服务认证规范
  • 时间服务
  • 命名与目录服务
  • 通用唯一识别符UUID
  • 。。。

分布式需要考虑的问题:

  • 远程的服务在哪里(服务发现)
  • 有多少个(负载均衡)
  • 网络出现分区、超时或者服务出错了怎么办(熔断、隔离、降级)
  • 方法的参数与返回结果如何表示(序列化协议)
  • 如何传输(传输协议)
  • 服务权限如何管理(认证、授权)
  • 如何保证通信安全(网络安全层)
  • 如何令调用不同机器的服务能返回相同的结果(分布式数据一致性)

远程调用不能使用内联等传统编译原理中的优化算法提升程序的运行速度。分布式与单体应用的差异不仅仅体现在架构形式上,还会直接影响编码逻辑、部署方式、运行效率以及迭代方式。单体应用是绘图,而分布式是拼图;前者注重整体轮廓,后者需要着眼于拼图的契合。

二、单体架构——轻业务的首选项

单体可以说是是程序本身,这个概念也是在微服务出现之后才被提出的。单体相对于分布式来说,不仅易于开发、易于测试、易于部署,而且因为各个功能、模块、方法的调用过程,都是在进程内调用的,不会发生进程间通讯

进程间通讯:Inter-Process Communication,RPC是IRC的一种,即是远程过程调用(Remote Procedure Call)的缩写形式。

单体的拆分

但是,单体并不意味着不可分。在纵向上,单体可以进行分层;在横向上,单体也可以按照功能模块进行拆分。

分层架构(Layered Architecture)已经是现在几乎所有的信息系统建设中,都普遍认可、普遍采用的软件设计方法了。对于单体架构来说,在这个意义上的“可拆分”,单体其实完全不会展露出丝毫的弱势,反而还可能因为更容易开发、部署、测试而更加便捷。

如下所示 image.png

在横向角度的“可拆分”上,单体架构也可以支持按照技术、功能、职责等角度,把软件拆分为各种模块,以便重用和团队管理;亦或是添加单体的副本,通过中间件进行负载均衡,从而达到分流的目的。

非独立

单体可以按功能拆分成不同的jar包,但是最终还是运行在一个程序上,并且共享相同的内存空间;会存在隔离与自治能力上的欠缺。

  • 隔离能力的缺失

一旦架构中出现了内存泄漏、线程爆炸、阻塞、死循环等问题,就都将会影响到整个程序的运行,而不仅仅是某一个功能、模块本身的正常运作;而如果消耗的是某些更高层次的公共资源,比如端口占用过多或者数据库连接池泄漏,还将会波及到整台机器,甚至是集群中其他单体副本的正常工作。

  • 无法动态更新

此外,同样是因为所有代码都共享着同一个进程空间,如果代码无法隔离,那也就意味着,我们无法做到单独停止、更新、升级某一部分代码。因此,单体无法动态更新(可使用 OSGi 运行时模块化框架动态更新,用法复杂)。

  • 难以技术异构

技术异构:意思是说允许系统的每个模块,自由选择不一样的程序语言、不一样的编程框架等技术栈去实现。单体系统的技术栈异构不是一定做不到,比如 JNI 就可以让 Java 混用 C/C++,但是这也是很麻烦的事,是迫不得已下的选择。

  • 难以兼容“Phoenix”架构

“Phoenix”架构:周志明老师提出的凤凰架构,按我本人的理解是 程序一定是有缺陷的,而兼容凤凰架构的程序拥有“新陈代谢”的能力,对于崩溃的程序可以进行隔离与祛除。

三、SOA架构

大型单体的拆分之路演变出了许多的架构模式,直到SOA架构的诞生。

服务拆分的经典架构模式

  • 烟囱式架构

信息烟囱也被叫做信息孤岛(Information Island),使用这种架构的系统呢,也被称为孤岛式信息系统或者烟囱式信息系统。这种信息系统,完全不会跟其他相关的信息系统之间进行互操作,或者是进行协调工作

image.png

  • 微内核架构(Microkernel Architecture)

微内核架构会把这些主数据,连同其他可能被各个子系统使用到的公共服务、数据、资源,都集中到一块,成为一个被所有业务系统共同依赖的核心系统(Kernel,也称为 Core System);而具体的业务系统就能以插件模块(Plug-in Modules)的形式存在。但是微内核架构也有它的局限和使用前提,它会假设系统中各个插件模块之间是互不认识的(不可预知系统会安装哪些模块),这些插件会访问内核中一些公共的资源,但不会发生直接交互;即子插件相互间无法进行通信,只能与共享内核交互

image.png

  • 事件驱动架构(Event-Driven Architecture)

事件驱动架构会在子系统之间建立一套事件队列管道(Event Queues),来自系统外部的消息将以事件的形式发送到管道中,各个子系统可以从管道里获取自己感兴趣、可以处理的事件消息,也可以为事件新增或者是修改其中的附加信息,甚至还可以自己发布一些新的事件到管道队列中去。这种架构高度解耦,灵活交互。

image.png

SOA 架构时代的探索

SOA 的概念最早是由 Gartner 公司在 1994 年提出的。当时的 SOA 还不具备发展的条件,直到 2006 年情况才有所变化,IBM、Oracle、SAP 等公司,共同成立了 OSOA 联盟(Open Service Oriented Architecture),来联合制定和推进 SOA 相关行业标准。

当软件架构发展至 SOA 时代的时候,其中的许多概念、思想都已经能在今天的微服务中,找到对应的身影了。比如说,服务之间的松散耦合、注册、发现、治理、隔离、编排等等,都是微服务架构中耳熟能详的概念了,也大多是在分布式服务刚被提出的时候,就已经可以预见到的困难。

所以,SOA 就针对这些问题,乃至于针对“软件开发”这件事儿本身,进行了更具体更系统的探索。

更具体

尽管 SOA 本身还是属于一种抽象概念,而不是特指某一种具体的技术;不能简单地把 SOA 看作是一种架构风格了,而是可以称之为一套软件架构的基础平台

“基础平台”的体现:

  • SOA 拥有领导制定技术标准的组织 Open CSA;
  • SOA 具有清晰的软件设计的指导原则,比如服务的封装性、自治、松耦合、可重用、可组合、无状态,等等;
  • SOA 架构明确了采用 SOAP 作为远程调用的协议,依靠 SOAP 协议族(WSDL、UDDI 和一大票 WS-* 协议)来完成服务的发布、发现和治理;
  • SOA 架构会利用一个被称为是企业服务总线(Enterprise Service Bus,ESB)的消息管道,来实现各个子系统之间的通讯交互,这就让各个服务间在 ESB 的调度下,不需要相互依赖就可以实现相互通讯,既带来了服务松耦合的好处,也为以后可以进一步实现业务流程编排(Business Process Management,BPM)提供了基础;
  • SOA 架构使用了服务数据对象(Service Data Object,SDO)来访问和表示数据,使用服务组件架构(Service Component Architecture,SCA)来定义服务封装的形式和服务运行的容器;

更系统

“更系统”,指的是 SOA 的宏大理想。因为 SOA 最根本的目标,就是希望能够总结出一套自上而下的软件研发方法论,让企业只需要跟着它的思路,就能够一揽子解决掉软件开发过程中的全套问题。比如,如何挖掘需求、如何将需求分解为业务能力、如何编排已有服务、如何开发测试部署新的功能,等等;即总结出一套系统开发的八股文编写指南

SOA 最终没有获得成功的致命伤,其实跟当年的EJB(Enterprise JavaBean,企业级 JavaBean)的失败如出一辙。过于严格的体系使开发者难以适应、难以快速迭代;开发者更期望的是有一套无为而治、轻量的开发体系。

四、微服务

“Micro-Web-Service”的初始含义,指的是一种专注于单一职责的、与语言无关的、细粒度的 Web 服务(Granular Web Services)。这一阶段的微服务,是作为 SOA 的一种轻量化的补救方案而被提出来的。

2012 年,在波兰克拉科夫举行的“33rd Degree Conference”大会上,Thoughtworks 首席咨询师 James Lewis 做了题为《Microservices - Java, the Unix Way》的主题演讲。其中,他提到了单一服务职责、康威定律、自动扩展、领域驱动设计等原则,却只字未提 SOA,反而号召大家,应该重拾 Unix 的设计哲学(As Well Behaved Unix Services)。微服务已经迫不及待地要脱离 SOA 的附庸,想要成为一种独立的架构风格,也许,它还将会是 SOA 的革命者,找到一条能被广大开发者普遍接受且愿意接受的、实现服务化系统的目标。

现代微服务的理念

来自Martin Fowler 和 James Lewis 合写的文章《“Microservices: a definition of this new architectural term》提出:

微服务是一种通过多个小型服务的组合,来构建单个应用的架构风格,这些服务会围绕业务能力而非特定的技术标准来构建。各个服务可以采用不同的编程语言、不同的数据存储技术、运行在不同的进程之中。服务会采取轻量级的通讯机制和自动化的部署机制,来实现通讯与运维。

核心思想如下:

  • 围绕业务能力构建开发团队架构决定产品架构】。这个核心技术特征,实际上再次强调了康威定律的重要性。它的意思是,有怎样的结构、规模和能力的团队,就会产生出对应结构、规模、能力的产品。这个结论不是某个团队、某个公司遇到的巧合,而是必然的演化结果。

  • 分散治理(Decentralized Governance)。强调的是,在确实有必要进行技术异构的时候,一个开发团队应该能有选择“不统一”的权利。

  • 通过服务来实现独立自治的组件【通过远程调用添加隔离与自治能力】(Componentization via Services)。之所以强调要通过“服务”(Service)而不是“类库”(Library)来构建组件,是因为类库是在编译期静态链接到程序中的,会通过本地调用来提供功能,而服务是进程外组件,它是通过远程调用来提供功能的。

  • 产品化思维(Products not Projects)。避免把软件研发看作是要去完成某种功能,而要把它当做是一种持续改进、提升的过程。

  • 数据去中心化(Decentralized Data Management)【分布式数据】。微服务这种架构模式也明确地提倡,数据应该按领域来分散管理、更新、维护和存储。尽管在分布式中,我们要想处理好一致性的问题也很困难,很多时候都没法使用传统的事务处理来保证不出现一致性问题。但是两害相权取其轻,一致性问题这些必要的代价是值得付出的。

  • 轻量级通讯机制【RESTful】(Smart Endpoints and Dumb Pipes)。这个弱管道(Dumb Pipes)机制,可以说几乎算是在直接指名道姓地反对 ESB、BPM 和 SOAP 等复杂的通讯机制。如果服务需要上面的某一种功能或能力,那就应该在服务自己的 Endpoint(端点)上解决,而不是在通讯管道上一揽子处理。 微服务提倡的是类似于经典 Unix 过滤器那样,简单直接的通讯方式。比如说,RESTful 风格的通讯,在微服务中就是比较适合的。

  • 容错性设计(Design for Failure)【可靠系统完全可以由容许出错的服务来组成】。容错性设计,是指软件架构不再虚幻地追求服务永远稳定,而是接受服务总会出错的现实。“断路器”这类设施,对实际生产环境的微服务来说,并不是可选的外围组件,而是一个必须的支撑点。

  • 演进式设计(Evolutionary Design)【承认服务会被报废淘汰

  • 基础设施自动化(Infrastructure Automation)。基础设施自动化,如 CI/CD 的长足发展,大大降低了构建、发布、运维工作的复杂性。

与SOA的区别

“Microservices: a definition of this new architectural term”一文中,对微服务特征的描写已经非常具体了,除定义了微服务是什么,还专门申明了微服务不是什么:微服务不是 SOA 的衍生品,应该明确地与 SOA 划清界线,不再贴上任何 SOA 的标签。

微服务追求的是更加自由的架构风格,它摒弃了 SOA 中几乎所有可以抛弃的约束和规定,提倡以“实践标准”代替“规范标准”。

但是没有了统一的规范和约束,以前 SOA 解决的那些分布式服务的问题,需要重新进行处理。服务的注册发现、跟踪治理、负载均衡、故障隔离、认证授权、伸缩扩展、传输通讯、事务处理等问题,在微服务中,都不再会有统一的解决方案。

微服务架构下,我们需要解决什么问题,就引入什么工具;团队熟悉什么技术,就使用什么框架。

五、后微服务时代:跨越软件与硬件之间的界限

在微服务架构中,会面临一些必须解决的问题,比如注册发现、跟踪治理、负载均衡、传输通讯等。但这些问题,其实在 SOA 时代甚至可以说自原始分布式时代,就一直存在了。

  • SOA是集成了一整套的解决方案,要使用这套方案的前提是接受复杂的规定、启用不需要的组件;
  • 微服务在SOA之后,抛弃了这些附加的方案,选择回归业务本省;但是需要开发者选择合适的技术解决对应的问题,极度考验开发人员的个人能力。

在微服务时代,我们之所以不得不在应用服务层面,而不是基础设施层面去解决这些分布式问题,完全是因为由硬件构成的基础设施,跟不上由软件构成的应用服务的灵活性

在微服务兴起的时代,依托于Docker 为代表的早期容器化技术提供的虚拟化技术容器化技术,在基础架构层面解决了部分麻烦,无需通过技术进行处理。

操作系统层虚拟化(英语:Operating system–level virtualization),亦称容器化(英语:Containerization),是一种虚拟化技术,这种技术将操作系统内核虚拟化,可以允许用户空间软件实例(instances)被分割成几个独立的单元,在内核中运行,而不是只有一个单一实例运行。这个软件实例,也被称为是一个容器(containers),虚拟引擎(Virtualization engine),虚拟专用服务器(virtual private servers)或是 jails。对每个行程的拥有者与用户来说,他们使用的服务器程序,看起来就像是自己专用的。

在计算机技术中,虚拟化(技术)或虚拟技术(英语:Virtualization)是一种资源管理技术,是将计算机的各种实体资源(CPU、内存、磁盘空间、网络适配器等),予以抽象、转换后呈现出来并可供分割、组合为一个或多个电脑配置环境。由此,打破实体结构间的不可切割的障碍,使用户可以比原本的配置更好的方式来应用这些电脑硬件资源。这些资源的新虚拟部分是不受现有资源的架设方式,地域或物理配置所限制。一般所指的虚拟化资源包括计算能力和资料存储。

2017 年,可以说是容器生态发展历史中具有里程碑意义的一年,Kubernetes 赢得容器战争的胜利。

Kubnernetes 拥有容器编排的能力,很好地定义容器的“组织方式”和“管理规范”,可以支撑起生产级大规模容器化部署的要求;而Docker只是单一容器技术,并没有容器管理的能力。Kubernetes 中提供的基础设施层面的解决方案:

image.png

当虚拟化的基础设施,开始从单个服务的容器发展到由多个容器构成的服务集群,以及集群所需的所有通讯、存储设施 的时候,软件与硬件的界限就开始模糊了。

一旦硬件能够跟得上软件的灵活性,那么这些与业务无关的技术问题,便很可能从软件的层面剥离出来,在硬件的基础设施之内就被悄悄解决掉,让软件可以只专注于业务,真正“围绕业务能力构建”团队与产品。

至此,“云原生”时代到来。

在云原生时代追求的目标,跟此前微服务时代中追求的目标相比,并没有什么本质的改变,它们都是通过一系列小型服务去构建大型系统。在服务架构演进的历史进程中,我更愿意把“云原生时代”称为“后微服务时代”。

服务网格

此时,Kubernetes 其实并没有完美地解决全部的分布式问题。

这里所说的“不完美”的意思是,仅从功能灵活强大这点来看,Kubernetes 反而还不如之前的 Spring Cloud 方案。这是因为有一些问题处于应用系统与基础设施的边缘,我们很难能完全在基础设施的层面中,去精细化地解决掉它们。

例如,B微服务提供b1、b2两个接口,当b1不可用时依旧会影响b2的使用。

image.png

为了解决这一类问题,微服务基础设施很快就进行了第二次进化,引入在今天被我们叫做是“服务网格”(Service Mesh)的“边车代理模式”【劫持访问】(Sidecar Proxy)。

“边车”的意思是,微服务基础设施会由系统自动地在服务的资源容器(指 Kubernetes 的 Pod)中注入一个通讯代理服务器(相当于那个挎斗),用类似网络安全里中间人攻击的方式进行流量劫持,在应用毫无感知的情况下,悄悄接管掉应用的所有对外通讯。

这个代理除了会实现正常的服务调用以外(称为数据平面通讯),同时还接受来自控制器的指令(称为控制平面通讯),根据控制平面中的配置,分析数据平面通讯的内容,以实现熔断、认证、度量、监控、负载均衡等各种附加功能。

这样,就实现了既不需要在应用层面附带额外的代码,也提供了几乎不亚于应用代码的精细管理能力的目的。

image.png

六、前瞻:无服务架构

“无服务”只涉及了后端设施(Backend)和函数(Function)两块内容:

  • 后端设施是指数据库、消息队列、日志、存储等这一类用于支撑业务逻辑运行,但本身无业务含义的技术组件。这些后端设施都运行在云中,也就是无服务中的“后端即服务”(Backend as a Service,BaaS)。

  • 函数指的就是业务逻辑代码。这里函数的概念与粒度,都已经和程序编码角度的函数非常接近了,区别就在于,无服务中的函数运行在云端,不必考虑算力问题和容量规划(从技术角度可以不考虑,但从计费的角度来看,你还是要掂量一下自己的钱包够不够用),也就是无服务中的“函数即服务”(Function as a Service,FaaS)。

无服务的愿景是让开发者只需要纯粹地关注业务

无服务架构天生的一些特点,比如冷启动、 无状态、运行时间有限制等等,决定了它不是一种具有普适性的架构模式。

  • 冷启动:启动需要一定时间;不适合响应速度要求较高、需要长连接等特征的应用
  • 无状态:按需启动,避免流量损耗;不适合业务逻辑复杂、依赖服务端状态

微服务架构是分布式系统这条路当前所能做到的极致,那无服务架构,也许就是“不分布式”的云端系统这条路的起点。微服务与无服务可以是相辅相成的。

七、来源

周志明的软件架构课