首頁 > 運動

語雀的技術架構演進之路

由 CSDN 發表于 運動2023-01-27

簡介在沒有引入函式計算之前,語雀為了支援這些功能,儘管單獨分配了一個任務叢集,在上面執行這些三方服務,接受主服務的請求來避免影響主服務的穩定性

怎麼進入語雀平臺

語雀的技術架構演進之路

作者 | 不四

每個技術人心中或多或少都有一個「產品夢」,好的技術需要搭配好的產品,才能讓使用者愛不釋手,尤其是做一款知識服務型產品。

本文從技術架構的視角,回顧了語雀的原型、內部服務和對外商業化的全過程,並對函式計算在語雀架構演進過程中所扮演的角色做了詳細的介紹。

語雀是一個專業的雲端知識庫,用於團隊的文件協作。現在已是阿里員工進行文件編寫和知識沉澱的標配,並於 2018 年開始對外提供服務。

語雀的技術架構演進之路

好的技術需要搭配好的產品

| 回到故事的開始。

2016 年,語雀孵化自螞蟻科技,當時,螞蟻金融雲需要一個工具來承載它的文件,負責的技術同學利用業餘時間,搭建了這個文件工具。專案的初期,沒有任何人員和資源支援,同時也是為了快速驗證原型,技術選型上選擇了最低成本的方案。

底層服務完全基於體驗技術部內部提供的 BaaS 服務和容器託管平臺:

Object 服務:一個類 MongoDB 的資料儲存服務;

File 服務:阿里雲 OSS 的基礎上封裝的一個檔案儲存服務;

DockerLab:一個容器託管平臺;

語雀的技術架構演進之路

這些服務和平臺都是基於 Node。js 實現的,專門給內部創新型應用使用,也正是由於有這些降低創新成本的內部服務,才給工程師們提供了更好的創新環境。

語雀的應用層服務端,自然而然的選用了螞蟻體驗技術部開源的 Node。js Web 框架 Egg(螞蟻內部的封裝 Chair),透過一個單體 Web 應用實現服務端。

應用層客戶端也選用了 React 技術棧,結合內部的 antd,並採用 CodeMirror 實現了一個功能強大、體驗優雅的 markdown 線上編輯器。

原型階段

語雀的技術架構演進之路

原型階段

2017年,隨著語雀得到團隊內部的認可,他的目標已經不僅僅是金融雲的文件工具,而是成為阿里所有員工的知識管理平臺。

不僅面向技術人員 Markdown 編輯器,還向非技術知識創作者,提供了富文字編輯器,並選擇了更“Web”的路線,在富文字編輯器中加入了公式、文字繪圖、思維導圖等特色功能。

而隨著語雀在知識管理領域的不斷探索,知識管理的三層結構(團隊、知識庫、文件)開始成型。

| 在此之上的協作、分享、搜尋與訊息動態等功能越來越複雜單純的依靠 BaaS 服務已經無法滿足語雀的業務需求了。

為了應對業務發展帶來的挑戰,我們主要從下面幾個點進行改造:

BaaS 服務雖然使用簡單成本低,但是它們提供的功能不足以滿足語雀業務的發展,同時穩定性上也有不足。所以我們將底層服務由 BaaS 替換成了阿里雲的 IaaS 服務(MySQL、OSS、快取、搜尋等服務)。

Web 層仍然採用了 Node。js 與 Egg 框架,但是業務層借鑑 rails 社群的實踐開始變成了一個大型單體應用,透過引入 ORM 構建資料模型層,讓程式碼的分層更清晰。

前端編輯器從 codeMirror 遷移到 Slate。為了更好的實現語雀編輯器的功能,我們內部 fork 了 Slate 進行深入開發,同時也自定義了一個獨立的內容儲存格式,以提供更高效的資料處理和更好的相容性。

語雀的技術架構演進之路

當時僅僅是一個工程師的業餘專案,採用內部專為創新應用提供的 BaaS 服務和一系列的開源技術,驗證了線上文件工具這個產品原型。

語雀的技術架構演進之路

