Cloudflare為cdnjs提供支持,這個開源項目通過利用Cloudflare的網(wǎng)絡提供流行的JavaScript庫和資源,從而給網(wǎng)站加速。自12月發(fā)布重要更新以來,我們專注于改造cdnjs以實現(xiàn)可擴展性和彈性。今天,我們很高興宣布Cloudflare如何利用Cloudflare Workers以及它的鍵值存儲Workers KV來交付cdnjs(遷移到無服務器基礎架構(gòu))!
什么是cdnjs?
對于感覺陌生的人來說,cdnjs是一個描述面向JavaScript(JS)的內(nèi)容交付網(wǎng)絡(CDN)的首字母縮寫詞。CDN是一個由地理上分散的服務器構(gòu)成的網(wǎng)絡,主要用于提供互聯(lián)網(wǎng)內(nèi)容,不論這些內(nèi)容是回憶、小貓視頻還是HTML網(wǎng)頁。而本文所說的CDN是指Cloudflare那個由200多個分布于全球的數(shù)據(jù)中心構(gòu)成并且依然在不斷壯大的網(wǎng)絡。
這與您相關(guān)的原因在于:它使頁面加載時間快如閃電。您訪問的每個網(wǎng)站基本上都需要獲取JS庫才能加載,也包括本網(wǎng)站。假設您要一個位于悉尼的網(wǎng)站,它包含一個來自jQuery的本地文件。jQuery是一個流行的庫,可見于76.2%的網(wǎng)站。如果您位于紐約,那么可能會注意到一個延遲,因為獲取這個文件所需的時間輕易會超過300ms,更不用說TLS握手涉及的往返時間了。不過,如果該網(wǎng)站使用cdnjs.cloudflare.com來引用jQuery,您可以從Cloudflare距您最近的布法羅數(shù)據(jù)中心檢索這個文件,將延遲縮短到閃電般的20ms。
盡管cdnjs在幕后運作,但有超過11%的網(wǎng)站使用它,使互聯(lián)網(wǎng)變得更加快速、更加可靠。在7月份,cdnjs服務了將近1900億個請求,涉及的數(shù)據(jù)量達到了3.46PB。
文件存儲在哪里?
cdnjs加快了互聯(lián)網(wǎng)速度,憑借的當然不是魔法!
在過去,Cloudflare某個核心數(shù)據(jù)中心的若干負載均衡服務器會定期從后備存儲中提取cdnjs文件,充當cdnjs.cloudflare.com的源站。有人請求了新文件時,Cloudflare會將它緩存,從而能夠從我們的任何數(shù)據(jù)中心快速獲取到這個文件。
后備存儲是JS、CSS和其他Web庫的目錄,采用開源GitHub存儲庫的形式。這意味著,包括您在內(nèi)的任何人都可以出力貢獻,但要經(jīng)過審核和其他流程。
然而,到最近為止,這些現(xiàn)有操作還是勞動密集型的,并且比較脆弱。
這篇博客帖子將解釋為什么我們改變cdnjs背后的基礎架構(gòu),以使其更快速、更可靠,并且更容易維護。首先,我們將討論社區(qū)過去如何為cdnjs做出貢獻,同時概述舊系統(tǒng)的痛處和問題。接著,我們將探討遷移到Workers KV的好處。然后,我們將深入解析新架構(gòu),以及對網(wǎng)站和cdnjs API的升級。最后,我們將回顧cdnjs的歷史,并且展望它的未來。
如果認為自己知道如何提交PR,請再思考
對于非技術(shù)讀者,拉取請求(PR)是一種用來合并您對存儲庫所做更改的請求。往常的話,如果您想將自己的JavaScript庫包含到cdnjs中,首先要在GitHub上創(chuàng)建對cdnjs/cdnjs的PR,附上一個描述您的程序包的JSON文件以及您想包括的任何版本的其他文件。在您的PR獲得我們舊版機器人的批準,通過了人工審核,并且被維護人員合并后,您的程序包就與cdnjs集成在一起了。
貌似很簡單,對吧?只要復刻存儲庫,克隆一下并復制粘貼幾個文件,不是嗎?
沒錯。如果您有幾個小時的時間來刻錄一個區(qū)分大小寫的文件系統(tǒng),并且有幾百GB的可用磁盤空間來git clone 300GB的存儲庫,那么貢獻很簡單。時間不夠也沒問題,您隨時可以利用高階的git sparse-checkout知識來完成這項工作。不懂git?只需通過GitHub的UI逐個添加文件便可。
我猜您想到點上了。我當然知道,我可是天真地花了10個小時克隆存儲庫,結(jié)果卻發(fā)現(xiàn)macOS默認為不區(qū)分大小寫。
但是,更新cdnjs不僅對貢獻者來說很困難,對維護人員來說也不容易。在過去,社區(qū)能夠直接貢獻版本文件,這些文件有可能是惡意的。這給維護人員帶來許多工作,他們需要手動檢視每個文件,與官方庫源文件進行比較,并運行惡意軟件檢查。
那么,程序包在cdnjs中后如何更新?在描述每個程序包的JSON文件中,有一個可選的自動更新定義,它會告訴機器人在哪里尋找?guī)斓男掳姹?。如果存在這個定義,那么您的程序包從npm或GitHub發(fā)布新版本時,機器人會下載新版文件,并推送至cdnjs/cdnjs,同時將計算的子資源完整性(SRI)哈希推送到cdnjs/SRIs。如果缺少自動更新屬性,那么您要負責手動提交PR,從而用任何新的版本更新cdnjs。
cdnjs的警示
今年4月,在維護我們一個核心數(shù)據(jù)中心的過程中,技術(shù)人員不慎斷開了一些電纜,它們提供了連到我們其他數(shù)據(jù)中心的所有外部連接,因此導致該數(shù)據(jù)中心離線約四個小時。這個事故是向cdnjs發(fā)出的第一個警示,特別是因為受影響的數(shù)據(jù)中心承載了主要的cdnjs源站W(wǎng)eb服務器。在這一事件中,我們確實有在外部提供商那里運行的備份,但真正救了我們的是Cloudflare的全球緩存,它最大程度地減少了停機的影響,只有未緩存的資產(chǎn)才無法加載。
我們開始思考如何才能改進cdnjs服務的可靠性和性能。目光直接投向了Cloudflare Workers這個我們自己為邊緣網(wǎng)絡上開發(fā)提供的平臺。Workers內(nèi)置了功能強大的工具Workers KV,它是一個針對高讀取應用程序進行了優(yōu)化的低延遲、全球分布的鍵值存儲。
我們通過推理發(fā)現(xiàn),不用拉取cdnjs/cdnjs存儲庫并從磁盤提供文件,而可以徹底告別物理服務器,將數(shù)據(jù)分布到全球并直接從邊緣提供文件。這樣,cdnjs將能夠從任何原始數(shù)據(jù)中心故障中恢復,同時還可以提高其可擴展性。
Workers KV前來拯救
乍一看,決定使用Workers KV是件明擺著的事。既然cdnjs中的文件永不更改,但需要頻繁讀取,那么Workers KV就非常適合。
但是,在計劃遷移時我們開始擔心,cdnjs中的資產(chǎn)數(shù)量有700萬多,無疑會存在超過Workers KV 10MiB限值的文件。經(jīng)過調(diào)查,我們發(fā)現(xiàn)數(shù)百個cdnjs文件大小超限,其中大多數(shù)是JavaScript源映射。
后來,我們想到了一個主意。將Cdnjs文件的壓縮版本存儲在Workers KV中,這不僅能解決文件大小超限問題,還可優(yōu)化文件的服務方式。
如果您支付過互聯(lián)網(wǎng)賬單,就會知道帶寬有多么昂貴!因此,所有現(xiàn)代瀏覽器都會盡可能獲取壓縮的Web內(nèi)容。同樣在Cloudflare,我們經(jīng)常試驗通過即時壓縮來減小我們的帶寬,只要被接受,我們就將壓縮后的內(nèi)容提供給瀏覽者。結(jié)果,我們決定提前壓縮所有cdnjs文件,并將它們以最佳的Brotli和gzip形式寫入Workers KV。這樣,我們可以使用比即時壓縮時更高的壓縮級別,因為不再有延遲方面的要求。
如此一來,我們可以更快的速度提供更小的cdnjs文件!
全面改造cdnjs
現(xiàn)在,如果您要將自己的JavaScript庫包含在cdnjs中,首先在GitHub上創(chuàng)建一個對我們新存儲庫cdnjs/packages的PR。這個存儲庫很容易克隆,大小僅為50MB,由數(shù)千個JSON文件構(gòu)成,各自描述一個cdnjs程序包并說明如何從npm或git自動更新。當您的文件獲得我們新版自動CI的驗證(由新版機器人提供支持),并由維護人員進行合并后,您的程序包就會自動登記到我們的自動更新服務中。
新系統(tǒng)優(yōu)先考慮了安全性和可維護性。首先,cdnjs版本文件由我們的機器人創(chuàng)建,最大程度降低了合并新版本時出現(xiàn)人為錯誤的可能。雖然JSON文件是由易錯的人類添加到cdnjs/packages中的,但會經(jīng)過機器人的檢查,然后再由維護人員審批。每個文件都會對照JSON架構(gòu)自動驗證,另外也會檢查在npm或GitHub上流行程度。
當機器人發(fā)現(xiàn)新版本時,它會將文件的Brotli和gzip壓縮版本推送到Workers KV中的文件命名空間。對于每個條目,機器人會在Workers KV中寫入一些元數(shù)據(jù),用于ETag和Last-Modified HTTP標頭。與之前類似,機器人也會計算未壓縮文件的子資源完整性(SRI)哈希,但現(xiàn)在將它們推送到Workers KV中的SRI命名空間。
然后,有人從cdnjs.cloudflare.com請求新文件時,Cloudflare Worker會檢查客戶端的Accept-Encoding標頭,并從Workers KV中獲取Brotli或gzip壓縮版本及其ETag和Last-Modified元數(shù)據(jù)。當壓縮文件通過Cloudflare返回時,它將緩存下來以備日后請求時使用,同時也根據(jù)需要即時解壓縮。
目前,仍有少數(shù)文件超過了Workers KV的大小限制。因此,如果Cloudflare Worker未能從Workers KV檢索到文件,它會從原始git存儲庫支持的源站中獲取文件。在接下來幾個月中,我們計劃逐步移除此基礎架構(gòu)。
擴展網(wǎng)站和API
除了核心cdnjs基礎架構(gòu)之外,它的許多其他組件也得到了升級!
在cdnjs項目的主頁上,您會看到一個漂亮的新Beta網(wǎng)站。這個Beta網(wǎng)站由Matt開發(fā),采用了Vue和Nuxt,全部通過cdnjs API提供支持。因此,它始終會更新最新的程序包信息,而且為網(wǎng)站服務時需要使用的資源也較少(加載第一頁之后完全在客戶端一側(cè)運行),這有助于我們隨著cdnjs的不斷成長而擴展。
實際上,cdnjs API也從采用無服務器架構(gòu)中獲益,加強了可擴展性,這個架構(gòu)與我們在cdnjs和Workers KV中看到的非常接近。
在遷移到Workers KV之前,cdnjs API依賴于一個定期計劃的流程,這個流程會生成大約300MB元數(shù)據(jù)。然后,cdnjs API的后端會將這個巨大的“package.min.js”文件提取到內(nèi)存中,并使用它來運行該API。如果您好奇的話,該文件仍托管在此處,但請注意,它可能會拖累您的瀏覽器!同樣,文件SRI推送到了cdnjs/SRIs,由API在本地克隆以服務SRI響應。
在所有cdnjs文件(在允許的大小限制內(nèi))轉(zhuǎn)移到Workers KV之后,這些舊流程變得不可持續(xù),因為需要數(shù)百萬次讀取和不合理的時間。因此,我們決定將找到的所有元數(shù)據(jù)上傳到Workers KV中。我們將元數(shù)據(jù)分拆到四個名稱空間中:一個用于程序包級元數(shù)據(jù),一個用于版本特定元數(shù)據(jù),一個包含聚合元數(shù)據(jù),另一個則用于文件SRI。
與cdnjs的無服務器設計類似,Cloudflare Worker駐留在metadata.speedcdnjs.com上,使用多個公共端點來提供Workers KV中的數(shù)據(jù)。目前,cdnjs API已經(jīng)和這些端點完全集成,能夠隨著cdnjs的繼續(xù)擴展而提供優(yōu)雅的解決方案。
公開透明和cdnjs的未來
自2011年1月誕生以來,cdnjs一直深深扎根于公開透明,從社區(qū)汲取力量。即使cdnjs在規(guī)模上暴增,并且其創(chuàng)始人Ryan Kirkman和Thomas Davis與我們在2011年6月確立合作關(guān)系后,這個項目依然在GitHub保持完全開源。
隨著時間流逝,創(chuàng)始人越來越難以保持活躍,因此這個項目重度依賴于社區(qū)的支持。由于幾乎沒有任何預算,對存儲庫的訪問也極少,核心cdnjs維護人員每天都面臨著項目存續(xù)的挑戰(zhàn)。
去年,這樣的局面促使我們聯(lián)系了創(chuàng)始人,他們愉快地接受了我們對項目的協(xié)助。隨著Cloudflare發(fā)揮更大的作用,cdnjs變得與往常一樣穩(wěn)定,擁有來自Cloudflare和社區(qū)的多位活躍成員。
但是,由于我們不再依賴舊系統(tǒng),而且也將文件存儲到了Workers KV中,人們開始擔心cdnjs日后會轉(zhuǎn)變?yōu)閷S行再|(zhì)。不用擔心,我們正在努力確保cdnjs保持盡可能透明和開源。為了幫助社區(qū)審核對Workers KV的更新,我們建立了一個新的存儲庫cdnjs/logs,供機器人用于記錄所有與Worker KV相關(guān)的事件。此外,任何人都可通過從cdnjs API提取SRI來驗證cdnjs文件的完整性。
結(jié)論
總體而言,去年對cdnjs來說是一個動蕩的時期,但所有缺點成為了幫助我們構(gòu)建更好系統(tǒng)的警示信號。最近,我們將cdnjs遷移到了無服務器基礎架構(gòu),并將它的文件存儲在Workers KV中,緩解了依賴于單一位置上物理服務器的風險。
今天,cdnjs運作良好,不會在短期內(nèi)消失。在此,我們向維護人員Sven和Matt表示感謝,謝謝他們給予這個項目巨大的力量,處理從擴展cdnjs到編輯這篇帖文等一切工作。
展望未來,我們會致力于使cdnjs盡可能公開透明。在繼續(xù)改進cdnjs的過程中,我們也會發(fā)表更多帖文,讓社區(qū)掌握最新的消息。如果您有興趣,請訂閱我們的博客。畢竟,是社區(qū)造就了cdnjs!特別感謝我們活躍的GitHub貢獻者和cdnjs社區(qū)論壇成員,感謝你們一直與我們同甘共苦!