溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

語(yǔ)雀如何用JavaScript全棧打造商業(yè)級(jí)應(yīng)用

發(fā)布時(shí)間:2021-09-30 09:49:20 來(lái)源:億速云 閱讀:121 作者:柒染 欄目:云計(jì)算

本篇文章為大家展示了語(yǔ)雀如何用JavaScript全棧打造商業(yè)級(jí)應(yīng)用,內(nèi)容簡(jiǎn)明扼要并且容易理解,絕對(duì)能使你眼前一亮,通過(guò)這篇文章的詳細(xì)介紹希望你能有所收獲。

語(yǔ)雀是什么?

語(yǔ)雀是一個(gè)專(zhuān)業(yè)的云端知識(shí)庫(kù),面向個(gè)人和團(tuán)隊(duì),提供與眾不同的知識(shí)管理,打造輕松流暢的工作協(xié)同,它提供各種格式的在線(xiàn)文檔(富文本、表格、設(shè)計(jì)稿等)編輯能力,支持實(shí)時(shí)在線(xiàn)多人協(xié)同編輯,數(shù)據(jù)云端保存不丟失。而語(yǔ)雀與其他文檔工具最大的不同是,它通過(guò)知識(shí)庫(kù)來(lái)對(duì)文檔進(jìn)行組織,讓知識(shí)創(chuàng)作者更好的管理知識(shí)。

語(yǔ)雀如何用JavaScript全棧打造商業(yè)級(jí)應(yīng)用

語(yǔ)雀技術(shù)架構(gòu)演進(jìn)

原型階段

語(yǔ)雀誕生于 2016 年,當(dāng)時(shí)螞蟻金融云需要一個(gè)工具來(lái)承載它的文檔。當(dāng)時(shí)負(fù)責(zé)的技術(shù)同學(xué)利用業(yè)余時(shí)間,開(kāi)始搭建這個(gè)文檔工具。項(xiàng)目的初期,沒(méi)有任何人員和資源支持,同時(shí)也為了快速驗(yàn)證原型,技術(shù)選型上選擇了最低成本的方案。

底層服務(wù)完全基于體驗(yàn)技術(shù)部?jī)?nèi)部提供的 BaaS 服務(wù)和容器托管平臺(tái):

  • Object 服務(wù):一個(gè)類(lèi) MongoDB 的數(shù)據(jù)存儲(chǔ)服務(wù);

  • File 服務(wù):阿里云 OSS 的基礎(chǔ)上封裝的一個(gè)文件存儲(chǔ)服務(wù);

  • DockerLab:一個(gè)容器托管平臺(tái);

這些服務(wù)和平臺(tái)都是基于 Node.js 實(shí)現(xiàn),專(zhuān)門(mén)給內(nèi)部創(chuàng)新型應(yīng)用使用,也正是由于有這些降低創(chuàng)新成本的內(nèi)部服務(wù),才給工程師們提供了更好的創(chuàng)新環(huán)境。

應(yīng)用層服務(wù)端自然而然的選用了體驗(yàn)技術(shù)部開(kāi)源的 Node.js Web 框架 Egg(螞蟻內(nèi)部的封裝 Chair),通過(guò)一個(gè)單體 Web 應(yīng)用實(shí)現(xiàn)服務(wù)端。應(yīng)用層客戶(hù)端也選用了 React 技術(shù)棧,結(jié)合內(nèi)部的 antd,并采用 CodeMirror 實(shí)現(xiàn)了一個(gè)功能強(qiáng)大、體驗(yàn)優(yōu)雅的 markdown 在線(xiàn)編輯器。

語(yǔ)雀如何用JavaScript全棧打造商業(yè)級(jí)應(yīng)用

這時(shí)可以算作語(yǔ)雀的“原型階段”,它僅僅是一個(gè)工程師的業(yè)余項(xiàng)目,采用內(nèi)部專(zhuān)為創(chuàng)新應(yīng)用提供的 BaaS 服務(wù)和一系列的開(kāi)源技術(shù)解決方案,驗(yàn)證了在線(xiàn)文檔工具這個(gè)產(chǎn)品原型。

PS:當(dāng)時(shí)我還不在語(yǔ)雀團(tuán)隊(duì),但是巧的是我卻在給語(yǔ)雀提供 Object、File 等 BaaS 服務(wù)和 Egg.js Web 框架的支持。

內(nèi)部服務(wù)階段

