查看原文
其他

互联网后端架构演进及未来猜想

风弈 淘系技术 2021-11-09


当年的一个不经意间产生了一个伟大的发明——计算机,在这之上又是一个不经意间产生了彻底的改变你我生活的-互联网;这些伟大的转折点,从来不是突然之间到来,就像大海都是由一滴滴水滴慢慢汇聚而成,而我们就像水消失在水中,看似微不足道,但却都不可或缺;作为互联网技术发展的史上的一滴微不足道的水滴,让我们来回顾一下互联网后端应用的架构发展,以及对未来的一些思考和展望吧...



本文是纯粹的思考和讨论,尽可能客观的讨论相关架构。主要对后端 api 应用,以一种事后诸葛亮式的视角进行分析。


历史回顾

随着业务复杂度以及开发团队规模的不断扩大,互联网后端部署架构也随之不断演进。


注:由于作者本人水平有限,且没有亲身经历过当年的架构演进过程,可能存在理解错误或不够深入,欢迎前来一起讨论。


   单体应用架构



在早期互联网站点上,所有服务都放在一个仓库并部署在一起,且由 lb 来把整个站点的流量负载到多个 server 节点上,做到了高可用(当然,在这之前还存在完全的单点,没有lb,只有一个服务器来部署应用,扩容完全靠机器升级配置;这里不对这种架构过多阐述)。
此时的后端开发团队往往非常简单--只有一个团队来负责设计,开发,部署,运维。那么随着开发人员增加,整个流程想变得相互耦合,相互等待,生产效率变得极低(人员上的扩展在达到一定数量后将变得难以管理且沟通效率极低)。


  • 优势

  1. 运维成本低:只需要部署维护一个应用部署和一个代码仓库即可,相关通用逻辑(中间件/通用业务逻辑)的升级成本也相对较低。

  2. 开发成本低:业务逻辑只需考虑单个应用内的情况,业务请求失败。


  • 缺点

  1. 单点风险:所有业务逻辑都在一个应用内,如果应用 Crash 则会导致这个站点不可用。

  2. 应用越来越笨重,启动速度随着业务初始化逻辑的复杂变得越来越慢。而启动速度慢后影响了业务发布迭代的效率。

  3. 开发人员增加时,处理代码提交冲突的时间将占有开发者的大量时间,极大降低开发效率(当然,如果你在代码模块设计的足够好,单仓库对人员增加的提交冲突概率并不会体现)。



   按业务功能模块拆分


随着业务的发展,人员的不断增多,并且对整体的稳定性和风险控制的要求越来越高,自然而然的出现了把单体应用拆分的想法(而人员团队也由于管理上的需要也需要拆分,那么很自然的想到了一个团队负责一个模块),此时往往团队人会不是特别多,最容易想到的自然就是按照业务功能模块拆分,例如电商系统的商品,订单,交易,用户等等。同时也在垂直链路对db等基础组件的访问做了拆分(背后分别对应不同的开发团队去完成设计,开发,部署,运维)。



而此时出现了不同模块之间相互调用的需求,但此时由于各个模块分散在不同的团队,当时往往也没有一个专门的团队去维护模块之间如何通讯,导致模块之间的通讯变得没有规范(当时RPC比较多,也没有统一的数据传输协议,业务开发者需要处理连接性、路由、数据模型转换等),从而比较复杂。


  • 优势


  1. 解决了需要拆分团队后单体架构无法支持快速迭代的问题。

  2. 一定程度上解决了单点风险,一个模块 Crash 不会导致整体全部不可用。


  • 缺点

  1. 模块间通讯比较复杂。

  2. 模块内业务逻辑更加复杂后,单个模块就像单体应用一样变得笨重和维护成本的急剧上升。


注:代码仓库和应用的拆分不一定一一对应,比如可以将多个部署模块都使用相同的代码仓库,而只是在部署的时候分不同模块分开部署。


   面向服务的架构(SOA)-ESB