內部服務階段

隨著語雀在內部的影響力越來越大,一些離職出去創業的阿里校友們開始找到玉伯(螞蟻體驗技術部研究員):“語雀挺好用的,有沒有考慮商業化之後讓外面的公司也能夠用起來?”

| 經過小半年的醞釀和重構,2018 年初,語雀開始正式對外提供服務,進行商業化。

當一個應用走出公司內到商業化環境中,面臨的技術挑戰一下子就變大了。最核心的知識創作管理部分的功能越來越複雜,表格、思維導圖等新格式的加入,多人實時協同的需求對編輯器技術提出了更高的挑戰。

而為了更好的服務企業使用者與個人使用者,語雀在企業服務、會員服務等方面也投入了很大精力。在業務快速發展的同時,服務商業化對質量、安全和穩定性也提出了更高的要求。

為了應對業務發展,語雀的架構也隨之發生了演進:

我們將底層的依賴完全上雲,全部遷移到了阿里雲上,阿里雲不僅僅提供了基礎的儲存、計算能力,同時也提供了更豐富的高階服務,同時在穩定性上也有保障。

豐富的雲計算基礎服務,保障語雀的服務端可以選用最適合語雀業務的的儲存、佇列、搜尋引擎等基礎服務;

更多人工智慧服務給語雀的產品帶來了更多的可能性,包括 OCR 識圖、智慧翻譯等服務,最終都直接轉化成為了語雀的特色服務;

而在應用層,語雀的服務端依然還是以一個基於 Egg 框架的大型的 Node。js Web 應用為主。但是隨著功能越來越多,也開始將一些相對比較獨立的服務從主服務中拆出去,可以把這些服務分成幾類:

微服務類:例如多人實時協同服務,由於它相對獨立,且長連線服務不適合頻繁釋出,所以我們將其拆成了一個獨立的微服務,保持其穩定性。

任務服務類:像語雀提供的大量本地檔案預覽服務,會產生一些任務比較消耗資源、依賴複雜。我們將其從主服務中剝離,可以避免不可控的依賴和資源消耗對主服務造成影響。

內部服務階段

隨著編輯器越來越複雜,在 slate 的基礎上進行開發遇到的問題越來越多。最終語雀還是走上了自研編輯器的道路,基於瀏覽器的 Contenteditable 實現了富文字編輯器,透過 Canvas 實現了表格編輯器,透過 SVG 實現了思維導圖編輯器。

語雀的技術架構演進之路

語雀的這個階段(也是現在所處的階段)是商業化階段,但是我們仍然保持了一個很小的團隊,透過 JavaScript 全棧進行研發。底層的服務全面上雲,借力雲服務打造語雀的特色功能。同時為企業級使用者和個人知識工作者者提供知識創作和管理工具。

語雀的技術架構演進之路

在內部服務階段,語雀已經成為了一個正式的產品,透過在阿里內部的磨鍊,語雀的產品形態基本定型。

語雀是一個複雜的 Web 應用,也是一個典型的資料密集型應用(Data-Intensive Application),背後依賴了大量的資料庫等雲服務。語雀服務端是 Node。js 技術棧。

當提到 node 的時候,可能立刻就會有幾個詞浮現在我們腦海之中:

對外商業化階段

對外商業化階段

函式計算類:類似 Plantuml 預覽、Mermaid 預覽等任務,對響應時間的敏感度不高,且依賴可以打包到阿里雲函式計算中,我們會將其放到函式計算中執行,既省錢又安全。

,這些特性一方面非常的適合於構建可擴充套件的網路應用,用來實現 Web 服務這類 I/O 密集型的應用,另一方面它也是大家一直對 node 詬病的地方,對 CPU 密集型的場景不夠友好,一旦有任何阻塞程序的方法被執行,整個程序就被阻塞。

像語雀這樣用 node 實現整個服務端邏輯的應用,很難保證不會出現一些場景可能會消耗大量 CPU 甚至是死迴圈阻塞程序的,例如以 markdown 轉換舉例,由於使用者的輸入無法窮舉,總有各種可能讓轉換程式碼進入到一個低效甚至是死迴圈的場景之中。