隨著在線(xiàn)文檔工具得到了團(tuán)隊(duì)內(nèi)部的認(rèn)可,語(yǔ)雀的目標(biāo)已經(jīng)不僅僅是金融云的文檔工具,而是志在替代 confluence 等競(jìng)品,成為阿里內(nèi)部十萬(wàn)員工的知識(shí)管理平臺(tái)。語(yǔ)雀要面向知識(shí)創(chuàng)作者,只提供 Markdown 編輯器肯定無(wú)法讓非技術(shù)人員更高效的使用語(yǔ)雀。盡管有不少真愛(ài)粉因?yàn)檎Z(yǔ)雀開(kāi)始學(xué)習(xí)甚至愛(ài)上了 Markdown,但是我們?nèi)匀涣x無(wú)反顧的踏入了富文本編輯器領(lǐng)域的深坑。同時(shí)和 Word 等富文本編輯器不同,我們選擇了更“Web”的路線(xiàn),在富文本編輯器中加入了公式、文本繪圖、思維導(dǎo)圖等特色功能。而隨著語(yǔ)雀在知識(shí)管理領(lǐng)域的不斷探索,知識(shí)管理的三層結(jié)構(gòu)(團(tuán)隊(duì)、知識(shí)庫(kù)、文檔)開(kāi)始成型。在此之上的協(xié)作、分享、搜索與消息動(dòng)態(tài)等功能越來(lái)越復(fù)雜單純的依靠 BaaS 服務(wù)已經(jīng)無(wú)法滿(mǎn)足語(yǔ)雀的業(yè)務(wù)需求了。

為了應(yīng)對(duì)業(yè)務(wù)發(fā)展帶來(lái)的挑戰(zhàn),我們主要從下面幾個(gè)點(diǎn)進(jìn)行改造:

  • BaaS 服務(wù)雖然使用簡(jiǎn)單成本低,但是它們提供的功能不足以滿(mǎn)足語(yǔ)雀業(yè)務(wù)的發(fā)展,同時(shí)穩(wěn)定性上也有不足。所以我們將底層服務(wù)由 BaaS 替換成了內(nèi)部的 IaaS 服務(wù)(MySQL、OSS、緩存、搜索等服務(wù))。

  • Web 層仍然采用了 Node.js 與 Egg 框架,但是業(yè)務(wù)層借鑒 rails 社區(qū)的實(shí)踐開(kāi)始變成了一個(gè)大型單體應(yīng)用,通過(guò)引入 ORM 構(gòu)建數(shù)據(jù)模型層,讓代碼的分層更清晰;

  • 前端編輯器從 codeMirror 遷移到 Slate。為了更好的實(shí)現(xiàn)語(yǔ)雀編輯器的功能,我們內(nèi)部 fork 了 Slate 進(jìn)行深入開(kāi)發(fā),同時(shí)也自定義了一個(gè)獨(dú)立的內(nèi)容存儲(chǔ)格式,以提供更高效的數(shù)據(jù)處理和更好的兼容性。

語(yǔ)雀如何用JavaScript全棧打造商業(yè)級(jí)應(yīng)用

在內(nèi)部服務(wù)階段,語(yǔ)雀已經(jīng)成為了一個(gè)正式的產(chǎn)品,和螞蟻的其他項(xiàng)目沒(méi)有什么區(qū)別了,通過(guò)在阿里內(nèi)部的磨煉,語(yǔ)雀的產(chǎn)品形態(tài)基本定型。

商業(yè)化階段

隨著語(yǔ)雀的內(nèi)部影響力越來(lái)越大,一些離職出去創(chuàng)業(yè)的阿里校友們開(kāi)始找到玉伯:“語(yǔ)雀挺好用的,有沒(méi)有考慮商業(yè)化之后讓外面的公司也能夠用起來(lái)?” 經(jīng)過(guò)小半年的醞釀和重構(gòu),18 年初,語(yǔ)雀開(kāi)始正式對(duì)外提供服務(wù),進(jìn)行商業(yè)化。

當(dāng)一個(gè)應(yīng)用走出公司內(nèi)到商業(yè)化環(huán)境中,面臨的技術(shù)挑戰(zhàn)一下子就變大了。最核心的知識(shí)創(chuàng)作管理部分的功能越來(lái)越復(fù)雜,表格、思維導(dǎo)圖等新格式的加入,多人實(shí)時(shí)協(xié)同的需求對(duì)編輯器技術(shù)提出了更高的挑戰(zhàn)。而為了更好的服務(wù)企業(yè)用戶(hù)與個(gè)人用戶(hù), 語(yǔ)雀在企業(yè)服務(wù)、會(huì)員服務(wù)等方面也投入了很大精力。在業(yè)務(wù)快速發(fā)展的同時(shí),服務(wù)商業(yè)化對(duì)質(zhì)量、安全和穩(wěn)定性也提出了更高的要求。