当"按业务功能模块拆分"的架构下,同一模块内业务逻辑的复杂需要的开发人员也需要更加多,且组织架构上也需要进一步拆分,两方面都促成了相同的需求:系统模块迫切需要进一步拆分。且需要解决模块间通讯比较复杂的问题,同时 Web 服务标准和规范(SOAP 等)以及  XML 协议的出现使得技术上有了相应的标准,把通用的服务调用组件从业务中剥离出来并统一部署管理,于是SOA 理念诞生了:把应用按服务维度拆分,并把公共的服务治理(服务发现,限流,熔断等)能力从应用拆分出来,有了标准的服务调用方式。于是团队架构也可以更细粒度的进行拆分,并出现了维护公共能力的中间件团队。

而 ESB(企业服务总线) 是 SOA 其一种实现方式,使用中心式的总线,把服务治理能力都集成到了中心 ESB 中:



  • 优势

  1. 通过更高的敏捷性和更有效的开发来降低成本

  2. 便于维护:由于所有服务都是彼此独立、互不依存,因此可以根据需要对其进行修改和更新,而不会影响其他服务。

  3. 可扩展性:由于 SOA 允许服务跨多种服务、平台和编程语言运行,因此极大地提高了可扩展性。SOA 采用的是标准通信协议,这让企业减少了客户端与服务之间的交互。减少这一级的交互可以降低应用扩展的压力,同时提高扩展的便利性。

  4. 更可靠:由于调试小型服务要比调试大量代码更加容易,因此 SOA 生成的应用更加可靠。


  • 缺点

  1. 单点风险:ESB 是个集中式的集群,负载较高,且单点不可用会影响全局服务。


   微服务架构


而为了解决 ESB 的单点风险,出现了另外一种新的 SOA 实现:微服务架构。微服务架构把 ESB 的单点总线服务拆分到了每个服务内部,最处普遍使用 SDK 的方式集成到应用内部,此时出现了管理这些 SDK 以及背后能力的中间件团队(阿里的中间件团队正式诞生于这个时期)。



  • 优势

  1. 无单点风险:所有服务功能和相关服务里治理能力都在单个服务内部集成,不存在集中式单点集群
  2. 通过更高的敏捷性和更有效的开发来降低成本
  3. 更可靠:由于调试小型服务要比调试大量代码更加容易,因此微服务同样也具有 SOA 的可靠性。

  • 缺点

  1. 扩展性变低:以 SDK 集成到服务内部的方式其实一定程度上牺牲了 ESB 所带来的跨编程语言调用的优势(需要和业务开发语言相同才容易集成),同时对企业内多语言调用需要维护多种语言的 SDK,并且很难达到多语言完全一致。
  2. 运维成本较高:中间件相关 SDK 升级需要推动业务方一个个应用重新集成和发布,相关升级人力成本大。

进一步的细分


  富 sdk


随着微服务架构中的中间件的 sdk 化后,有些通用的业务服务也把部分通用逻辑写入到了自身提供的 sdk 中,出现了富 sdk,而这类 sdk 往往包含大量业务逻辑,且存在不同用户使用方式的不同,使得微服务已变得不再 "微",在没有写任何业务代码的时候,就已经包含了大量业务依赖二方库,相关初始化逻辑的叠加使得服务启动速度也受到很大的影响。


此时,要推进某个 sdk 的全量升级,往往需要耗费大量的工作量,并且需要让每个业务方都参与进来,导致即使所有 sdk 同时更新且一年只更新一次,那么也需要消耗大量人力成本,同时稳定性也有巨大的挑战。

而同时这里面需要解决不同 sdk 以及业务逻辑之间所依赖的同一个库(比如 json 库)的不同版本问题:



对于 java 服务,由于存在类隔离机制,往往能做到中间件等 sdk 和业务逻辑使用不同的类加载器而做到同时依赖同一个库的多个版本。但对于没有类似类隔离机制的其他语言,那么势必要让所有 sdk 使用同一个版本的 json 库,而如果这个库各个版本之间是不兼容的,那么升级成本则更加巨大,甚至几乎无法升级。这大大限制了以 java 为主的企业内对其他语言的支持度和发展。


  Service mesh


