[Java] 服务架构演进总结 -- 读《The Fenix Project》
书籍地址:软件架构探索:The Fenix Project | 软件架构探索:The Fenix Project (icyfenix.cn)
一、原始分布式时代
UNIX的分布式设计哲学
Simplicity of both the interface and the implementation are more important than any other attributes of the system — including correctness, consistency, and completeness
保持接口与实现的简单性,比系统的任何其他属性,包括准确性、一致性和完整性,都来得更加重要。
历史上的原因
在20世纪70年代末期到80年代初,计算机科学刚经历了从以大型机为主向以微型机为主的蜕变,计算机逐渐从一种存在于研究机构、实验室当中的科研设备,转变为存在于商业企业中的生产设备,甚至是面向家庭、个人用户的娱乐设备。此时的微型计算机系统通常具有16位寻址能力、不足5MHz时钟频率的处理器和128KB左右的内存地址空间。譬如著名的英特尔处理器的鼻祖,Intel 8086处理器就是在1978年研制成功,流行于80年代中期,甚至一直持续到90年代初期仍有生产销售。
当时计算机硬件局促的运算处理能力,已直接妨碍到了在单台计算机上信息系统软件能够达到的最大规模。为突破硬件算力的限制,各个高校、研究机构、软硬件厂商开始分头探索,寻找使用多台计算机共同协作来支撑同一套软件系统运行的可行方案。
成果
- 惠普公司(及后来被惠普收购的Apollo)提出的网络运算架构(Network Computing Architecture,NCA)是未来远程服务调用的雏形;
- 卡内基·梅隆大学提出的AFS文件系统(Andrew File System)是日后分布式文件系统的最早实现(Andrew意为纪念Andrew Carnegie和Andrew Mellon);
- 麻省理工学院提出的Kerberos协议是服务认证和访问控制的基础性协议,是分布式服务安全性的重要支撑,目前仍被用于实现包括Windows和MacOS在内众多操作系统的登录、认证功能
失败的结果
DCE的研究是计算机科学中第一次对分布式有组织领导、有标准可循、有巨大投入的尝试,但无论是DCE还是稍后出现的CORBA,从结果来看,都不能称取得了成功,将一个系统拆分到不同的机器中运行,这样做带来的服务发现、跟踪、通信、容错、隔离、配置、传输、数据一致性和编码复杂度等方面的问题,所付出的代价远远超过了分布式所取得的收益。亲身经历过那个年代的计算机科学家、IBM院士Kyle Brown事后曾评价道:“这次尝试最大的收获就是对RPC、DFS等概念的开创,以及得到了一个价值千金的教训:某个功能能够进行分布式,并不意味着它就应该进行分布式,强行追求透明的分布式操作,只会自寻苦果。”
原始分布式时代的教训
Just because something can be distributed doesn’t mean it should be distributed. Trying to make a distributed call act like a local call always ends in tears
某个功能能够进行分布式,并不意味着它就应该进行分布式,强行追求透明的分布式操作,只会自寻苦果
—— Kyle Brown,IBM Fellow,Beyond Buzzwords: A Brief History of Microservices Patterns,2016
二、单体系统时代
单体架构(Monolithic)
“单体”只是表明系统中主要的过程调用都是进程内调用,不会发生进程间通信,仅此而已。
命名来源
“单体架构”在整个软件架构演进的历史进程里,是出现时间最早、应用范围最广、使用人数最多、统治历史最长的一种架构风格,但“单体”这个名称,却是在微服务开始流行之后才“事后追认”所形成的概念。此前,并没有多少人将“单体”视作一种架构来看待,如果你去查找软件架构的开发资料,可以轻而易举地找出大量以微服务为主题的书籍和文章,却很难找出专门教你如何开发单体系统的任何形式的材料,这一方面体现了单体架构本身的简单性,另一方面,也体现出在相当长的时间尺度里,大家都已经习惯了软件架构就应该是单体这种样子。
单体架构的使用场景
对于小型系统——即由单台机器就足以支撑其良好运行的系统,单体不仅易于开发、易于测试、易于部署,且由于系统中各个功能、模块、方法的调用过程都是进程内调用,不会发生进程间通信(Inter-Process Communication,IPC。广义上讲,可以认为RPC属于IPC的一种特例,但请注意这里两个“PC”不是同个单词的缩写),因此也是运行效率最高的一种架构风格,完全不应该被贴上“反派角色”的标签,反倒是那些爱赶技术潮流却不顾需求现状的微服务吹捧者更像是个反派。
Monolithic Application
Monolith means composed all in one piece. The Monolithic application describes a single-tiered software application in which different components combined into a single program from a single platform.
单体意味着自包含。单体应用描述了一种由同一技术平台的不同组件构成的单层软件。
—— Monolithic Application,Wikipedia
关于超市架构选用的例子
共享同一进程获得简单、高效的代价是同时损失了各个功能模块的自治、隔离能力
沃尔玛将超市分为仓储部、采购部、安保部、库存管理部、巡检部、质量管理部、市场营销部等,可以划清职责,明确边界,让管理能力能支持企业的成长规模;但如果你家楼下开的小卖部,爸、妈加儿子,再算上看家的中华田园犬小黄一共也就只有四名员工,再去追求“先进管理”,划分仓储部、采购部、库存管理部……那纯粹是给自己找麻烦。单体架构下,哪怕是信息系统中两个相互毫无关联的子系统,也依然会部署在同一个进程中。当系统规模小的时候,这是优势,但当系统规模大,或程序需要修改的时候,其部署的成本、技术升级的迁移成本都会变得更为昂贵。继续以前面的例子来比喻,当公司小时,让安保部和质检部两个不相干的部门在同一栋大楼中办公是节约资源,但当公司人数增加,办公室已经拥挤不堪,最多只能在楼顶加盖新楼层(相当于增强硬件性能)来解决办公问题,而不能让安保、质检分开地方办公,这便是缺陷所在。
分层架构
从纵向角度来看,笔者(周志明老师)从未见过实际生产环境里有哪个大型的现代信息系统是完全不分层的。分层架构(Layered Architecture)已是现在几乎所有信息系统建设中都普遍认可、采用的软件设计方法,无论是单体还是微服务,抑或是其他架构风格,都会对代码进行纵向层次划分,收到的外部请求在各层之间以不同形式的数据结构进行流转传递,触及最末端的数据库后按相反的顺序回馈响应,如下图所示。对于这个意义上的“可拆分”,单体架构完全不会展露出丝毫的弱势,反而可能会因更容易开发、部署、测试而获得一些便捷性上的好处。
单体架构的缺陷
- 拆分困难:拆分之后的隔离与自治能力上的欠缺。由于所有代码都运行在同一个进程空间之内,所有模块、方法的调用都无须考虑网络分区、对象复制这些麻烦的事和性能损失。获得了进程内调用的简单、高效等好处的同时,也意味着如果任何一部分代码出现了缺陷,过度消耗了进程空间内的资源,所造成的影响也是全局性的、难以隔离的。譬如内存泄漏、线程爆炸、阻塞、死循环等问题,都将会影响整个程序,而不仅仅是影响某一个功能、模块本身的正常运作。如果消耗的是某些更高层次的公共资源,譬如端口号或者数据库连接池泄漏,影响还将会波及整台机器,甚至是集群中其他单体副本的正常工作。
- 维护的困难:由于所有代码都共享着同一个进程空间,不能隔离,也就无法(其实还是有办法的,譬如使用OSGi这种运行时模块化框架,但是很别扭、很复杂)做到单独停止、更新、升级某一部分代码,因为不可能有“停掉半个进程,重启1/4个程序”这样不合逻辑的操作,所以从可维护性来说,单体系统也是不占优势的。程序升级、修改缺陷往往需要制定专门的停机更新计划,做灰度发布、A/B测试也相对更复杂。
- 技术异构的困难:每个模块的代码都通常需要使用一样的程序语言,乃至一样的编程框架去开发。单体系统的技术栈异构并非一定做不到,譬如JNI就可以让Java混用C或C++,但这通常是迫不得已的,并不是优雅的选择。
- 没有容错能力(根本原因):单体靠高质量来保证高可靠性的思路,在小规模软件上还能运作良好,但系统规模越大,交付一个可靠的单体系统就变得越来越具有挑战性。正是随着软件架构演进,构筑可靠系统从“追求尽量不出错”,到正视“出错是必然”的观念转变,才是微服务架构得以挑战并逐步开始取代运作了数十年的单体架构的底气所在。
三、SOA时代
SOA架构(Service-Oriented Architecture)
面向服务的架构是一次具体地、系统性地成功解决分布式服务主要问题的架构模式。
拆分服务的三种架构模式
历史上对于大型单体系统进行拆分,有以下三种代表性的架构模式
- 烟囱式架构(Information Silo Architecture):信息烟囱又名信息孤岛(Information Island),使用这种架构的系统也被称为孤岛式信息系统或者烟囱式信息系统。它指的是一种完全不与其他相关信息系统进行互操作或者协调工作的设计模式。这样的系统其实并没有什么“架构设计”可言。接着上一节中企业与部门的例子来说,如果两个部门真的完全不会发生任何交互,就并没有什么理由强迫它们必须在一栋楼里办公;两个不发生交互的信息系统,让它们使用独立的数据库和服务器即可实现拆分,而唯一的问题,也是致命的问题是,企业中真的存在完全不发生交互的部门吗?对于两个信息系统来说,哪怕真的毫无业务往来关系,但系统的人员、组织、权限等主数据,会是完全独立、没有任何重叠的吗?这样“独立拆分”“老死不相往来”的系统,显然不可能是企业所希望见到的。
- 微内核架构(Microkernel Architecture):微内核架构也被称为插件式架构(Plug-in Architecture)。既然在烟囱式架构中,没有业务往来关系的系统也可能需要共享人员、组织、权限等一些的公共的主数据,那不妨就将这些主数据,连同其他可能被各子系统使用到的公共服务、数据、资源集中到一块,成为一个被所有业务系统共同依赖的核心(Kernel,也称为Core System),具体的业务系统以插件模块(Plug-in Modules)的形式存在,这样也可提供可扩展的、灵活的、天然隔离的功能特性,即微内核架构。
- 事件驱动架构(Event-Driven Architecture):为了能让子系统互相通信,一种可行的方案是在子系统之间建立一套事件队列管道(Event Queues),来自系统外部的消息将以事件的形式发送至管道中,各个子系统从管道里获取自己感兴趣、能够处理的事件消息,也可以为事件新增或者修改其中的附加信息,甚至可以自己发布一些新的事件到管道队列中去,如此,每一个消息的处理者都是独立的,高度解耦的,但又能与其他处理者(如果存在该消息处理者的话)通过事件管道进行互动。
SOA架构
软件架构来到SOA时代,许多概念、思想都已经能在今天微服务中找到对应的身影了,譬如服务之间的松散耦合、注册、发现、治理,隔离、编排,等等。这些在今天微服务中耳熟能详的名词概念,大多数也是在分布式服务刚被提出时就已经可以预见的困难点。SOA针对这些问题,甚至是针对“软件开发”这件事情本身,都进行了更加系统性、更加具体的探索。
具有以下严谨的规定或者说特点
- 可以称为一套软件设计的基础平台
- 拥有领导制定技术标准的组织Open CSA
- 有清晰软件设计的指导原则,譬如服务的封装性、自治、松耦合、可重用、可组合、无状态,等等
- 明确了采用SOAP作为远程调用的协议,依靠SOAP协议族(WSDL、UDDI和一大票WS-*协议)来完成服务的发布、发现和治理
- 利用一个被称为企业服务总线(Enterprise Service Bus,ESB)的消息管道来实现各个子系统之间的通信交互,令各服务间在ESB调度下无须相互依赖却能相互通信,既带来了服务松耦合的好处,也为以后可以进一步实施业务流程编排(Business Process Management,BPM)提供了基础
- 使用服务数据对象(Service Data Object,SDO)来访问和表示数据,使用服务组件架构(Service Component Architecture,SCA)来定义服务封装的形式和服务运行的容器,等等
SOA架构的缺陷
农村包围城市,武装夺取政权。
过于严格的规范定义带来过度的复杂性,而构建在SOAP基础之上的ESB、BPM、SCA、SDO等诸多上层建筑,进一步加剧了这种复杂性。开发信息系统毕竟不是作八股文章,过于精密的流程和理论也需要懂得复杂概念的专业人员才能够驾驭。SOA诞生的那一天起,就已经注定了它只能是少数系统阳春白雪式的精致奢侈品,它可以实现多个异构大型系统之间的复杂集成交互,却很难作为一种具有广泛普适性的软件架构风格来推广。SOA最终没有获得成功的致命伤与当年的EJB如出一辙,尽管有Sun Microsystems和IBM等一众巨头在背后力挺,EJB仍然败于以Spring、Hibernate为代表的“草根框架”,可见一旦脱离人民群众,终究会淹没在群众的海洋之中,连信息技术也不曾例外过。
四、微服务时代
微服务架构(Microservices)
微服务是一种通过多个小型服务组合来构建单个应用的架构风格,这些服务围绕业务能力而非特定的技术标准来构建。各个服务可以采用不同的编程语言,不同的数据存储技术,运行在不同的进程之中。服务采取轻量级的通信机制和自动化的部署机制实现通信与运维。
起源
“微服务”这个技术名词最早在2005年就已经被提出,它是由Peter Rodgers博士在2005年度的云计算博览会(Web Services Edge 2005)上首次使用,当时的说法是“Micro-Web-Service”,指的是一种专注于单一职责的、语言无关的、细粒度Web服务(Granular Web Services)。“微服务”一词并不是Peter Rodgers直接凭空创造出来的概念,最初的微服务可以说是SOA发展时催生的产物,就如同EJB推广过程中催生了Spring和Hibernate那样,这一阶段的微服务是作为一种SOA的轻量化的补救方案而被提出的。时至今日,在英文版的维基百科上,仍然将微服务定义为一种SOA的变种形式,所以微服务在最初阶段与SOA、Web Service这些概念有所牵扯也完全可以理解,但现在来看,维基百科对微服务的定义已经颇有些过时了。
What is microservices
Microservices is a software development technique — a variant of the service-oriented architecture (SOA) structural style.
微服务是一种软件开发技术,是一种SOA的变体形式。
—— Wikipedia,Microservices
Microservices
微服务真正的崛起是在2014年,相信阅读此文的大多数读者,也是从Martin Fowler与James Lewis合写的文章《Microservices: A Definition of This New Architectural Term》中首次了解到微服务的。今天大家所了解的“微服务”是这篇文章中定义的“微服务”。在此文中,首先给出了现代微服务的概念:“微服务是一种通过多个小型服务组合来构建单个应用的架构风格,这些服务围绕业务能力而非特定的技术标准来构建。各个服务可以采用不同的编程语言,不同的数据存储技术,运行在不同的进程之中。服务采取轻量级的通信机制和自动化的部署机制实现通信与运维。”此外,文中列举了微服务的九个核心的业务与技术特征,下面将其一一列出并解读。
- 围绕业务能力构建(Organized around Business Capability)。这里再次强调了康威定律的重要性,有怎样结构、规模、能力的团队,就会产生出对应结构、规模、能力的产品。这个结论不是某个团队、某个公司遇到的巧合,而是必然的演化结果。如果本应该归属同一个产品内的功能被划分在不同团队中,必然会产生大量的跨团队沟通协作,跨越团队边界无论在管理、沟通、工作安排上都有更高昂的成本,高效的团队自然会针对其进行改进,当团队、产品磨合调节稳定之后,团队与产品就会拥有一致的结构。
- 分散治理(Decentralized Governance)。这是要表达“谁家孩子谁来管”的意思,服务对应的开发团队有直接对服务运行质量负责的责任,也应该有着不受外界干预地掌控服务各个方面的权力,譬如选择与其他服务异构的技术来实现自己的服务。这一点在真正实践时多少存有宽松的处理余地,大多数公司都不会在某一个服务使用Java,另一个用Python,下一个用Golang,而是通常会有统一的主流语言,乃至统一的技术栈或专有的技术平台。微服务不提倡也并不反对这种“统一”,只要负责提供和维护基础技术栈的团队,有被各方依赖的觉悟,要有“经常被凌晨3点的闹钟吵醒”的心理准备就好。微服务更加强调的是确实有必要技术异构时,应能够有选择“不统一”的权利,譬如不应该强迫Node.js去开发报表页面,要做人工智能训练模型时,应该可以选择Python,等等。
- 通过服务来实现独立自治的组件(Componentization via Services)。之所以强调通过“服务”(Service)而不是“类库”(Library)来构建组件,是因为类库在编译期静态链接到程序中,通过本地调用来提供功能,而服务是进程外组件,通过远程调用来提供功能。前面的文章里我们已经分析过,尽管远程服务有更高昂的调用成本,但这是为组件带来隔离与自治能力的必要代价。
- 产品化思维(Products not Projects)。避免把软件研发视作要去完成某种功能,而是视作一种持续改进、提升的过程。譬如,不应该把运维只看作运维团队的事,把开发只看作开发团队的事,团队应该为软件产品的整个生命周期负责,开发者不仅应该知道软件如何开发,还应该知道它如何运作,用户如何反馈,乃至售后支持工作是怎样进行的。注意,这里服务的用户不一定是最终用户,也可能是消费这个服务的另外一个服务。以前在单体架构下,程序的规模决定了无法让全部人员都关注完整的产品,组织中会有开发、运维、支持等细致的分工的成员,各人只关注于自己的一块工作,但在微服务下,要求开发团队中每个人都具有产品化思维,关心整个产品的全部方面是具有可行性的。
- 数据去中心化(Decentralized Data Management)。微服务明确地提倡数据应该按领域分散管理、更新、维护、存储,在单体服务中,一个系统的各个功能模块通常会使用同一个数据库,诚然中心化的存储天生就更容易避免一致性问题,但是,同一个数据实体在不同服务的视角里,它的抽象形态往往也是不同的。譬如,Bookstore应用中的书本,在销售领域中关注的是价格,在仓储领域中关注的库存数量,在商品展示领域中关注的是书籍的介绍信息,如果作为中心化的存储,所有领域都必须修改和映射到同一个实体之中,这便使得不同的服务很可能会互相产生影响而丧失掉独立性。尽管在分布式中要处理好一致性的问题也相当困难,很多时候都没法使用传统的事务处理来保证,但是两害相权取其轻,有一些必要的代价仍是值得付出的。
- 强终端弱管道(Smart Endpoint and Dumb Pipe)。弱管道(Dumb Pipe)几乎算是直接指名道姓地反对SOAP和ESB的那一堆复杂的通信机制。ESB可以处理消息的编码加工、业务规则转换等;BPM可以集中编排企业业务服务;SOAP有几十个WS-*协议族在处理事务、一致性、认证授权等一系列工作,这些构筑在通信管道上的功能也许对某个系统中的某一部分服务是有必要的,但对于另外更多的服务则是强加进来的负担。如果服务需要上面的额外通信能力,就应该在服务自己的Endpoint上解决,而不是在通信管道上一揽子处理。微服务提倡类似于经典UNIX过滤器那样简单直接的通信方式,RESTful风格的通信在微服务中会是更加合适的选择。
- 容错性设计(Design for Failure)。不再虚幻地追求服务永远稳定,而是接受服务总会出错的现实,要求在微服务的设计中,有自动的机制对其依赖的服务能够进行快速故障检测,在持续出错的时候进行隔离,在服务恢复的时候重新联通。所以“断路器”这类设施,对实际生产环境的微服务来说并不是可选的外围组件,而是一个必须的支撑点,如果没有容错性的设计,系统很容易就会被因为一两个服务的崩溃所带来的雪崩效应淹没。可靠系统完全可能由会出错的服务组成,这是微服务最大的价值所在,也是这部开源文档标题“The Fenix Project”的含义。
- 演进式设计(Evolutionary Design)。容错性设计承认服务会出错,演进式设计则是承认服务会被报废淘汰。一个设计良好的服务,应该是能够报废的,而不是期望得到长存永生。假如系统中出现不可更改、无可替代的服务,这并不能说明这个服务是多么的优秀、多么的重要,反而是一种系统设计上脆弱的表现,微服务所追求的独立、自治,也是反对这种脆弱性的表现。
- 基础设施自动化(Infrastructure Automation)。基础设施自动化,如CI/CD的长足发展,显著减少了构建、发布、运维工作的复杂性。由于微服务下运维的对象比起单体架构要有数量级的增长,使用微服务的团队更加依赖于基础设施的自动化,人工是很难支撑成百上千乃至成千上万级别的服务的。
MicroService and SOA
相同点
- 对大型单体应用的拆分,都是从面向服务这个方面进行
不同点
- 微服务追求的是更加自由的架构风格,摒弃了SOA几乎可以抛弃的约定和规定
比如,服务的注册发现、跟踪治理、负载均衡、故障隔离、认证授权、伸缩扩展、传输通信、事务处理,等等,这些问题,在微服务中不再会有统一的解决方案,即使只讨论Java范围内会使用到的微服务,光一个服务间远程调用问题,可以列入解决方案的候选清单的就有:RMI(Sun/Oracle)、Thrift(Facebook)、Dubbo(阿里巴巴)、gRPC(Google)、Motan2(新浪)、Finagle(Twitter)、brpc(百度)、Arvo(Hadoop)、JSON-RPC、REST,等等;光一个服务发现问题,可以选择的就有:Eureka(Netflix)、Consul(HashiCorp)、Nacos(阿里巴巴)、ZooKeeper(Apache)、Etcd(CoreOS)、CoreDNS(CNCF),等等。其他领域的情况也是与此类似,总之,完全是八仙过海,各显神通的局面。
对于软件架构师
微服务所带来的自由是一把双刃开锋的宝剑,当软件架构者拿起这把宝剑,一刃指向SOA定下的复杂技术标准,将选择的权力夺回的同一时刻,另外一刃也正朝向着自己映出冷冷的寒光,对架构者是满满的恶意,对架构能力要求已提升到史无前例的程度,笔者(周志明老师)在这部文档的多处反复强调过,技术架构者的第一职责就是做决策权衡,有利有弊才需要决策,有取有舍才需要权衡,如果架构者本身的知识面不足以覆盖所需要决策的内容,不清楚其中利弊,恐怕也就无可避免地陷入选择困难症的困境之中。
五、后微服务时代
后微服务时代(Could Native)
从软件层面独力应对微服务架构问题,发展到软、硬一体,合力应对架构问题的时代,此即为“后微服务时代”。
从软件层面独力应对分布式架构所带来的各种问题,发展到应用代码与基础设施软、硬一体,合力应对架构问题的时代,现在常被媒体冠以“云原生”这个颇为抽象的名字加以宣传。
寻求硬件层面的解决方案
分布式架构中出现的问题,如注册发现、跟踪治理、负载均衡、传输通信等,其实在SOA时代甚至可以说从原始分布式时代起就已经存在了,只要是分布式架构的系统,就无法完全避免,但我们不妨换个思路来想一下,这些问题一定要由软件系统自己来解决吗?
如果不局限于采用软件的方式,这些问题几乎都有对应的硬件解决方案。譬如,某个系统需要伸缩扩容,通常会购买新的服务器,部署若干副本实例来分担压力;如果某个系统需要解决负载均衡问题,通常会布置负载均衡器,选择恰当的均衡算法来分流;如果需要解决传输安全问题,通常会布置TLS传输链路,配置好CA证书以保证通信不被窃听篡改;如果需要解决服务发现问题,通常会设置DNS服务器,让服务访问依赖稳定的记录名而不是易变的IP地址,等等。经过计算机科学多年的发展,这些问题大多有了专职化的基础设施去解决,而之所以微服务时代,人们选择在软件的代码层面而不是硬件的基础设施层面去解决这些分布式问题,很大程度上是因为由硬件构成的基础设施,跟不上由软件构成的应用服务的灵活性的无奈之举。软件可以只使用键盘命令就能拆分出不同的服务,只通过拷贝、启动就能够伸缩扩容服务,硬件难道就不可以通过敲键盘就变出相应的应用服务器、负载均衡器、DNS服务器、网络链路这些设施吗?
虚拟化容器技术
尽管2014年微服务开始崛起的时候,Docker Swarm(2013年)和Apache Mesos(2012年)就已经存在,更早之前也出现了软件定义网络(Software-Defined Networking,SDN)、软件定义存储(Software-Defined Storage,SDS)等技术,但是,被业界广泛认可、普遍采用的通过虚拟化基础设施去解决分布式架构问题的开端,应该要从2017年Kubernetes赢得容器战争的胜利开始算起。以Docker Swarm、Apache Mesos与Kubernetes为主要竞争者的“容器编排战争”终于有了明确的结果,Kubernetes登基加冕是容器发展中一个时代的终章,也将是软件架构发展下一个纪元的开端。笔者(周志明老师)在表中列出了在同一个分布式服务的问题在传统Spring Cloud中提供的应用层面的解决方案与在Kubernetes中提供的基础设施层面的解决方案,尽管因为各自出发点不同,解决问题的方法和效果都有所差异,但这无疑是提供了一条全新的、前途更加广阔的解题思路。
Kubernetes | Spring Cloud | |
---|---|---|
弹性伸缩 | Autoscaling | N/A |
服务发现 | KubeDNS / CoreDNS | Spring Cloud Eureka |
配置中心 | ConfigMap / Secret | Spring Cloud Config |
服务网关 | Ingress Controller | Spring Cloud Zuul |
负载均衡 | Load Balancer | Spring Cloud Ribbon |
服务安全 | RBAC API | Spring Cloud Security |
跟踪监控 | Metrics API / Dashboard | Spring Cloud Turbine |
降级熔断 | N/A | Spring Cloud Hystrix |
边车代理模式(服务网格)
kubernetes的缺陷
仅从功能上看,单纯的Kubernetes反而不如之前的Spring Cloud方案。这是因为有一些问题处于应用系统与基础设施的边缘,使得完全在基础设施层面中确实很难精细化地处理。举个例子,微服务A调用了微服务B的两个服务,称为B1和B2,假设B1表现正常但B2出现了持续的500错,那在达到一定阈值之后就应该对B2进行熔断,以避免产生雪崩效应。如果仅在基础设施层面来处理,这会遇到一个两难问题,切断A到B的网络通路则会影响到B1的正常调用,不切断的话则持续受B2的错误影响。
基础设施是针对整个容器来管理的,粒度相对粗旷,只能到容器层面,对单个远程服务就很难有效管控。类似的情况不仅仅在断路器上出现,服务的监控、认证、授权、安全、负载均衡等都有可能面临细化管理的需求,譬如服务调用时的负载均衡,往往需要根据流量特征,调整负载均衡的层次、算法,等等,而DNS尽管能实现一定程度的负载均衡,但通常并不能满足这些额外的需求。
服务网格(service mesh)的边车代理模式(sidecar proxy)
系统自动在服务容器(通常是指Kubernetes的Pod)中注入一个通信代理服务器,相当于那个挎斗,以类似网络安全里中间人攻击的方式进行流量劫持,在应用毫无感知的情况下,悄然接管应用所有对外通信。这个代理除了实现正常的服务间通信外(称为数据平面通信),还接收来自控制器的指令(称为控制平面通信),根据控制平面中的配置,对数据平面通信的内容进行分析处理,以实现熔断、认证、度量、监控、负载均衡等各种附加功能。这样便实现了既不需要在应用层面加入额外的处理代码,也提供了几乎不亚于程序代码的精细管理能力。
很难从概念上判定清楚一个与应用系统运行于同一资源容器之内的代理服务到底应该算软件还是算基础设施,但它对应用是透明的,不需要改动任何软件代码就可以实现服务治理,这便足够了。
我司的类sidecar代理模式
类似于边车代理模式,在管控和机器之间做了一层代理,不仅仅局限于托管应用对外的所有通信,来实现熔断、认证、度量、监控、负载均衡;并且也是一个保姆级进程负责拉起机器进程和终止机器进程。
六、无服务时代
无服务架构(Serverless)
如果说微服务架构是分布式系统这条路的极致,那无服务架构,也许就是“不分布式”的云端系统这条路的起点。
起源
如果单台服务器的性能可以是无限的,那架构演进的结果肯定会与今天有很大的差别,分布式也好,容器化也好,微服务也好,恐怕都未必会如期出现,最起码不必一定是像今天这个样子。绝对意义上的无限性能必然是不存在的,但在云计算落地已有十年时间的今日,相对意义的无限性能已经成为了现实。在工业界,2012年,Iron.io公司率先提出了“无服务”(Serverless,应该翻译为“无服务器”才合适,但现在称“无服务”已形成习惯了)的概念,2014年开始,亚马逊发布了名为Lambda的商业化无服务应用,并在后续的几年里逐步得到开发者认可,发展成目前世界上最大的无服务的运行平台。
十年之后的2019年,UC Berkeley的第二篇有着相同命名风格的论文《Cloud Programming Simplified: A Berkeley View on Serverless Computing》发表,再次预言未来“无服务将会发展成为未来云计算的主要形式”,由此来看,“无服务”也同样是被主流学术界所认可的发展方向之一。
定义
无服务现在还没有一个特别权威的“官方”定义,但它的概念并没有前面各种架构那么复杂,本来无服务也是以“简单”为主要卖点的,它只涉及两块内容:后端设施(Backend)和函数(Function)。
- 后端设施是指数据库、消息队列、日志、存储,等等这一类用于支撑业务逻辑运行,但本身无业务含义的技术组件,这些后端设施都运行在云中,无服务中称其为“后端即服务”(Backend as a Service,BaaS)。
- 函数是指业务逻辑代码,这里函数的概念与粒度,都已经很接近于程序编码角度的函数了,其区别是无服务中的函数运行在云端,不必考虑算力问题,不必考虑容量规划(从技术角度可以不考虑,从计费的角度你的钱包够不够用还是要掂量一下的),无服务中称其为“函数即服务”(Function as a Service,FaaS)。
无服务的愿景是让开发者只需要纯粹地关注业务,不需要考虑技术组件,后端的技术组件是现成的,可以直接取用,没有采购、版权和选型的烦恼;不需要考虑如何部署,部署过程完全是托管到云端的,工作由云端自动完成;不需要考虑算力,有整个数据中心支撑,算力可以认为是无限的;也不需要操心运维,维护系统持续平稳运行是云计算服务商的责任而不再是开发者的责任。在UC Berkeley的论文中,把无服务架构下开发者不再关心这些技术层面的细节,类比成当年软件开发从汇编语言踏进高级语言的发展过程,开发者可以不去关注寄存器、信号、中断等与机器底层相关的细节,从而令生产力得到极大地解放。
缺陷
无服务架构的远期前景看起来是很美好的,但笔者自己对无服务中短期内的发展并没有那么乐观。与单体架构、微服务架构不同,无服务架构有一些天生的特点决定了它现在不是,以后如果没有重大变革的话,估计也很难成为一种普适性的架构模式。无服务机构对一些适合的应用确实能够降低开发和运维环节的成本,譬如不需要交互的离线大规模计算,又譬如多数Web资讯类网站、小程序、公共API服务、移动应用服务端等都契合于无服务架构所擅长的短链接、无状态、适合事件驱动的交互形式;但另一方面,对于那些信息管理系统、网络游戏等应用,又或者说所有具有业务逻辑复杂,依赖服务端状态,响应速度要求较高,需要长链接等这些特征的应用,至少目前是相对并不适合的。这是因为无服务天生“无限算力”的假设决定了它必须要按使用量(函数运算的时间和占用的内存)计费以控制消耗算力的规模,因而函数不会一直以活动状态常驻服务器,请求到了才会开始运行,这导致了函数不便依赖服务端状态,也导致了函数会有冷启动时间,响应的性能不可能太好(目前无服务的冷启动过程大概是在数十到百毫秒级别,对于Java这类启动性能差的应用,甚至能到接近秒的级别)。
无论如何,云计算毕竟是大势所趋,今天信息系统建设的概念和观念,在(较长尺度的)明天都是会转变成适应云端的,笔者并不怀疑Serverless+API的设计方式会成为以后其中一种主流的软件形式,届时无服务还会有更广阔的应用空间。
七、总结
We can only see a short distance ahead, but we can see plenty there that needs to be done.
尽管目光所及之处,只是不远的前方,即使如此,依然可以看到那里有许多值得去完成的工作在等待我们。
借历史之名,理解好每种架构出现的意义与淘汰的原因,为的是更好地解决今天的现实问题,寻找出未来架构演进的发展道路。