javascript
CDN边缘JavaScript敏捷交付实践
本文由百度智能云-視頻云-內容分發加速技術架構師——高巖 在百度開發者沙龍線上分享的演講內容整理而成。內容從CDN應用Serverless的意義出發,詳細介紹EdgeJS Serverless服務。
文/ 高巖
整理/ 百度開發者中心
視頻回放:https://developer.baidu.com/live.html?id=11
本次分享的主題是:CDN邊緣JavaScript敏捷交付實踐,內容主要分為以下三個方面:
- CDN應用Serverless的意義
- EdgeJS Severless服務
- 沉浸式CDN編程體驗
01 CDN應用Serverless的意義
CDN基本介紹
CDN含義:
是指一組分布在不同地理位置的服務器,協同工作以提供互聯網內容的快速交付。
CDN服務已得到不斷普及。如今,大多數web流量都通過CDN提供服務,幾乎所有的門戶網站、常用的視頻 APP(例如,愛奇藝、抖音)都會用 CDN 架構實現更加快速的內容分發,讓用戶更快地看到視頻內容,帶來更好的用戶體驗。
這是因為 CDN 允許快速傳輸、加載互聯網內容所需要的資源。以門戶網站為例,我們需要加載 HTML 頁面、JavaScript、文件 css 等資源;而視頻網站則需要加載縮略圖、視頻文件。
CDN 還可幫助保護網站免受某些常見的惡意攻擊,例如分布式拒絕服務(DDOS)攻擊。
使用CDN優勢:
縮短網站加載時間
通過將內容分發到訪問者附近的CDN服務器(以及其他優化措施),訪問者體驗到更快的頁面加載時間。由于訪問者更傾向于離開加載緩慢的網站,CDN 可以降低跳出率并增加人們在該網站上停留的時間。換句話說,網站速度越快,用戶停留的時間越長。
減少帶寬成本
網站托管的帶寬消耗成本是網站的主要費用。通過緩存和其他優化,CDN 能夠減少源服務器必須提供的數據量,從而降低網站所有者的托管成本。
增加內容可用性和冗余
大流量或硬件故障可能會擾亂正常的網站功能。由于CDN具有分布式特性,因此與許多源服務器相比,CDN 可以處理更多流量并更好地承受硬件故障。
改善網站安全性
CDN 可以通過提供鑒權、安全證書的改進以及其他優化措施來提高安全性。
vCDN的發展
早在2009年,伯克利曾針對當時興起的云計算做過評論,并提出了以下6個潛在的優點:
基于云計算的理念,可以實現一個虛擬化CDN(vCDN), 即可在專有、裸金屬、虛擬化或基于容器的基礎設施上運行CDN。vCDN作為云上的一個應用,是CDN和云緊密結合的產品。其主要功能特性包括:
硬件虛擬化:虛擬化基礎架構使軟件和硬件功能得以分解,服務器運維大大簡化。
低延遲:在共享基礎設施上運行CDN功能,可以更快的調起其他應用,比如可以實時進行圖片處理。
彈性伸縮:可以按需使用CDN,在流量高峰和低峰,進行自動的彈性擴容和縮容。
但是云計算技術發展到今天,虛擬化并不能解決所有的問題,
在對性能有特別高要求的場景下,面臨的主要難點如下:
- 虛擬機/容器,構建業務應用運維成本較高。
- 不能做到按需付費,仍然需要獨占虛擬機。
- 開發復雜,需要很多其它的依賴,在開發業務的過程中,需要數據庫、對象存儲等框架。需要自己掌控運維和使用情況,開發難度較高。
Serverless介紹與特點
為了解決上述問題,亞馬遜的 AWS 在 2015 年推出了 lambda 服務,提出了Cloud Function的概念,引起了業界對于 Serverless 的關注廣泛。
Serverless 主要包含 Faas、Baas 兩種形態。其中,Faas 將 Function 作為服務,Baas 將后端服務作為服務。
在應用了 vCDN 后,也可以用 Baas 的方式運行服務。Serverless 旨在讓開發人員不需要再關注服務器,云會幫自動實現服務器的運維和伸縮。
具體而言,Serverless 具有以下特點:
CDN應用Serverless的意義
早在 2018年,云管理公司 RightScale 開展的一項調查顯示,Serverless 是增長最快的公共云服務。
據統計,AWS 上超過一半的用戶已經在使用 Serverless 服務。Serverless 一直在高速發展,呈現出越來越大的影響力。
Serverless 將無處不在,CDN也必須擁抱Serverless理念,提供邊緣可編程能力,使用戶可以在控制臺上通過 API 設置代碼,形成編程能力,更有效地控制 CDN。
在 CDN 業務中實踐 Serverless 理念可以提供敏捷交付的能力,具有以下優勢:
- 編程能力 對于剛接觸CDN的客戶來說,可編程能力,即便不理解CDN的具體運作流程,也能快速編寫出可用的代碼。
- 敏捷交付 對于CDN研發人員,serverless節省了他們部署和運維的時間,讓他們能夠更加專注于解決和優化應用本身的問題。
- 場景下沉結合編程能力,可以和其他場景更方便的結合在一起。
- 邊緣計算在CDN的邊緣,可以進行更加自由的計算,節省端上處理的時間和延遲。
02 EdgeJS Serverless服務
EdgeJS Serverless服務目標
Serverless 的目標是使用戶可以更容易地編寫和部署代碼,而無需關注底層結構。盡管目前的 CDN 業務可以實現按需付費,但是仍然不夠靈活。
為此,我們推出了 EdgeJS Serverless 服務。該服務在百度智能云 CDN 上使用JavaScript 語言去提供的一種可編程的配置能力,實現高并發、低成本的敏捷交付,使 CDN 能夠體現出 serverless 的思想,進行靠攏或者是計劃。
該服務具有以下特點:
- 嵌入式 JavaScript runtime,而非獨立的 runtime。出于對性能的考慮,在支持 JS 標準庫的同事,避免了獨立runtime 帶來的冷啟動時間。
- 提供請求對象 request,可以在代碼中進行隨意更改。
- 提供對外訪問能力。
作為一種 Serverless 服務,EdgeJS 需要實現用戶隔離、具備較高的性能,能夠動態編碼。具體而言,EdgeJS 具有以下特性:
EdgeJS Serverless服務的特性
EdgeJS致力于讓CDN更易用,向serverless服務能力邁進。所以EdgeJS的定位一開始就是完全免費,在CDN按需使用的帶寬費用之外,不會產生額外的費用。
在降低CDN使用門檻的初衷下,必須還得保持CDN的高可用、低延遲、就近服務的能力。
EdgeJS的設計,是完全嵌入到CDN接入層的JavaScriptruntime,無需冷啟動時間,沒有性能損失,并支持標準ES6語法的JavaScriptAPI,各種特性陸續補齊中。
而且相比傳統交付,開發上線至少周級別的交付周期,EdgeJS真正能做到秒級交付,用戶在控制臺配置上代碼,就可以秒級生效。當然建議在正式發布前,先使用預發布功能來灰度驗證。EdgeJS,可以根據請求進行各種特征處理,這極大豐富了CDN接入的場景。
為了更好地配合 CDN 業務,EdgeJS 具有以下特性:
- 使用 EdgeJS 在邊緣進行計算(如一些特殊的鑒權等不能緩存的動態需求),將預計算任務部署在邊緣設備上,大大減輕源站的壓力
- 做到秒級交付,使用戶在控制臺上配置代碼,秒級生效。
- 根據請求進行各種特征處理,極大豐富 CDN接入場景(包括不限于跨域訪問、重定向、訪問控制、單請求限速、自定義鑒權、m3u8改寫、請求改寫、A/BTest自定義錯誤頁面等。
除此之外,EdgeJS還可以利用fetch等能力,和遠端進行協同,包括不限于遠程鑒權、云服務協同和請求畫像打點上傳。這些能力已經遠超CDN的傳統場景,向serverless服務能力靠攏。
在CDN龐大的算力加持下,可以減輕源站的性能壓力和支出。而且,當增加了新的特征或者增加了新的計算方法,可以隨時修改JavaScript代碼,進行實時控制。
03 沉浸式CDN編程
url改寫與復雜文件名改寫
CDN 控制臺本身支持一些 URL 改寫的基本功能,但這些預定義的功能靈活性較低。我們可以通過 EdgeJS 根據用戶的要求確定配置。EdgeJS 使用標準的 JS 語法,需要用戶建立一個請求對象 request。
如上圖所示,我們首先將 URL 中的大寫字母轉成小寫,接著我們將請求的 variables 參數改成小寫。variable 映射的是 UNIX 的變量,而這的規則完全一致的。
EdgeJS 支持復雜的文件名改寫,這里涉及到三種情況:
(1)參數 attname 存在且不為空字符串
(2)參數 attname 存在且為空字符串
(3)參數 attname 不存在。
回源鑒權頭
在一些對象存儲場景下,我們可以通過 EdgeJs 構建回源鑒權頭 authorization。首先生成一個隨機數,獲取當前時間,并生成請求 URL。接著,我們利用以上三者根據 crypto 算法生成鑒權頭。
我們可以通過請求的 headersIn 特性獲取請求頭。有些特殊的請求頭只能存在一份,如果重復則會被忽略(例如,host、connection,詳見官網)。此外,重復的 Cookie 的請求頭會返回所有的重復部分,并以分號分隔開來。如果我們想要獲取所有的請求頭,我們需要使用 rawHeadersIn 特性,如果請求頭有多個,則會輸出數組。
r.headersIn{}
請求頭對象,可寫Foo請求頭可以使用r.headersIn.foo或者r.headersIn[‘Foo’]來訪問
“Host”, “Connection”, “If-Modified-Since”, “If-Unmodified-Since”, “If-Match”, “If-None-Match”, “User-Agent”, “Referer”,
“Content-Length”, “Content-Range”, “Content-Type”, “Range”, “If-Range”, “Transfer-Encoding”, “TE”, “Expect”,
“Upgrade”, “Accept-Encoding”, “Via”, “Authorization”, “Keep-Alive”, “X-Real-IP”, “Accept”, “Accept-Language”, “Depth”,
“Destination”, “Overwrite”, "Date"這些請求頭只能有一個,重復的會被忽略
重復的“Cookie”請求頭,會返回所有的重復部分,并以分號(;)分隔,
重復的其他請求頭,會返回所有的重復部分,并以逗號(,)分隔,
r.headersIn.foo =‘foo’,賦值會覆蓋所有的重復部分。
r.headersIn [‘Foo’]= [‘a’, ‘b’],賦值數組,會產生兩個重復的請求頭:
Foo:a和Foo:b
r.rawHeadersIn{}
請求頭KV Array,只讀。
比如請求頭Host:localhost;Foo: bar ;foo: bar2
r.rawHeadersIn輸出類似于[‘Host’, ‘localhost’], [‘Foo’, ‘bar’], [‘foo’, ‘bar2’]
獲取所有的請求頭foor.rawHeadersIn.filter(v=>v[0].toLowerCase()
== ‘foo’).map(v=>v[1])
輸出[‘bar’, ‘bar2’]
文件名改寫
EdgeJS Serverless 服務通過請求的 headersOut 特性可以實現文件名改寫、跨域訪問、設置相同的響應頭等功能。
如上圖所示,我們可以使用請求參數 filename 命名下載文件,使用請求頭 Origin 賦值給跨域響應頭 Access-Control-A。在設置相同的響應頭時,我們可以通過賦值數組,產生重復的響應頭。
r.headersOut{}響應頭對象,可寫Foo響應頭可以使用r.headersOut.foo或者r.headersOut[‘Foo’]來訪問。
“Server”, “Date”, “Content-Length”, “Content-Encoding”,
“Location”, “Refresh”, “Last-Modified”,
“Content-Range”, “Accept-Ranges”,
“WWW-Authenticate”, “Expires”, “E-Tag”, “ETag”, “Content-Type”, “X-Override-Charset”,
“Cache-Control”, “Link”, “Age”,
“Retry-After”,這些響應頭只能有一個,重復的會被忽略
重復的"Set-Cookie"響應頭,會返回一個數組,例如,r.headersOut[‘Set-Cookie’].forEach
(element=> console.log(element));
重復的其他響應頭,會返回所有的重復部分,并以逗號(,)分隔
r.headersOut.foo =‘foo’,賦值會覆蓋所有的重復部分。
r.headersOut [‘Foo’]= [‘a’, ‘b’],賦值數組,會產生兩個重復的響應頭:
Foo:a和Foo:b
r.rawHeadersOut{}
響應頭KV Array,只讀
用法類似于r.rawHeadersIn{}
自定義錯誤頁面
我們可以使用 EdgeJS 實現自定義的錯誤頁面,當源站返回 404 時,可以重定向到一個對用戶友好的頁面。
如上圖所示,我們通過 respHeader 回調實現上述功能。類似地,我們可以通過 respHeader 實現 A/B 測試,
如果源站返回了特殊頭 a,可以命中一個升級的邏輯,重定向到一個應用升級的頁面。
IP黑名單
EdgeJS 支持通過百度自有的庫提供一些常用的訪問控制功能:
(1)IP 黑名單,如果客戶端地址在 192.168.1.1/32 或 192.168.2.1/24 等ip段內,則返回 403。
(2)Referer 白名單,如果 referer 不匹配某些通配符的形式,則返回 403。
(3)UA 黑名單,如果 UA 包含 curl 或 AppleWebKit,則返回 403。
代碼示例:
r.remoteAddress
客戶端地址,只讀
baidu_utils庫
function ipInCidr(ipv4,cidrs)
參數:
ipv4為點分十進制的ipv4地址,比如’192.168.2.100’
cidrs為CIDR地址列表,比如[‘192.168.1.1/32’,‘192.168.2.1/24’]
使用示例:
if (baidu_utils.ipInCidr (‘192.168.2.100’,[‘192.168.1.1/32’,‘192.168.2.1/24’])) {
r.return(403);
}
function matchWildcard(str,rule)
參數:
str為待匹配的字符串,比如’http://www.baidu.com/’
rule為有通配符的字符串,比如’http://.baidu.com/’
使用示例:
if (baidu_utils.matchWildcard(‘http://www.baidu.com/’, ‘http://.baidu.com/’)
{
r.return(403);
}
鑒權
百度云基于 EdgeJS 提供了 B 類防盜鏈等鑒權功能。原始的 URL 包含協議頭 HTTP、域名、文件名。在加密之后,URL 變成了協議頭、域名、時間戳、MD5 編碼,文件名。
在上圖的第一段代碼中,CDN 服務器收到請求之后,首先拆分 URL,得到時間戳、MD5 、文件名。
在第二段代碼中,時間戳的格式并非 Unicode,而是可讀的格式,我們需要進行時間戳格式的轉換,將秘鑰與時間戳、文件名拼接,并進行 MD5 加密編碼的比對。
子請求
CDN本身是一個緩存體系,對于hls或者dash來說,其索引文件中包含的防盜鏈信息,在用戶請求的時候很可能過期了,所以在用戶請求的m3u8或者mpd的防盜鏈驗證通過之后,需要將這個請求中的防盜鏈信息改寫到索引文件內容中。
Fetch
Fetch
ngx.fetch(url, [options])
類似于JavaScript原生的Fetch,請求URL,并返回解析Response對象的Promise。參考Using_Fetch
目前僅支持http協議,重定向需要調用者處理
類似于js fetch,options支持body/ headers / method。
ssl選項如下:
ssl_name 指定sni,默認為url中的域名
ssl_verify是否開啟證書校驗,默認開啟
Response
標準的JavaScript內置對象
我們可以通過 EdgeJS Serverless 服務實現 Fetch 功能,請求遠程鑒權服務器,根據遠程服務器的響應進行處理,如果得到非 2xx 狀態碼,則返回 403 禁止訪問。Fetch 返回的是一個 Promise 對象,包含類方法,可以將異步的操作變為序列化的操作,只要需要關心業務邏輯。與 Fetch 一同上線的還有 Await、SubtleCrypto 等功能。
以上是老師的全部分享內容,有任何問題可以在討論區提出。
點擊進入獲得更多技術信息~~
掃描二維碼,備注:音視頻開發,立即加入音視頻開發技術交流群。
總結
以上是生活随笔為你收集整理的CDN边缘JavaScript敏捷交付实践的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 做一名真正的软件工程师
- 下一篇: Kira同学:斩获百度校招提前批offe