天天看點

網際網路後端架構演進及未來猜想

原創 風弈 淘系技術  6月2日

網際網路後端架構演進及未來猜想

當年的一個不經意間産生了一個偉大的發明——計算機,在這之上又是一個不經意間産生了徹底的改變你我生活的-網際網路;這些偉大的轉折點,從來不是突然之間到來,就像大海都是由一滴滴水滴慢慢彙聚而成,而我們就像水消失在水中,看似微不足道,但卻都不可或缺;作為網際網路技術發展的史上的一滴微不足道的水滴,讓我們來回顧一下網際網路後端應用的架構發展,以及對未來的一些思考和展望吧...

本文是純粹的思考和讨論,盡可能客觀的讨論相關架構。主要對後端 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. 更可靠:由于調試小型服務要比調試大量代碼更加容易,是以微服務同樣也具有 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 等服務後可以做到大部分情況不再需要關注底層基礎設施。但整體發展還處在非常早期,目前看起來主要還是對各個雲服務廠商能力的封裝和抽象的一個工具,有自己的預發,同時也提供一些其他語言的類庫(可以給别的現有程式設計語言調用)。距離理想的完全屏蔽基礎設施還有很大的差距(也或許永遠無法屏蔽吧)。

未來将走向何方,就交給時間和一滴滴改變世界的創作者們吧~

引用