正因为富 sdk 所存在的问题,于是出现了把中间件 sdk 剥离到独立进程中,同时随着服务一起部署,在微服务架构不改变的前提下解决中间件 sdk 的升级维护问题,且微服务业务开发者不再需要关注这些服务治理相关的 sdk ,全部由中间件部门统一完成部署和升级,同时对业务方可以无感升级,sidecar 和业务代码所用的开发语言不再需要相同。



而这里的 servicemesh 只是解决了服务治理相关中间件 sdk 的剥离,还有一些其他基础组件 sdk 还未剥离,如何不同部门的不同基础设施(db, mq等待)都集成到同一个sidecar,那么这个 sidecar 本身也会比较重,所以目前一种趋势是多 sidecar 拆分部署,例如 db sidecar,mq sidecar 等等,并由各自团队分别维护和管理。同时 sidecar 的拆分则往往也是跟随着背后维护团队的组织架构来决定,且各个 sidecar 的升级对业务开发者和其他 sidecar 开发者透明。



  • 相比富 sdk ,标准化 service mesh 的优点:


  1. 中间件迭代速度加快:可快速升级和回滚,可以让中间件等基础设施快速拥有快速试错的可能,并提高迭代效率。

  2. 多语言支持成本低:新接入一种语言只需要实现其最基础的通讯协议的轻量 sdk 即可(比如 dubbo,redis等),相关容错,高可用,安全等业务无关的逻辑可不用在每种语言内重复实现。


  • 而 mesh 也不是万能的,同样也存在一些问题:

  1. 增加了一定的性能消耗(RT, CPU 消耗)。

  2. 需要根据业务方所使用的基础组件,适配每种组件的协议。

  3. 对于每种基础设施,业务方仍然需要引入其原始协议 sdk(比如 redis,mysql,dubbo,rocketmq,nsq 等等)。


  统一编程平面(sidecar 标准化)


当富 sdk 中的中间件被分别 sidecar 化后,可能还有很多业务相关的通用 sdk 存在于业务进程内,而这部分其实也是通用逻辑,也可以像中间件一样 sidecar 化,但是业务 sdk 往往比中间件 sdk 逻辑更加复杂且种类更加多;而即便中间件 sdk 数量增长短期内看起来不会特别快,但如果把所有 sidecar 都以一种相同的 api 规范对业务提供服务,对于同一种类的中间完全可以使用相同的 api,这样可以做到对业务更进一步屏蔽中间件/通用业务的种类和使用方式(业务只需要知道这一层通用 api 以及其使用方式,底层实际中间件提供可以随时替换)。这就出现了 dapr/bottle(淘系自研) 等标准化 api 为思路的 sidecar 方案。



  • 相比service mesh,标准化 sidecar 的优点:

  1. 业务依赖更少:业务部署代码更进一步轻量化,只有业务代码和非常轻量的标准化 api sdk。

  2. 扩展性:每个标准化协议背后提供服务的具体基础设施可以被无感的替换种类和升级。

  • 但同样也存在一些问题:

  1. 现有业务接入改造非常大,需要重新使用标准的 api。

  2. 目前 api 不全,抽象有一定的难度(自由度和易用度的权衡)。


目前这还是一个正在探索的方向(当然阿里云和淘系内都已经有部分业务落地了),对于有标准化需求的 serverless 平台或者全新的业务等有较大的吸引力。


  Serverless


