我經營 Wirewiki.com,一個用於檢查網際網路基礎設施(如網域名稱)的網站。它能幫助人們檢查(歷史)DNS 記錄、DNS 委派、電子郵件可送達性設定等。
市面上有許多提供此類服務的網站(由於「vibe coding」的盛行,數量正以前所未有的速度增長),因此我需要一個能讓我的網站脫穎而出的方法。我選擇了工具的品質/實用性以及使用者體驗。
自動完成是導覽 Wirewiki 的主要方式,因此它應該盡可能完整、準確且快速。我希望它是即時的。就像下一幀就出現一樣。
我大致上已經實現了這個目標。您可以親自試試:
在按鍵按下時(使用者開始按鍵),我們會預先擷取當前按鍵字元加上下一個字元的建議。而在按鍵放開時,我們則渲染建議。
這樣我們就有一個時間預算:按鍵 1 持續時間 + 按鍵之間的間隔 + 按鍵 2 持續時間。如果 API 在第二次按鍵結束前返回,我們就能及時準備好結果。
(60 Hz 的顯示器每 16.7 毫秒渲染一次。所以我們在 p50 時技術上還有 8.33 毫秒的額外時間預算,但在 p99 時則接近 0 毫秒。)
因此,為了本文的目的,我們將延遲定義為從按鍵放開到結果準備好渲染的時間。p99 0 毫秒意味著 99% 的時間,結果會在使用者放開按鍵之前就準備好了。
我們需要兩樣東西來實現這一點:
這是我最初的擔憂。但結果證明這不是問題。有效的網域名稱字元只有 38 個:a-z、0-9、- 和 .。這設定了回應中 (38 + 1) * 8 = 312 個網域名稱的上限。
實際上,這相當於每次請求最多約 5 kB 的資料。壓縮後,資料傳輸量約為 2.5 kB。
考慮到 50-100 kB 通常被認為是健康的圖片大小,相當的資料量相當於輸入 20-40 個字元。
我從未因為頻寬使用量而猶豫是否要在頁面上包含圖片,所以我認為對此我很滿意。
我們現在知道我們可以花費兩次按鍵持續時間和一次間隔持續時間,但這以毫秒為單位是多少呢?
我透過快速輸入 100 個網域名稱進行了測量,發現對我來說,p99 結果是 121 毫秒。
這是我的結果。您可以開始輸入以查看對您而言是多少。
這測量了從一次按鍵到下一次放開的時間。滑桿告訴您在給定的 API 延遲下,有多少百分比的按鍵會在下一幀渲染。
好的,所以我們有一個 121 毫秒的延遲目標。但我們能讓 API 多快呢?
我使用 Tranco 列表中的前 100 萬個最受歡迎的網域名稱來建置這個 API。這些應該會被優先建議,並輔以目前正在使用的任何其他網域名稱。
CZDS 提供了大多數通用頂級網域(如 .com、.net、.org)的網域名稱列表。不幸的是,國家頂級網域(如 .uk、.de、.fr)無法取得。但任何有意義流量的國家頂級網域的網域名稱反正都會在 Tranco 列表中。還有其他來源,如憑證透明度日誌和 Archive.org,我們可以使用,但我尚未整合它們。
我設計的 API 首先搜尋 Tranco(前端),然後在必要時搜尋 CZDS(後端)。結果按排名順序返回,因此前 8 個是最受歡迎的。
前端:記憶體中的字元 trie。Trie(前綴樹)儲存了每個前綴預先計算好的前 8 個建議。前綴查找是走訪幾個指標。最壞情況時間複雜度:O(您輸入的長度)。
後端:SSD 支援的記憶體映射區塊索引。CZDS 網域名稱被排序並 delta 編碼成固定大小的區塊,並帶有一個微小的記憶體內目錄。查找會對目錄(27 MB)進行二元搜尋,然後線性掃描一個包含 256 個名稱的區塊。2.4 億個網域名稱大約佔用 2.5 GB 的磁碟空間。熱門頁面由作業系統快取在記憶體中。最壞情況時間複雜度:O(您輸入的長度 * log(網域名稱數量))。
網域名稱數量和查詢長度都有上限。這使得兩種資料結構的最壞情況實際上是 O(1),這應該能讓 p99 延遲保持較低。讓我們來看看。
我讓一個 LLM 壓力測試了生產伺服器。它透過模擬 6 萬個輸入的網域名稱產生了 72 萬次按鍵查詢,並以開放迴路方式(無論回應速度如何,都以固定目標速率發送)重播。它在隔離環境、透過 Nginx 以及端對端測試了 API。
不同請求速率下的延遲百分位。兩個軸均為對數刻度。
大多數請求在 2 毫秒內由 API 回答。即使在 1.6k req/s 的情況下,Nginx + API 在 99% 的時間內也能在 15 毫秒內回應。
我相信我們可以再節省幾毫秒,但我對此很滿意。進一步優化 API 沒有意義,因為網路延遲佔主導地位。
實際上,自動完成延遲約等於從瀏覽器透過 Cloudflare 到伺服器的往返時間 + 10 毫秒。
透過 Cloudflare 的往返會增加顯著的延遲,但也能吸收頻繁的請求。
在我的測試中,這種端對端延遲在我們的預算範圍內。即使有 1000 人同時輸入。
問題在於我只在歐洲運行單一伺服器。因此,來自更遠地方的流量將在 p99 時超出預算。例如,來自美國的流量將增加 100-200 毫秒。
熱門路徑的 CDN 快取和 Nielsen 的 0.1 秒「即時」閾值在很大程度上彌補了這一點,但不足以讓我們達到目標。
我可以設置多個伺服器並進行地理負載平衡流量。這將使我達到 p99 0 毫秒* 的延遲。但這有點過了。即使對我來說也是如此。
如果我將其發展成一個產品,我會這樣做。但我認為這太小眾,不足以建立一個企業。但如果您願意付費使用此 API,請發送電子郵件給我,我可能會改變主意。
哦,這也是我在 Wirewiki 上為自己設定的使用者體驗標準,所以如果您看到任何可以改進的地方,也請讓我知道。