為了應(yīng)對(duì)業(yè)務(wù)發(fā)展,語(yǔ)雀的架構(gòu)也隨之發(fā)生了演進(jìn):

我們將底層的依賴(lài)完全上云,全部遷移到了阿里云上,阿里云不僅僅提供了基礎(chǔ)的存儲(chǔ)、計(jì)算能力,同時(shí)也提供了更豐富的高級(jí)服務(wù),同時(shí)在穩(wěn)定性上也有保障。

  • 豐富的云計(jì)算基礎(chǔ)服務(wù),保障語(yǔ)雀的服務(wù)端可以選用最適合語(yǔ)雀業(yè)務(wù)的的存儲(chǔ)、隊(duì)列、搜索引擎等基礎(chǔ)服務(wù);

  • 更多人工智能服務(wù)給語(yǔ)雀的產(chǎn)品帶來(lái)了更多的可能性,包括 OCR 識(shí)圖、智能翻譯等服務(wù),最終都直接轉(zhuǎn)化成為了語(yǔ)雀的特色服務(wù);

而在應(yīng)用層,語(yǔ)雀的服務(wù)端依然還是以一個(gè)基于 Egg 框架的大型的 Node.js web 應(yīng)用為主。但是隨著功能越來(lái)越多,也開(kāi)始將一些相對(duì)比較獨(dú)立的服務(wù)從主服務(wù)中拆出去,可以把這些服務(wù)分成幾類(lèi):

  • 微服務(wù)類(lèi):例如多人實(shí)時(shí)協(xié)同服務(wù),由于它相對(duì)獨(dú)立,且長(zhǎng)連接服務(wù)不適合頻繁發(fā)布,所以我們將其拆成了一個(gè)獨(dú)立的微服務(wù),保持其穩(wěn)定性;

  • 任務(wù)服務(wù)類(lèi):像語(yǔ)雀提供的大量本地文件預(yù)覽服務(wù),會(huì)產(chǎn)生一些任務(wù)比較消耗資源、依賴(lài)復(fù)雜。我們將其從主服務(wù)中剝離,可以避免不可控的依賴(lài)和資源消耗對(duì)主服務(wù)造成影響;

  • 函數(shù)計(jì)算類(lèi):類(lèi)似 plantuml 預(yù)覽、mermaid 預(yù)覽等任務(wù),對(duì)響應(yīng)時(shí)間的敏感度不高,且依賴(lài)可以打包到阿里云函數(shù)計(jì)算中,我們會(huì)將其放到函數(shù)計(jì)算中運(yùn)行,既省錢(qián)又安全;

隨著編輯器越來(lái)越復(fù)雜,在 slate 的基礎(chǔ)上進(jìn)行開(kāi)發(fā)遇到的問(wèn)題越來(lái)越多。最終語(yǔ)雀還是走上了自研編輯器的道路,基于瀏覽器的 contenteditable 實(shí)現(xiàn)了富文本編輯器,通過(guò) canvas 實(shí)現(xiàn)了表格編輯器,通過(guò) SVG 實(shí)現(xiàn)了思維導(dǎo)圖編輯器。

語(yǔ)雀富文本編輯器相關(guān)的介紹,可以看看 Lake Editor 之父隆昊的分享:富文本編輯器的技術(shù)演進(jìn)。

語(yǔ)雀如何用JavaScript全棧打造商業(yè)級(jí)應(yīng)用

語(yǔ)雀的這個(gè)階段(也是現(xiàn)在所處的階段)是商業(yè)化階段,但是我們?nèi)匀槐3至艘粋€(gè)很小的團(tuán)隊(duì),通過(guò) JavaScript 全棧進(jìn)行研發(fā)。底層的服務(wù)全面上云,借力云服務(wù)打造語(yǔ)雀的特色功能。同時(shí)為企業(yè)級(jí)用戶(hù)和個(gè)人知識(shí)工作者者提供知識(shí)創(chuàng)作和管理工具。

JavaScript 全棧

語(yǔ)雀如何用JavaScript全棧打造商業(yè)級(jí)應(yīng)用

在社交網(wǎng)絡(luò)上,大家好像對(duì) JavaScript 全棧的看法都比較負(fù)面,“樣樣通,樣樣松”可能是大家聽(tīng)到全棧工程師這個(gè)名詞后的第一印象。那為什么語(yǔ)雀選擇了 JavaScript 全棧的方向呢?

JavaScript 全棧與產(chǎn)品工程師