前面列举了这么多,业务开发者还是需要去关注自己的业务部署在哪些机器(或者说容器 pod)上,以及需要自己去根据性能来扩缩容节点数,线上运行环境等等,总之业务开发者除了关注自己的代码以外还需要或多或少关注到底层技术设施,为了更进一步的减轻业务开发者的关注点,Serverless 的概念就出现了,目的是让业务开发者只需要关注自己业务逻辑相关的代码,其他所有底层基础设施,中间件,通用层都不再需要关注。而把这些都交给 Serverless 平台以及底下的各个基础设施团队去维护。
而 Serverless 平台把计算部分(微服务中的 service)进一步拆分成了更小的粒度-- faas(函数即服务),同时相关存储、数据库、中间件等基础服务以 baas(后端即服务) 的形态提供;业务开发者只需要提交代码,不需要关注时如何运行起来的。
Serverless 平台的构建可以用前面提到的 Service mesh 或者标准 api 等方式作为其基础设施。
Serverless 平台往往也提供了弹性扩缩容,自动化容量评估,无人值守等等能力,但如果一个函数(业务代码)依赖的相关 sdk 以及初始化逻辑复杂影响了冷启动时间,那么会使得其弹性扩缩容非常受限,如果想真正发挥出其价值的话,需要让每个函数拆分的足够小,足够轻量;而函数拆分的粒度太小的话,整个系统相互调用时整体开销就会大幅增加(会出现更多的网络调用),所以目前 Serverless 用的较多的都是在前端 api 的业务聚合层,或者相对比较独立的业务系统(iot 设备上报,ai 算法计算等)。

展望


回顾整个历史的发展,应用被不断拆分的越来越细,且从前期的横向拆分到后面的 sidecar 等方式的纵向单节点内的拆分,而其背后的团队则越来越多也越来越复杂,职责边界越来越明确也驱动了架构的拆分(和组织拆分一样需要保持小而高效的团队,一个内聚的服务被太多的人同时维护效率也会降低效率);同时底层细节不断被屏蔽,编程门槛在不断的降低,似乎可以猜测将来几乎任何人都会将自己的想法快速变成代码执行。

下面是一些技术对未来更进一步的实现 Serverless 落地的猜想:


  WASI (系统 api 层面的标准化)


WebAssembly 最早期是被用于给 web 编程支持更多语言的扩展,把其他语言实现的代码能够直接被 JavaScript 所调用,并在浏览器运行。其定义了一套把其他语言编译成统一字节码规范的方式,并由 WebAssembly 虚拟机解释并运行(更多 WebAssembly 的细节不是本文重点,这里不再多介绍,如果不熟悉可以自行去了解)。

然后随着 WASI(WebAssembly System Interface) 的出现,使得 WebAssembly 可以在 web 之外运行,这让 WebAssembly 有了更多的想象空间,WASI 定义了一套类似 POISX 的标准编程协议,开发者不再直接面对内核提供的 POISX 标准编程(或者说不再关注内核来做相关编程)。



而这将会带来什么变化呢?于对运行在上的 runtime 提供的 WASI 标准,runtime 就是宿主机。也就是说开发者不再接触到具体的内核,对于开发者来说极机器就是 WASI 虚拟机!这使得编译成 WebAssembly 的代码可以被非常轻量的调度(一个 WASI 虚拟机进程可以随时替换和增加其所运行的 WebAssembly 代码)和启动。这给了 Serverless 的更大规模落地有了更多的想象空间。



当然目前 WASI 标准还处于比较早期,网络通讯和多线程都还未支持,这使得目前使用上比较受限,基本只能做纯计算,例如 Service mesh 中的 sidecar (envoy 以及蚂蚁的 mosn 等) 目前把 WebAssembly 当成了网络流量处理插件,而如果插件中涉及需要网络调用的部分则直接委托给了 sidecar 本身的实现。而同时 Java 等自带虚拟机的语言对支持 WebAssembly 难度更大,如正在尝试的 GraalVM,其把现有代码编译成 WebAssembly 还需要一些改造,目前现状是如果没有研究 GraalVM 专业人员的帮助,现有业务代码几乎很难成功编译。

WASI 和 WebAssembly 似乎可以解决 Serverless 计算中拆分过细导致的网络开销过大的问题(用 WebAssembly 虚拟机统一收敛出口,以及随时全局调度函数尽可能的本地化调用)。

如果将来 WASI 成了后端开发者的编程标准,那么将彻底屏蔽内核等传统"机器"的的概念,取而代之的是更加轻量化的"机器"-WASI运行时,在这之上将会开启一个全新的纪元(但这显然是个漫长的路程且不一定会走这个方向)。


  统一编程平面 (业务 api 层面的标准化)



