CloudFlare Worker 將你的網站遁於無形

背景知識

1、網站如何工作

當你在瀏覽器敲下一個域名,瀏覽器會先去 DNS 查詢域名對應的 IP 地址。然後和這個 IP 地址的 443 端口建立 tcp 連接,連接建立後開始進行 HTTPS 握手(針對 HTTP/1、2)。

HTTPS 握手的時候,客戶端(你)會將域名信息以 SNI 的形式發送給服務端, 服務端根據這個 SNI 找到相應的網絡服務器,然後返回其證書,完成 HTTPS 的五步握手。

2、CDN 是什麼

在沒有 CDN 的時候,假設網站的服務器跑在某個 IDC 內,那麼域名的 DNS 解析應該直接返回這個 IDC 的出口 IP。

當然,返回的 IP 可能不止一個,畢竟這個網站可能存在於多個 IDC,每個 IDC 可能也不止一個網絡出口(可能會接入多運營商)。DNS 服務應該根據查詢來源的地理信息和運營商信息,選擇一個理論上 “最近” 的 IP 地址返回給客戶端。

這樣做有一些缺點,一是所有的動態和靜態請求都直接觸達服務器,壓力比較大,成本也比較高。另一個缺點是機房 IP 直接暴露在外,很容易被惡意攻擊,如果被惡意 DDoS 機房 IP,那整個機房可能就癱瘓了。

於是人們在客戶端和機房之間多加了一層代理,域名不再解析 IDC 的 IP,而是將 IP 解析到這層代理上。這層代理還提供了存儲,做了很多靜態資源的緩存。這些代理服務器的邏輯也很簡單,獲取請求後,判斷一下是否是靜態資源, 如果是靜態資源,就返回緩存(此處省略緩存更新策略一萬字),否則就把請求轉發回源站。

因爲邏輯比較簡單,成本也就會比較低,所以可以在世界範圍內大規模的部署, 因爲部署的數量很多,所以也形成了規模效應,對於全球各地的絕大部分用戶,總能找到一個離他比較近的代理站。然後人們就把這些分佈很廣的靜態資源緩存服務器稱爲內容分發網絡(Content Delivery Network, CDN)。

使用 CDN 後不但能相對廉價地對廣泛區域內的客戶實現加速,而且因爲客戶端不再能拿到你的源站 IP, 對源站也起了很好的保護作用。

考慮到 HTTPS 會對 CNI 和證書做校驗,如果你使用了 CDN,那麼也得把證書前置到 CDN 服務器上。也就是將請求拆分爲兩段,用戶和 CDN 進行 SSL 握手,CDN 服務器再和你的源站進行 SSL 握手。

3、FaaS 是什麼

傳統的 webserver 24 小時在後臺運行,監聽 TCP 端口的 HTTP 請求,處理後再返回。優點是響應很快,缺點是運行成本比較高,無論有沒有請求,都 24 小時跑在那裏。

爲了提高應對請求壓力的靈活性,人們提出了 stateless。就是如果 webserver 做成無狀態的, 可以理解爲其內部不保存任何中間變量,數據都在外部進行持久化存儲,其本身只負責業務邏輯。那麼 webserver 就可以隨時隨地的跟隨負載壓力進行擴縮容。

這個想法再激進一點,如果有一些請求的頻率非常低,比如 CI/CD,或者一些 cronjob, 或者流量的峯值效應很明顯,絕大部分請求集中在一天的一部分時間。那麼人們就認爲根本沒必要隨時隨地運行一個服務器在那等着, 完全可以在有請求來的時候,再去啓動一個服務,如果請求量很大,就啓動更多的服務, 如果一段時間沒有請求量,就把服務器關機釋放出來。

這種想法就被稱爲 FaaS(Function as a Service),因爲此時的後端服務表現得就像是一個普通函數(而且不是閉包哦), 接受固定的入參,執行一些邏輯,然後返回,銷燬。

4、CF Worker 是什麼

瞭解了上述的背景知識,我們就可以來交接 CF Worker 是什麼了。

拋開 LB、SSL 等 infra,純粹從後端的角度來看 webserver,實際上就是整個邏輯鏈路就是 socket listener、router、handler。listener 獲取到請求後,交給 router 分發給 handler 處理,然後返回。

那我們再來回頭看看 CDN,CDN 因爲會進行 SSL 卸載,所以可以獲取 HTTP 的全部負載明文。我們知道 CDN 可以分析 HTTP URL,發現是靜態請求就直接返回緩存,發現是動態請求就轉發回源站。

那麼如果我們在 CDN 裏跑一個 FaaS 的體系,讓你可以編寫一些相對簡單的代碼, 直接在 CDN 服務器內處理和響應一些動態的請求,而不是把它們轉發給源站,這樣整個 RTT 的鏈路不是更少了嗎?這樣就可以更快地響應請求,也可以進一步爲源站減壓。

CF worker 就是這麼一個運行在 CF CDN 內的 FaaS。它的核心組成就是:router、FaaS(handler)和 KV(存儲)。

你把域名託管在 CF 上並啓用 CDN 後。就可以爲域名創建 worker,編寫一些 handler 函數(可以用 JS、Python 等等)。這些 handler 都是無狀態的(因爲會被隨機啓停),持久化的數據可以使用外部的 KV 存儲。然後再配置上路由,將匹配的流量分發給 worker, worker 內的代碼可以選擇將能處理的請求直接就地處理,不能處理的再轉發會源站處理。

甚至於,如果你的站點內容如果都可以放進 KV 存儲的話,你可以根本不需要源站, 而將整個站點都跑在 worker 裏。

CF Worker 使用

前期準備:

  1. 一個域名

  2. 一個 cloudflare 賬號

首先,進入 CF 控制檯的 worker 頁面,按照如下順序進行操作:

  1. 先創建 KV

  2. 再編輯 worker handler,此時不需要 router,因爲 cf 會給你創建一個測試用的 dev 地址,可以在調試的時候請求

  3. 最後編輯域名對應的路由,開始負載正式的流量

創建 KV:

KV 創建後,爲了能夠在 worker 代碼中使用,需要被綁定到一個全局常量中:

KV 綁定好後,我們就可以開始編輯代碼了

代碼的編輯窗口:

本站使用 cf 緩存了所有 graphql query 的請求,以 ttl=1d 的生命週期放於 KV 中。爲了方便調試,還增加了 ?force 參數用來強制穿透和刷新緩存。

因爲 CF worker 目前僅支持一個源代碼文件,所以可以採用將第三方庫代碼全部粘貼進來的方式使用第三方庫。如果你有使用 webpack 等打包工具的話,也可以以 webpack 工程的形式,最終構建出一個單一的 js 文件即可。

示例代碼可見:https://github.com/Laisky/cloudflare-workers

總結

最好把 worker 和路由設計成可隨時停用的。比如 worker 攔截了某些路由,而後端服務器依然保留這些路由, 也就是說,即使臨時把 worker 關停,對外服務也不會受到影響。

原文鏈接:https://blog.laisky.com/p/cf-worker/

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/DzTfoVc01JlQkeCG6ZgCFw