在語(yǔ)雀,我們并不將用 JavaScript 全棧進(jìn)行開(kāi)發(fā)的工程師定義為全棧工程師,而是“一專(zhuān)多能”型的產(chǎn)品工程師

  • 他們是產(chǎn)品的“技術(shù)合伙人”,他們對(duì)產(chǎn)品有 owner 感,和產(chǎn)品經(jīng)理一起參與產(chǎn)品討論設(shè)計(jì),從技術(shù)的角度上對(duì)產(chǎn)品設(shè)計(jì)方案提出建議,獨(dú)立的完成產(chǎn)品功能的全棧研發(fā),并跟蹤發(fā)布后的產(chǎn)品結(jié)果。

  • 同時(shí)他們也是某一個(gè)技術(shù)領(lǐng)域的領(lǐng)域?qū)<?,例如有人可能是服?wù)端領(lǐng)域的專(zhuān)家、測(cè)試領(lǐng)域的專(zhuān)家、前端構(gòu)建領(lǐng)域的專(zhuān)家、CSS 領(lǐng)域的專(zhuān)家。他們可以用自己的專(zhuān)業(yè)領(lǐng)域知識(shí)來(lái)優(yōu)化團(tuán)隊(duì)研發(fā)工具鏈,提升產(chǎn)品研發(fā)效率。

語(yǔ)雀如何用JavaScript全棧打造商業(yè)級(jí)應(yīng)用

在語(yǔ)雀,產(chǎn)品工程師們的產(chǎn)品研發(fā)流程是這樣的:

  • 在產(chǎn)品設(shè)計(jì)階段,產(chǎn)品工程師就會(huì)參與進(jìn)去進(jìn)行討論,最終會(huì)產(chǎn)出一份 final design 的產(chǎn)品設(shè)計(jì)稿。由于前期產(chǎn)品工程師參與充分討論,一般此處定下的產(chǎn)品設(shè)計(jì)稿到后期的研發(fā)過(guò)程中不會(huì)遇到技術(shù)上的問(wèn)題;

  • **隨后會(huì)在語(yǔ)雀上進(jìn)行文檔化的系統(tǒng)分析設(shè)計(jì)。**會(huì)在語(yǔ)雀上發(fā)起異步的評(píng)審。一些大的技術(shù)方案會(huì)有其他的領(lǐng)域?qū)<壹尤脒M(jìn)來(lái)一起進(jìn)行評(píng)審,確保將所有的技術(shù)難點(diǎn)都梳理清楚;

  • 系統(tǒng)設(shè)計(jì)清晰后,進(jìn)入研發(fā)階段;

  • 對(duì)所有的代碼,都需要有自動(dòng)化測(cè)試覆蓋。對(duì)所有新增代碼和修改的業(yè)務(wù)邏輯都需要有完全覆蓋的單元測(cè)試,對(duì)關(guān)鍵鏈路的功能同時(shí)也要提供端到端測(cè)試。編寫(xiě)完自動(dòng)化測(cè)試是進(jìn)入代碼評(píng)審前的必備流程。

  • 階段性的功能研發(fā)完成、測(cè)試編寫(xiě)完善后會(huì)發(fā)起異步的代碼評(píng)審。會(huì)邀請(qǐng)相關(guān)業(yè)務(wù)的負(fù)責(zé)人和對(duì)應(yīng)的一些領(lǐng)域?qū)<襾?lái)進(jìn)行代碼評(píng)審。從業(yè)務(wù)邏輯的正確性,安全性,可維護(hù)性等多個(gè)角度來(lái)進(jìn)行代碼評(píng)審。

  • 最終在發(fā)布上線(xiàn)時(shí),必須遵循三板斧原則:可灰度、可應(yīng)急、可監(jiān)控。避免功能變更可能帶來(lái)的 bug 影響到大量用戶(hù)。

語(yǔ)雀如何用JavaScript全棧打造商業(yè)級(jí)應(yīng)用

語(yǔ)雀是如何進(jìn)行全棧 JavaScript 測(cè)試的呢?感興趣的同學(xué)可以看看語(yǔ)雀團(tuán)隊(duì)大前端自動(dòng)化測(cè)試大牛達(dá)峰老師的分享:大前端測(cè)試的思考和在語(yǔ)雀的實(shí)踐