在 node 剛出世的年代,很難給這些問題找到完美的解決辦法,而即便是 Java 等基於執行緒併發模型的語言,在遇到這樣的場景也很頭痛,畢竟 CPU 對於 web 應用來說都是非常重要的資源。而隨著基礎設定越來越完善,當函式計算出現時,node 最大的短板看起來有了一個比較完美的解決方案。

阿里雲函式計算是事件驅動的全託管計算服務。透過函式計算,您無需管理伺服器等基礎設施,只需編寫程式碼並上傳,只需要為程式碼實際執行所消耗的資源付費,程式碼未執行則不產生費用。

和函式計算的不解之緣

以語雀中遇到的一個實際場景來舉例,使用者傳入了一些 HTML 或者 Markdown 格式的文件內容,我們需要將其轉換成為語雀自己的文件格式。

在絕大部分情況下,解析使用者輸入的內容都很快,然而依然存在某些無法預料到的場景會觸發解析器的 bug 而導致死迴圈的出現,甚至我們不太敢升級 Markdown 解析庫和相關外掛以免引入更多的問題。

但是隨著函式計算的引入,我們將這個消耗 CPU 的轉換邏輯放到函式計算上,語雀的主服務穩定性不會再被影響。

語雀的技術架構演進之路

| 除了幫助 Web 系統分擔一些 CPU 密集型操作以外,函式計算還能做什麼呢?

在語雀上我們支援各種程式碼形式來繪圖,包括 Plantuml、公式、Mermaid,還有一些將文件匯出成 PDF、圖片等功能。這些場景有兩個特點:

他們依賴於一些複雜的應用軟體,例如 Puppeteer、Graphviz 等;

可能需要執行使用者輸入的內容;

支援這類場景看似簡單,透過 process。exec子程序呼叫一下就搞定了。但是當我們想把它做成一個穩定的對外服務時,問題就出現了。這些複雜的應用軟體可能從設計上並沒有考慮要長期執行,長期執行時的記憶體佔用、穩定性可能會有一些問題,同時在被大併發呼叫時,對 CPU 的壓力非常大。

再加上有些場景需要執行使用者輸入的程式碼,攻擊者透過構建惡意輸入,可以在伺服器上執行攻擊程式碼,非常危險。

在沒有引入函式計算之前,語雀為了支援這些功能,儘管單獨分配了一個任務叢集,在上面執行這些三方服務,接受主服務的請求來避免影響主服務的穩定性。但是為了解決上面提到的一系列問題還需要付出很大的成本:

需要維持一個不小的任務叢集,儘管可能大部分時間都用不上那麼多資源。

需要定時對三方應用軟體進行重啟,避免長時間執行帶來的記憶體洩露,即便如此有些特殊請求也會造成第三方軟體的不穩定。

對使用者的輸入進行檢測和過濾,防止駭客惡意攻擊,而駭客的攻擊程式碼很難完全防住,安全風險依舊很大。

語雀的技術架構演進之路

最後語雀將所有的第三方服務都分別打包在函式中,將這個任務叢集上的功能都拆分成了一系列的函式放到了函式計算上。透過函式計算的特點一下解決了上面的所有問題:

函式計算的計費模式是按照程式碼實際執行的 CPU 時間計費,不需要長期維護一個任務叢集了。

函式計算上的函式執行時儘管會有一些常駐函式的最佳化,但是基本不用考慮長期執行帶來的一系列問題,且每次呼叫之間都相互獨立,不會互相影響。

使用者的輸入程式碼是執行在一個沙箱容器中,即便不對使用者輸入做任何過濾,惡意攻擊者也拿不到任何敏感資訊,同時也無法進入內部網路執行程式碼,更加安全。

語雀的技術架構演進之路

| 除了上面提到的這些功能之外,語雀最近還使用 OSS + 函式計算替換了之前使用的阿里雲影片點播服務來進行影片和音訊的轉碼。