统一编程平面(标准业务 API) 则是以 dapr 等尝试的建立一套标准的业务开发所需的基础 api (前文已经提过),这是另外一种从业务 api 层面标准化的方向。这使得业务开发者只需要面向一组通用 api 来编程,而其背后的部署结构,实现方式则完全透明,并可随时替换。这看起来也给 Serverless 进一步落地带来了更大的可能性。

如果将来统一编程平面成了后端开发者的编程标准,那么将彻底屏蔽所有中间件以及基础设施和服务调用的差异,开发者的所有业务逻辑都将由标准 api 来组合完成,就像内核接口和系统调用一样,底层基础设施就变成了这个"庞大操作系统的内核"。


  云原生编程语言 (编程语言层面的标准化)


既然 Serverless 目的是让业务不再关注服务器等基础设施的细节,那么能不能直接从编程语言下手,细到每一个对象的 new ,每一行语句的执行都能被整个集群内分布式的调度(比函数级别更细),而开发者方编程的时候只需要把这个集群都当成一个巨大的单机机器即可,底下的所有计算,存储等等全部由 Serverless 托管,相关的执行性能优化也都交给 Serverless,每一行代码再哪个机器执行,如何执行都交给 Serverless 平台。

例如下面 HelloWord 代码可能被运行在了不同的机器上:

public class HelloWorld { public static void main(String[] args) { String str = new String("Hello, World"); // 运行在服务器 A System.out.println(str); // 传递数据到服务器 B,并在服务器 B 运行 }}


如果将来云原生编程语言能流行,并在语法上加以简化,似乎可以针对比较初级的程序员甚至是非程序员直接编写可以处理高并发,大流量,大数据等现在需要比较资深的程序员才能正确处理的工作,极大的降低互联网开发门槛。

而目前已经出现了两种云原生编程语言 Ballerina 和 Pulumi,他们都基于了IAC(基础设施即代码)的理念,在语言代码内可以直接非常简单的创建云基础设施,不再需要关注服务器等基础设施的部署。

例如 Pulumi 官方给出的创建一个 web server 的例子:

let aws = require("@pulumi/aws");let sg = new aws.ec2.SecurityGroup("web-sg", { ingress: [{ protocol: "tcp", fromPort: 80, toPort: 80, cidrBlocks: ["0.0.0.0/0"]}],});for (let i = 0; i < 3; i++) { new aws.ec2.Instance(`web-${i}`, { ami: "ami-7172b611", instanceType: "t2.micro", securityGroups: [ sg.name ], userData: `#!/bin/bash echo "Hello, World!" > index.html nohup python -m SimpleHTTPServer 80 &`, });}


只要编译运行就创建出了相应的 aws 的 ecs 机器,并运行相关命令和配置。

初看似乎比较好,声称去掉了繁琐的 yaml 配置,把很多相关逻辑其实写到了编译器和语言运行时内,把相关的云服务都封装了一层自己的语法,开发者还是可以关注背后云基础设施(看起来只是简化了),但结合 faas 等服务后可以做到大部分情况不再需要关注底层基础设施。但整体发展还处在非常早期,目前看起来主要还是对各个云服务厂商能力的封装和抽象的一个工具,有自己的预发,同时也提供一些其他语言的类库(可以给别的现有编程语言调用)。距离理想的完全屏蔽基础设施还有很大的差距(也或许永远无法屏蔽吧)。


未来将走向何方,就交给时间和一滴滴改变世界的创作者们吧~


引用


  • https://zhuanlan.zhihu.com/p/42115757

  • https://www.redhat.com/zh/topics/cloud-native-apps/what-is-service-oriented-architecture

  • https://jimmysong.io/kubernetes-handbook/cloud-native/cloud-native-programming-languages.html


✿  拓展阅读

作者|风弈

编辑|橙子君

出品|阿里巴巴新零售淘系技术


: . Video Mini Program Like ,轻点两下取消赞 Wow ,轻点两下取消在看

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存