通過(guò) JavaScript 全棧,語(yǔ)雀團(tuán)隊(duì)可以更高效、高質(zhì)量的的完成產(chǎn)品研發(fā):

  • 從代碼層面上來(lái)說(shuō),有大量的代碼可以復(fù)用,以編輯器舉例,它不僅僅可以在 Web 端使用,也可以在桌面端使用。同時(shí)許多數(shù)據(jù)處理的能力還可以在服務(wù)端使用。

  • 從產(chǎn)品研發(fā)效率上來(lái)說(shuō),全棧研發(fā)減少了大量溝通成本,在語(yǔ)雀當(dāng)前的階段是非常高效的。而 JavaScript 全棧避免了開(kāi)發(fā)者在不同的語(yǔ)言中進(jìn)行切換,不用考慮前端使用的 lodash / moment 等工具類(lèi)在其他語(yǔ)言中應(yīng)該用什么,大大提升全棧的研發(fā)效率。

  • 最后從工程師角度來(lái)看,全棧研發(fā)讓工程師有機(jī)會(huì)深度參與到產(chǎn)品研發(fā)的整個(gè)流程中,大家會(huì)自發(fā)的去思考產(chǎn)品有什么優(yōu)化點(diǎn),從技術(shù)上能幫助產(chǎn)品做什么。例如語(yǔ)雀最近新上的 OCR 搜圖功能,就是語(yǔ)雀的全棧工程師自發(fā)從技術(shù)預(yù)研到產(chǎn)品落地完成整個(gè)產(chǎn)品優(yōu)化的。

語(yǔ)雀如何用JavaScript全棧打造商業(yè)級(jí)應(yīng)用

JavaScript 全棧與 Node.js

說(shuō)到 JavaScript 全棧,有一個(gè)繞不過(guò)去的技術(shù)就是 Node.js。作為一個(gè)與前端結(jié)合緊密的服務(wù)端運(yùn)行時(shí),基本上就成為了全棧的代言人。那 Node.js 是不是真的是一個(gè)適合大型商業(yè)化項(xiàng)目的語(yǔ)言呢?大家對(duì)它都有頗多質(zhì)疑:

語(yǔ)雀如何用JavaScript全棧打造商業(yè)級(jí)應(yīng)用

其實(shí)隨著 JS 語(yǔ)言的發(fā)展,許多問(wèn)題已經(jīng)得到了解決,例如 Async Function 的出現(xiàn),可以讓開(kāi)發(fā)者以同步的方式編寫(xiě)異步代碼,理解起來(lái)更簡(jiǎn)單,異常處理也變簡(jiǎn)單了。同時(shí)隨著社區(qū)的進(jìn)一步完善,大量高質(zhì)量的工具模塊、框架涌現(xiàn)出來(lái)。語(yǔ)雀的服務(wù)端部分基于 Egg 框架,已經(jīng)集成了大量 Web 開(kāi)發(fā)需要的模塊和服務(wù),同時(shí)基于 Async Function 編程模型也更加簡(jiǎn)單。TypeScript 的出現(xiàn)也打消了許多人對(duì) JavaScript 進(jìn)行大型項(xiàng)目開(kāi)發(fā)的疑慮。除此之外,語(yǔ)雀還有一些其他的方式來(lái)保障代碼質(zhì)量和可維護(hù)性(語(yǔ)雀甚至是一個(gè)純 JavaScript 項(xiàng)目,沒(méi)有一行 TypeScript 代碼)。

語(yǔ)雀做的第一件事情就是確定核心系統(tǒng)和外部系統(tǒng)的邊界。通過(guò)六邊形架構(gòu)(也叫做端口適配器架構(gòu)),我們把語(yǔ)雀核心系統(tǒng)和外界系統(tǒng)和用戶(hù)之間的交互固定下來(lái)。通過(guò)“端口”的形式,來(lái)確定輸入和輸出。外部系統(tǒng)通過(guò)“適配器”來(lái)將系統(tǒng)對(duì)接到語(yǔ)雀暴露的端口之上,只需要按照“端口”定義來(lái)實(shí)現(xiàn),外部系統(tǒng)可以自由替換。

語(yǔ)雀如何用JavaScript全棧打造商業(yè)級(jí)應(yīng)用

在這個(gè)模型下,Controller 就是語(yǔ)雀暴露給用戶(hù)接口的 HTTP 適配器。在 Controller 中,我們對(duì)用戶(hù)請(qǐng)求參數(shù)進(jìn)行格式校驗(yàn)和轉(zhuǎn)換,檢查用戶(hù)權(quán)限,并格式化輸出。

語(yǔ)雀如何用JavaScript全棧打造商業(yè)級(jí)應(yīng)用

我們定義好語(yǔ)雀與第三方平臺(tái)和服務(wù)之間的交互方式(一般是一系列方法),通過(guò)適配器,將不同環(huán)境的不同服務(wù)封裝成統(tǒng)一的方法,并在調(diào)用時(shí)記錄好調(diào)用日志。

