我們如何將 Moove 的效能提升 59%
多數 Moove 用戶離我們的伺服器很遠。我們在不搬動任何伺服器的情況下,將首次回應時間縮短 59%,以及過程中比預期更困難的部分。距離是我們最慢的依賴項
大多數使用 Moove app 的人,都是透過奈及利亞和印尼的行動網路連線,與回應他們的伺服器相隔數千公里。本月我們改變了他們的請求抵達我們的方式,我們的首位元組時間 (TTFB),也就是手機送出請求到收到回應第一部分之間的時間,下降了 59%,從 0.74 秒降至 0.31 秒。而我們沒有搬動任何一台伺服器。
這篇文章說明為什麼距離造成的成本超出我們的預期、我們做了什麼改變、比改變本身更困難的部分,以及我們會對另一個即將做同樣事情的團隊說些什麼。
為什麼一趟長途變成三趟
工程師把傳送資料到抵達之間的延遲稱為延遲 (latency),把訊息抵達伺服器並收到回覆所需的時間稱為往返時間 (RTT)。從拉哥斯到我們的伺服器,RTT 介於 200 至 300 毫秒之間。單看這個數字還可以接受。問題在於,一個請求很少單獨出發。
手機要透過安全連線請求任何內容之前,必須先完成兩次握手 (handshake),也就是建立連線的簡短交換流程。TCP 握手負責開啟連線,接著由 TLS 握手協商保護連線私密性的加密金鑰。每一次都要花費一個完整的往返。之後請求本身才送出,這是第三趟。以每趟 250 毫秒計算,在我們的伺服器開始做任何事之前,用戶就已經等了大約四分之三秒。
在行動網路上,這個成本比你想像的更頻繁地發生。手機會在基地台之間移動,無線電為了省電會進入休眠,應用程式會被送到背景。這些事件都可能使連線中斷,下一個請求就必須從冷連線 (cold connection) 開始,也就是尚未完成任何握手的連線,並再次付出完整的建立成本。暖連線 (warm connection) 則是已經開啟且已加密,可以直接跳到請求這一步。
我們無法讓拉哥斯更靠近我們的伺服器。但我們可以讓連線的建立更靠近拉哥斯。
移動握手,而非伺服器
內容傳遞網路 (CDN) 通常被描述為一種快取:它是分布在全球各地的一組據點,保存檔案副本,讓檔案可以從附近提供。我們使用它的原因不同。對於我們的 API,也就是應用程式為完成所有操作而與之溝通的介面,快取是完全關閉的。每個請求仍然會前往我們的來源伺服器 (origin),這是 CDN 對其後方伺服器的稱呼。
改變的是連線開始的位置。手機現在會向最近的邊緣節點 (edge location) 開啟連線,邊緣節點也稱為接入點 (PoP):這是 CDN 在全球的據點之一,通常只相距數十毫秒而不是數百毫秒。邊緣節點負責執行 TLS 終止 (TLS termination),意思是來自手機的安全連線在此結束。接著它會重複使用一組通往我們來源伺服器的暖連線,這些連線透過 keep-alive 保持開啟,這個設定會讓連線在請求之間保持開啟而不是關閉。兩次昂貴的建立往返發生在短距離的那一段,只有請求本身跨越海洋,而且是在已經暖好的連線上。
三趟長途變成一趟長途和兩趟短途。整個想法就是這樣。
其餘部分我們刻意保持平淡無奇:
- 用戶端毫無感覺。 應用程式和網站沿用相同的位址,因此不需要發布新版應用程式,也沒有人需要更新任何東西。
- 失敗時的行為維持不變。 逾時設定與先前一致,因此以前會失敗的情況,現在仍以同樣方式失敗。我們不想出現任何新類型的錯誤。
- 邊緣節點先關閉閒置連線。 每條連線都有閒置逾時 (idle timeout),也就是連線在未使用多久後會被關閉。如果我們的伺服器關閉了一條安靜的連線,而邊緣節點仍以為它是開啟的,下一個請求就會失敗。把邊緣節點的逾時設定得更短,就能確保它永遠先放棄,並改為開啟一條新連線。
- 為手機採用較新的協定。 手機可以透過 HTTP/3 連線,這是網路請求協定的最新版本。它運行在 QUIC 之上,QUIC 將兩次握手合併為一次往返,並且更能應付行動網路上常見的封包遺失。
- 不遺漏任何地區。 邊緣網路較便宜的方案,往往恰好略過我們用戶所在的地區,所以我們選擇了完整覆蓋。
我們測量了什麼
我們從雪梨進行測量,它與我們伺服器的距離大致與拉哥斯相當,並比較舊路徑和新路徑。每一條路徑,我們都開啟五條全新的冷連線,這是行動網路上最關鍵的情況,並取其中位數 (median),也寫作 p50:即中間的那個結果,一半的請求比它快,一半比它慢。同一個專案還為我們網站的指令碼開啟了壓縮 (compression),這些指令碼之前是以完整大小傳送的。壓縮會在檔案傳送前縮小它,並在抵達後由瀏覽器還原;我們使用 Brotli,一種專為網路設計的壓縮格式。
| 測量項目 | 之前 | 之後 | 改善 |
|---|---|---|---|
| TCP 和 TLS 握手完成時間 (p50) | 490 ms | 59 ms | 快 88% |
| 首位元組時間 (p50) | 743 ms | 307 ms | 快 59% |
| 首頁指令碼負載 | 1.76 MB | 560 KB | 縮小 68% |
59% 的改善來自首位元組時間。過去需要半秒才能建立的連線,現在不到十分之一秒就能就緒;而首次造訪我們網站時,下載量現在少了三分之二,這對許多必須為每一 MB 付費的連線尤其重要。
這些數字來自一個城市,並不是全球基準測試。不過這套算術同樣適用於拉哥斯和雅加達,而那正是我們用戶所在之處。
困難的部分是知道誰在呼叫
把某樣東西放在伺服器前面很容易。讓你的保護機制在它後面繼續運作,才是大部分心力所在。
和大多數支付平台一樣,Moove 使用速率限制 (rate limiting),也就是限制任何一個呼叫者在特定時間內可以發出的請求數量,而這些限制是以呼叫者的 IP 位址為鍵值,這是在網際網路上識別裝置的號碼。在邊緣網路後面,每個請求都來自邊緣節點的位址,而不是用戶的位址。如果沒有其他改變,拉哥斯的所有人將共用同一份額度,一個繁忙的下午就會開始拒絕那些什麼都沒做錯的人。
解決方法基於兩個概念。
只信任你確實擁有的那幾層。 CDN 是一種反向代理 (reverse proxy),也就是位於你的伺服器前面並將請求轉交給它的伺服器。每個代理都會把它收到請求的來源位址加到一個名為 X-Forwarded-For 的標準標頭中,因此真正的呼叫者位於該清單往回固定數量的步數,也就是躍點 (hops)。這個數字弄錯,兩個方向都是錯誤。少算了層數,每個用戶看起來都像是他們的邊緣節點。多算了層數,你就會開始信任呼叫者自己寫入的條目。所以我們讓這個數字成為基礎設施的屬性,而不是程式碼的屬性。它會在網路改變的同一時刻一起改變,而且如果該值不合理,我們的服務會拒絕啟動,而不是去猜測。
讓邊緣節點為呼叫者作保,並且只相信邊緣節點。 邊緣節點現在會把真正呼叫者的位址寫入每個請求,並覆寫呼叫者所傳送的任何同類內容。我們的保護機制只有在能驗證請求確實經過我們的邊緣節點時,才會相信這個值。過去以網路位址為鍵值的每一項限制,現在對於邊緣流量都改以真正的呼叫者為鍵值。我們是在舊規則旁邊新增這些規則,而不是修改舊規則,因此上線過程中的每一項變更都是純粹的新增,任何人讀一遍就能審查。
我們在上線前抓到的一個錯誤
Moove Profiles 可以放在商家自己的網域上。這些網域的請求會連同商家的網域一併透過我們的網站轉交,讓網站知道要顯示哪個檔案。
網站前面的新邊緣節點會從傳入的請求中移除這類資訊,這完全正確,因為訪客絕不應該能夠設定它。但這也意味著,一旦網站移到邊緣節點後面,每個商家網域都會悄悄地顯示 Moove 首頁,而不是商家的檔案。
我們在審查中發現了這一點,先把商家網域的路徑移到它自己的路由,並讓我們的基礎設施在任何尚未完成該移動的地方拒絕套用網站的變更。一個過去只存在於審查者腦中的錯誤,現在成為建置流程會攔下的東西。
一般性的教訓是:當你新增一個會清理輸入的層時,要找出所有依賴你即將清理之輸入的東西。
我們如何上線
支付基礎設施不能「部署了再看」,所以我們先改了預備環境,之後才改正式環境,並且做了幾個選擇讓切換過程平靜無事。我們降低了 DNS 記錄的 TTL (存活時間),DNS 是網際網路的目錄,負責把像 moove.xyz 這樣的名稱轉成伺服器位址。TTL 是網際網路其餘部分可以快取該答案的時間,我們把它從一小時縮短為五分鐘,並且在任何東西移動之前整整一小時就做好,讓舊答案已在各處過期,回滾也能在幾分鐘內生效。我們讓位址記錄保持在可以就地切換的形式,因為移除再重新建立它們會留下空檔,而空檔就是停機。每一個有風險的步驟都是各自獨立的小變更,並且依序寫下了復原的步驟。
我們會對另一個團隊說的話
如果你的用戶離你的伺服器很遠,即使邊緣網路什麼都不快取,也值得擁有。在切換之前:
- 列出所有讀取呼叫者位址的東西,包括速率限制、允許清單、詐騙訊號、稽核日誌和安全規則。除非你改變它,否則它們每一個看到的都是邊緣節點的位址。
- 讓受信任的層數成為基礎設施的一部分,並在它錯誤時拒絕啟動。
- 讓邊緣節點記錄真正的呼叫者,並且只在你能驗證來自邊緣節點的流量上信任這筆記錄。
- 確保你的伺服器只接受經由邊緣節點的流量,並且在強制執行之前先觀察。
- 如果你快取頁面,請用回應實際上會隨之變化的所有內容來建立快取鍵 (cache key),也就是快取用來判斷兩個請求是否可以共用同一份已儲存回應的一組請求細節。伺服器會在 Vary 標頭中列出這些細節,所以請讀取它,而不是憑記憶。
- 在前後都進行測量,並分別觀察握手時間和首位元組時間,這樣你才能區分連線建立與伺服器工作所花的時間。
展望未來
我們的多數頁面都是個人化的,因此網站的邊緣快取目前大多處於閒置。不過連線的改善適用於每一個頁面,而個別頁面現在可以在安全共用時選擇加入快取。快取 API 回應需要自己的設計和審查,這是我們接下來想研究的事。
金錢應該以網路的速度移動,而不是以光穿越海洋的速度。
如果你在 Moove 之上開發,請閱讀我們對 Web3 支付 API 與 SDK 的介紹和 Moove Developer Program。
由moove.xyz工程團隊撰寫。
關於moove.xyz
moove.xyz是一個全球Web3金融科技平台,專為無需許可、輕鬆流轉的價值移動而建。我們賦能全球各地的企業和消費者,在任意區塊鏈上發送、接收、staking和交換任意加密貨幣——一切盡在一個平台。
我們是全球首批創新並構建全棧加密支付和去中心化金融基礎設施的Web3金融科技公司之一,實現了跨多鏈錢包訪問、個性化錢包標識、跨鏈代幣互換、嵌入式跨鏈交易和去中心化社交金融網絡的綜合全面覆蓋。我們的核心產品包括Moove Profile、Moove Send、Moove Receive、Moove Stake、Moove Swap、Moove Rewards、Moove Discover等。
我們的使命很簡單——為下一個10億Web3用戶創造和分發無需許可的輕鬆金融技術。我們從根本上相信,貨幣和價值流動的未來將是無成本、無邊界、無需許可、毫不費力並為所有人而建的——我們正在構建終極Web3金融科技平台,將這一未來變為現實。
你的財富。你的選擇。