由於瀏覽器可以直接支援播放的音影片格式並不多,大量使用者上傳的影片想要能夠直接在語雀上進行播放需要對它們進行轉碼,業界一般都是透過 FFmpeg 來對音影片進行轉碼的。

單執行緒(single-threaded)

函式計算直接集成了 FFmpeg 提供音影片處理能力,並整合到應用中心,配合 SLS 完善了監控和資料分析。

非阻塞(non-blocking)

語雀的技術架構演進之路

從語雀的實踐來看,語雀並沒有像 SFF 一樣將 Web 服務遷移到函式計算之上(SFF 模式並不是現在的函式計算架構所擅長的),但是函式計算在語雀整體的架構中對穩定性、安全性和成本控制起到了非常重要的作用。總結下來函式計算非常適合下面幾種場景:

對於時效性要求不算非常高的 CPU 密集型操作,分擔主服務 CPU 壓力。

當做沙箱環境執行使用者提交的程式碼。

執行不穩定的三方應用軟體服務。

需要很強動態伸縮能力的服務。

在引入函式計算之後,語雀現階段的架構變成了以一個 Monolith Application 為核心,並將一些獨立的功能模組根據使用場景和對能力的要求分別拆分成了 Microservices 和 Serverless 架構。

應用架構與團隊成員組成、業務形態息息相關,但是隨著各種雲服務與基礎設施的完善,我們可以更自如的選擇更合適的架構。

語雀的技術架構演進之路

為什麼要特別把 Serverless 單獨拿出來說呢?還記得之前說 Node。js 是單執行緒,不適合 CPU 密集型任務麼?

由於 Serverless 的出現,我們可以將這些存在安全風險的,消耗大量 CPU 計算的任務都遷移到函式計算上。它執行在沙箱環境中,不用擔心使用者的惡意程式碼造成安全風險,同時將這些 CPU 密集型的任務從主服務中剝離,避免出現併發時阻塞主服務。

按需付費的方式也可以大大節約成本,不需要為低頻功能場景部署一個常駐服務。所以我們會盡量的把這類服務都遷移到 Serverless 上(如阿里雲函式計算)。

語雀的技術架構演進之路

非同步(asynchronously programming)

語雀這幾年一步步發展過來,背後的技術一直在演進,但是始終遵循了幾條原則:

技術棧選型要匹配產品發展階段。產品在不同的階段對技術提出的要求是不一樣的,越前期,對迭代效率的要求越高,商業化規模化之後,對穩定性、效能的要求就會變高。不需要一上來就用最先進的技術方案,而是需要和產品階段一起考慮和權衡。

技術棧選型要結合團隊成員的技術背景。語雀選擇 JavaScript 全棧的原因是孵化語雀的團隊,大部分都是 JavaScript 背景的程式設計師,同時 Node。js 在螞蟻也算是一等公民,配套的設施相對完善。

最重要的一點是,不論選擇什麼技術棧,安全、穩定、可維護(擴充套件)都是要考慮清楚的。用什麼語言、用什麼服務會變化,但是這些基礎的安全意識、穩定性意識,如何編寫可維護的程式碼,都是決定專案能否長期發展下去的重要因素。

把函式計算引入之後,我們可以將那些 CPU 密集型、存在不穩定因素的操作統統放到函式計算服務中去執行,而我們的主服務再次迴歸到了 I/O 密集型應用模型,又可以愉快的享受 node 給我們帶來的高效研發福利了!

何翊宇,花名不四,高階前端技術專家,現就職於螞蟻金服體驗技術部,語雀產品技術負責人。2011 年開始專注在 Node。js 與 Web 研發領域,負責過內部的 Node。js 的模組管理系統和中介軟體服務等基礎設施,也做過 Node。js Web 框架的研發和開源。同時持續在使用 Node。js 進行產品研發,先後負責過淘寶時光機、天貓搭建渲染服務以及語雀等產品。開源愛好者,Koa。js 和 Egg。js 核心開發者,cnpm 中國映象維護者。

語雀的技術架構演進之路

Tags:語雀服務函式計算JS