語(yǔ)雀如何用JavaScript全棧打造商業(yè)級(jí)應(yīng)用

數(shù)據(jù)模型層即是數(shù)據(jù)層的 Model,以 Doc 模型舉例,它的 meta 信息數(shù)據(jù)被存儲(chǔ)在了 MySQL 中,而文檔正文數(shù)據(jù)被加密后存儲(chǔ)在 OSS 中。對(duì)于語(yǔ)雀核心的業(yè)務(wù)邏輯來(lái)說(shuō),完全不感知底層的存儲(chǔ)在哪里。更進(jìn)一步來(lái)說(shuō),只要語(yǔ)雀是使用 SQL 和數(shù)據(jù)庫(kù)進(jìn)行交互,底層數(shù)據(jù)可以無(wú)縫遷移到 OceanBase 等其他支持完整 SQL 語(yǔ)法的數(shù)據(jù)庫(kù)中,即使有少量修改也可以在 Model 層封裝掉。

語(yǔ)雀如何用JavaScript全棧打造商業(yè)級(jí)應(yīng)用

最終以一次文檔發(fā)布舉例,用戶(hù)通過(guò)調(diào)用 HTTP 接口與語(yǔ)雀進(jìn)行交互,數(shù)據(jù)會(huì)通過(guò) Model 層寫(xiě)入到存儲(chǔ)中,包括 MySQL 和 OSS,更新文檔緩存。同時(shí)出發(fā)異步消息給其他系統(tǒng),觸發(fā)釘釘?shù)?WebHook,并將數(shù)據(jù)同步到搜索引擎中。這些和外界系統(tǒng)的交互通過(guò)適配器封裝之后各司其職,參數(shù)轉(zhuǎn)換、權(quán)限校驗(yàn)、日志記錄,不僅確保核心邏輯的精簡(jiǎn),也讓系統(tǒng)調(diào)用鏈路跟蹤更加簡(jiǎn)單。

語(yǔ)雀如何用JavaScript全棧打造商業(yè)級(jí)應(yīng)用

混合應(yīng)用架構(gòu)

當(dāng)系統(tǒng)發(fā)展到一定程度后,到底是應(yīng)該繼續(xù)在大單體應(yīng)用上加功能,還是拆分成微服務(wù)呢?這兩種架構(gòu)既然存在,肯定有各自的優(yōu)劣,具體選擇那種架構(gòu)形式,應(yīng)該是與當(dāng)前的業(yè)務(wù)規(guī)模和團(tuán)隊(duì)分布決定的。所以語(yǔ)雀的技術(shù)架構(gòu)隨著語(yǔ)雀的業(yè)務(wù)形態(tài)也變成了一個(gè)混合式的技術(shù)架構(gòu)。

語(yǔ)雀如何用JavaScript全棧打造商業(yè)級(jí)應(yīng)用

語(yǔ)雀的主服務(wù)是一個(gè)大型的 Node.js 服務(wù),集中了所有的應(yīng)用業(yè)務(wù)邏輯。而在主服務(wù)之外,還有一些不同形態(tài)的其他服務(wù)。

  • 微服務(wù):一些獨(dú)立而穩(wěn)定的功能模塊,或者有額外部署架構(gòu)需求的服務(wù),會(huì)通過(guò)微服務(wù)的形式獨(dú)立部署,系統(tǒng)間暫時(shí)通過(guò) HTTP 接口進(jìn)行交互。例如實(shí)時(shí)協(xié)同服務(wù),由于其自身比較獨(dú)立穩(wěn)定,而且是長(zhǎng)連接服務(wù),不能頻繁發(fā)布重啟,所以將其部署成了一個(gè)獨(dú)立的微服務(wù)。

  • 任務(wù)集群:一些 CPU 密集型的任務(wù),或者依賴(lài)了一些復(fù)雜的第三方依賴(lài)的服務(wù),會(huì)放到一個(gè)獨(dú)立的任務(wù)集群中。例如各種文件預(yù)覽服務(wù),可能依賴(lài)到了其他服務(wù),且需要消耗大量計(jì)算成本,放到任務(wù)集群通過(guò)隊(duì)列消除并發(fā)后最為合適。

  • 函數(shù)計(jì)算:一些對(duì)響應(yīng)時(shí)間比較高且可以函數(shù)化的服務(wù),我們會(huì)盡量遷移到阿里云的函數(shù)計(jì)算,例如plantuml、mermaid 等文本繪圖服務(wù)。

語(yǔ)雀如何用JavaScript全棧打造商業(yè)級(jí)應(yīng)用

以 mermaid 的渲染舉例。用戶(hù)輸入一段 mermaid 代碼調(diào)用語(yǔ)雀,語(yǔ)雀調(diào)用一個(gè)部署在阿里云函數(shù)計(jì)算的函數(shù),在函數(shù)中運(yùn)行 puppeteer 渲染成 svg 返回。

語(yǔ)雀如何用JavaScript全棧打造商業(yè)級(jí)應(yīng)用

為什么要特別把 Serverless 單獨(dú)拿出來(lái)說(shuō)呢?還記得之前說(shuō) Node.js 是單線(xiàn)程,不適合 CPU 密集型任務(wù)么?由于 Serverless 的出現(xiàn),我們可以將這些存在安全風(fēng)險(xiǎn)的,消耗大量 CPU 計(jì)算的任務(wù)都遷移到函數(shù)計(jì)算上。它運(yùn)行在沙箱環(huán)境中,不用擔(dān)心用戶(hù)的惡意代碼造成安全風(fēng)險(xiǎn),同時(shí)將這些 CPU 密集型的任務(wù)從主服務(wù)中剝離,避免出現(xiàn)并發(fā)時(shí)阻塞主服務(wù)。按需付費(fèi)的方式也可以大大節(jié)約成本,不需要為低頻功能場(chǎng)景部署一個(gè)常駐服務(wù)。所以我們會(huì)盡量的把這類(lèi)服務(wù)都遷移到 Serverless 上(如阿里云函數(shù)計(jì)算)。

語(yǔ)言之外的通用領(lǐng)域

除了語(yǔ)言之外,任何的商業(yè)化系統(tǒng)還有更多需要考慮的方面,其中最重要的兩點(diǎn)可能就是安全性和穩(wěn)定性了。 語(yǔ)雀如何用JavaScript全棧打造商業(yè)級(jí)應(yīng)用

一個(gè)系統(tǒng)從前端、服務(wù)端到底層的依賴(lài)都存在著各種各樣的安全風(fēng)險(xiǎn):

  • 前端安全風(fēng)險(xiǎn):XSS、跳轉(zhuǎn)釣魚(yú)、跨站請(qǐng)求等

  • 服務(wù)端安全風(fēng)險(xiǎn):水平權(quán)限問(wèn)題、未授權(quán)訪問(wèn)、敏感信息泄露、SSRF、SQL 注入等

  • 云服務(wù)的安全風(fēng)險(xiǎn):短信/郵件轟炸、數(shù)據(jù)泄露風(fēng)險(xiǎn)、內(nèi)容安全等

這些安全問(wèn)題想要解決基本都沒(méi)有銀彈,只能一個(gè)個(gè)單獨(dú)處理,但是有一些基本的原則:

  • 不要信任用戶(hù)的任何輸入

    • 任何渲染富文本的地方都需要防范 XSS,內(nèi)容也可能并不是通過(guò) IDE 輸入的;

    • 要在服務(wù)端執(zhí)行用戶(hù)的代碼一定要放在沙箱中;

    • 要從服務(wù)端請(qǐng)求用戶(hù)傳遞的資源,一定要經(jīng)過(guò) SSRF 過(guò)濾;

  • 沉淀標(biāo)準(zhǔn)的編碼范式來(lái)處理安全風(fēng)險(xiǎn),且需要在 Code Review 中重點(diǎn)關(guān)注

    • 所有接口都必須有權(quán)限校驗(yàn);

    • 響應(yīng)序列化方法過(guò)濾敏感信息;

    • 不允許拼接 SQL;

語(yǔ)雀從商業(yè)化一開(kāi)始就和安全團(tuán)隊(duì)通力協(xié)作,從內(nèi)部的安全意識(shí)培訓(xùn)、內(nèi)部安全團(tuán)隊(duì)測(cè)試,到內(nèi)部的紅藍(lán)攻防、外部的白帽子滲透測(cè)試,安全是一場(chǎng)持久戰(zhàn)。

語(yǔ)雀如何用JavaScript全棧打造商業(yè)級(jí)應(yīng)用

為了保障語(yǔ)雀的穩(wěn)定性,我們從前端到服務(wù)端和云服務(wù)上都做了許多工作,和安全一樣,穩(wěn)定性也是一個(gè)從前到后的長(zhǎng)期工程。語(yǔ)雀的穩(wěn)定性保障主要在兩個(gè)維度:

  • 保障服務(wù)可用性:從架構(gòu)設(shè)計(jì)上要杜絕單點(diǎn),底層的數(shù)據(jù)都需要進(jìn)行容災(zāi)和備份,服務(wù)需要多單元、可用區(qū)部署。同時(shí)避免引入不必要的強(qiáng)依賴(lài);

  • 異??杀O(jiān)控和追溯:從前端的業(yè)務(wù)埋點(diǎn)日志、異常日志監(jiān)控,到服務(wù)端的全鏈路日志跟蹤和采集,系統(tǒng)性能監(jiān)控和分析。最終我們可以達(dá)到異常可及時(shí)感知和追溯,性能問(wèn)題可以定位分析;

什么叫做避免引入不必要的強(qiáng)依賴(lài)呢?以語(yǔ)雀的場(chǎng)景舉例,MySQL 就是一個(gè)無(wú)法去除的強(qiáng)依賴(lài),而緩存不應(yīng)該是一個(gè)強(qiáng)依賴(lài),但是最早語(yǔ)雀的 session 是存儲(chǔ)在緩存(Redis)中的,一旦 Redis 集群出問(wèn)題,用戶(hù)資料無(wú)法獲取就導(dǎo)致用戶(hù)無(wú)法登錄。這就把緩存變成了一個(gè)強(qiáng)依賴(lài)。所以我們將 session 存儲(chǔ)放到了 MySQL 中,Redis 就變成了一個(gè)弱依賴(lài),它掛了系統(tǒng)還能正常運(yùn)行。另一個(gè)例子,語(yǔ)雀前段時(shí)間上線(xiàn)了多人實(shí)時(shí)協(xié)同編輯的功能,而在這個(gè)功能上線(xiàn)之前,是通過(guò)文檔加鎖的方式避免多個(gè)人同時(shí)編輯同一篇文檔的。然而多人實(shí)時(shí)協(xié)同引入了另一個(gè)服務(wù),一旦實(shí)時(shí)協(xié)同服務(wù)掛了,用戶(hù)就無(wú)法編輯文檔了,它又變成了語(yǔ)雀系統(tǒng)的一個(gè)強(qiáng)依賴(lài),為了解決他,我們?cè)谟脩?hù)連接協(xié)同服務(wù)失敗的時(shí)候,自動(dòng)切換到老的鎖模式。這樣協(xié)同服務(wù)也變成了語(yǔ)雀的一個(gè)弱依賴(lài)。

語(yǔ)雀如何選擇技術(shù)棧

語(yǔ)雀如何用JavaScript全棧打造商業(yè)級(jí)應(yīng)用

語(yǔ)雀這幾年一步步發(fā)展過(guò)來(lái),背后的技術(shù)一直在演進(jìn),但是始終遵循了幾條原則:

  1. 技術(shù)棧選型要匹配產(chǎn)品發(fā)展階段。產(chǎn)品在不同的階段對(duì)技術(shù)提出的要求是不一樣的,越前期,對(duì)迭代效率的要求越高,商業(yè)化規(guī)?;?,對(duì)穩(wěn)定性、性能的要求就會(huì)變高。不需要一上來(lái)就用最先進(jìn)的技術(shù)方案,而是需要和產(chǎn)品階段一起考慮和權(quán)衡。

  2. 技術(shù)棧選型要結(jié)合團(tuán)隊(duì)成員的技術(shù)背景。語(yǔ)雀選擇 JavaScript 全棧的原因是孵化語(yǔ)雀的團(tuán)隊(duì),大部分都是 JavaScript 背景的程序員,同時(shí) Node.js 在螞蟻也算是一等公民,配套的設(shè)施相對(duì)完善。

  3. 最重要的一點(diǎn)是,不論選擇什么技術(shù)棧,安全、穩(wěn)定、可維護(hù)(擴(kuò)展)都是要考慮清楚的。用什么語(yǔ)言、用什么服務(wù)會(huì)變化,但是這些基礎(chǔ)的安全意識(shí)、穩(wěn)定性意識(shí),如何編寫(xiě)可維護(hù)的代碼,都是決定項(xiàng)目能否長(zhǎng)期發(fā)展下去的重要因素。

“阿里巴巴云原生關(guān)注微服務(wù)、Serverless、容器、Service Mesh 等技術(shù)領(lǐng)域、聚焦云原生流行技術(shù)趨勢(shì)、云原生大規(guī)模的落地實(shí)踐,做最懂云原生開(kāi)發(fā)者的技術(shù)圈?!?/p>

上述內(nèi)容就是語(yǔ)雀如何用JavaScript全棧打造商業(yè)級(jí)應(yīng)用,你們學(xué)到知識(shí)或技能了嗎?如果還想學(xué)到更多技能或者豐富自己的知識(shí)儲(chǔ)備,歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI