卡頓
動作不流暢,不斷短暫停住又繼續動。 ping 值正常時,多半是自己電腦的畫格問題(用戶端、OS);ping 忽高忽低時,多半是 Wi-Fi 或線路的抖動。不過遊戲內顯示的 ping 通常是在每個畫格跑一次的遊戲迴圈裡量的,所以畫格時間飆高時,ping 數字也可能跟著跳。
本白皮書從自己的遊戲畫面到伺服器的資料庫,分成 13 層說明畫面卡頓、角色瞬移、斷線的原因。每個原因都列出確認方法與負責團隊,也能在實驗中調整條件親自確認。內容以 MMO 案例為主,但大部分與遊戲類型無關,適用於各種線上遊戲。
原因超過一百種,但造成 lag 的因素大致可以歸成四類:封包晚到、封包忽快忽慢、封包根本沒到,或者某個環節停止了計算。遊戲會用各種技術掩蓋這些因素,掩蓋失敗時留下的痕跡,就是我們看到的 lag「樣貌」。
在 MMO 裡,自己畫面上的世界是依照伺服器送來的封包重新繪製的畫面。做法依遊戲而異,但伺服器通常 1 秒計算 10~30 次遊戲狀態(每一次稱為一個 tick),再從結果中只挑出每位玩家周圍的變化,用封包送出。自己的電腦讀取抵達的封包後繪製畫面。因此 lag 大多是遊戲如何呈現「封包沒有準時抵達」的問題。
也有四個因素以外的情況。伺服器和自己的電腦對同一件事算出不同結果時(移動規則不一致、bug),即使線路正常,也會出現拉回或看不見。這類 lag 的線索是:與 ping 無關,總在同一個地點、同一個動作上反覆發生。
用宅配來想像。配送總是要 3 天,是延遲;有的一天就到、有的要五天,是抖動(jitter);箱子不見了,是遺失;物流中心關門,是停滯。遊戲靠「箱子晚到或沒到,就用前後的箱子推測補上(內插、外插)」、「沒到就請對方重寄(重傳)」這類方法撐住。
Lag 的討論大多以毫秒(ms,1/1000 秒)為單位。只要記住下表中的幾個數字,就更容易聽懂遊戲開發團隊和基礎設施團隊在說什麼。
| 基準 | 時間 | 意義 |
|---|
CPU、磁碟、資料庫、分享器、電信業者線路。層雖然不同,結構卻一樣:有處理請求的 worker(CPU 核心、執行緒、DB 連線等),前面會形成佇列。當 worker 閒置時,佇列是空的,但忙碌程度(使用率)一超過 80~90%,佇列就會急遽變長。單一 worker、請求隨機抵達時,平均等待時間在使用率 50% 時等於處理時間,80% 時是 4 倍,90% 時是 9 倍。「CPU 明明還剩 10%,為什麼會 lag?」的答案就在這裡。而且監控畫面上的 CPU 數值通常是多個核心、1~5 分鐘的平均值,會掩蓋只有一個核心跑到 100% 的情況,或是只集中在短短幾秒內的負載。
本白皮書探討線上遊戲遊玩過程中造成 lag 的原因,範圍從繪製遊戲畫面的電腦、手機開始,到家用網路、電信業者、資料中心、伺服器與資料庫。以影像串流接收遊戲畫面的雲端遊戲、以獨立服務運作的語音聊天,以及更新與下載速度,因為結構不同,不在討論範圍內。不過其中的網路原因(Wi-Fi、bufferbloat、線路壅塞等)與這裡的原因相同。
按下技能鍵後,這個訊號會經過自己的電腦、家用網路、電信業者、資料中心,抵達伺服器。在伺服器內部經過多個層處理的結果,再反向經過同樣的層,繪製到畫面上。總共 13 層,任何一層卡住就會 lag。點選下方地圖中的層,即可跳到對應章節。
這是由一台伺服器、一條線路、一台電腦組成的小型模擬。逐一破壞條件,看看卡頓、瞬移、拉回、快轉、慢動作、輸入延遲、定格、斷線各自是怎麼產生的。封包時間軸用線條顯示每個封包何時送出、何時抵達。線越斜代表花的時間越久,× 是遺失的封包。
玩家只會說「很 lag」,但 lag 的樣貌其實透露了不少原因的線索。每個症狀的小圖,是畫面中角色經過的軌跡。點重疊代表停住,點拉開代表變快或跳過。
動作不流暢,不斷短暫停住又繼續動。 ping 值正常時,多半是自己電腦的畫格問題(用戶端、OS);ping 忽高忽低時,多半是 Wi-Fi 或線路的抖動。不過遊戲內顯示的 ping 通常是在每個畫格跑一次的遊戲迴圈裡量的,所以畫格時間飆高時,ping 數字也可能跟著跳。
角色沒有移動過程,一下子就出現在很遠的位置。 通常代表封包斷了一段時間。要查封包遺失、線路短暫中斷、伺服器停住、外插失敗。其他人都正常、只有一個人在跳的話,先懷疑那個人的線路。
自己的角色往前走到一半,被拉回剛剛經過的位置。 自己畫面上的預測和伺服器判定對不上。可能是自己的輸入沒送到伺服器(封包遺失)、伺服器的移動驗證把它擋掉,或是兩邊的移動計算不一致。
停住的畫面重新動起來時,積壓的移動、打擊和傷害一口氣快速跑完。 封包在某處堆積,然後一次放行。典型的例子是 TCP 等待重傳、伺服器追進度、用戶端處理積壓。
所有東西都變慢,技能施放和怪物移動看起來像被拉長。依伺服器設計不同,也可能速度不變,改以卡頓或瞬移的形式出現。 伺服器無法在時限內跑完 tick。線路本身正常,所以在遊戲外量的 ping 不變;遊戲內的 ping 若含有伺服器處理的等待時間,可能會稍微升高。要查人數暴增、視野計算、廣播、記憶體不足。
按下去之後要等一段時間才看到結果。畫面本身可能很流暢。 往返時間(ping)很長,或是某處的佇列堆積起來。要查距離、分享器佇列、Nagle(把小封包湊在一起再送的 TCP 功能)、伺服器佇列。ping 很低卻總是遲鈍的話,就查垂直同步(V-Sync)、FPS 偏低這類自己電腦端的因素,或是每個動作都要等伺服器確認的設計(見「同步方式」一章)。
畫面裡所有東西停住一下(0.5 秒到數秒),然後又開始動。 可能是整台伺服器停住(GC、死結、同步呼叫)、線路短暫中斷,或是自己的電腦停住。
明明做了的動作變成沒發生過,或是結果過了好一陣子才被推翻。 可能是請求遺失(封包遺失、佇列滿溢)、伺服器的判定和自己的畫面不同(判定時間點不同、先行演出後被拒絕),或是存檔途中失敗(DB 鎖定或故障、伺服器當機)。
遊戲中連線中斷,被送回登入畫面或重新連線視窗。 在逾時時間內一個封包都沒收到。要查線路長時間中斷、閒置逾時、伺服器當機或重啟,以及停住時間超過逾時的伺服器或自己的電腦(載入太久)。若沒有任何提示、遊戲直接關掉,先查用戶端被強制結束(閃退、記憶體不足),再查連線。
進不了遊戲,或是停在讀取、進場畫面。 負責接受新連線的地方(伺服器的連線等待佇列、防火牆、登入伺服器、DB)滿了。維護剛結束時特別常見。
應該存在的 NPC、怪物、玩家只在自己的畫面上消失,或是早已消失的物件只留在自己的畫面上。 多半是少了某個封包,或是繪製失敗,和速度快慢關係不大。要查頻道或相位不同、出現/消失通知遺失、載入期間被丟棄、資源載入失敗。離開視野再回來後會不會出現,是最關鍵的線索。
有些遊戲 ping 150ms 也毫無感覺,有些遊戲 60ms 就覺得遲鈍。即使不是動作遊戲也一樣。線路相同時,這個差異通常來自用戶端與伺服器事先約定「由誰、在何時、決定什麼」的方式,也就是同步設計。其中有些是刻意的選擇,有些則真的是做錯了。
所有連線遊戲都在解同一個問題。伺服器與自己的電腦之間必然有時間差,雙方總要有一方決定如何處理「尚未確定的事」。選項大致有四種。
因此對 ping 的敏感度,比起遊戲類型,更取決於兩個問題:一個核心動作要等待幾次伺服器往返,以及遊戲規則容許的時間是否比「ping + 人的反應時間」寬裕。
| 方式 | 運作方式 | 常見於 | ping 150ms 時的樣子 | 弱點 |
|---|---|---|---|---|
| 請求-回應 伺服器確認後才顯示 | 按下後先詢問伺服器,等回應到了才演出。 | 回合制、卡牌、放置型遊戲,商店、交易、製作 UI,早期 MMO 的技能與道具使用 | 所有動作都晚約 0.2 秒開始。回合制幾乎感覺不到 | 連續動作、一個畫面內有多次往返的 UI |
| 狀態同步 + 內插 伺服器權威 | 伺服器每個 tick 送出遊戲狀態,用戶端在兩個狀態之間補畫中間的動作。 | 多數 MMO 中其他玩家、怪物的顯示 | 其他人是約 0.2 秒前的樣子。平時幾乎看不出來 | 抖動(jitter,抵達間隔忽長忽短)、遺失 → 瞬移,tick rate 過低 |
| 用戶端預測 + 伺服器校正 | 自己的輸入立即反映,伺服器結果到了再比對修正。 | FPS、動作 MMO、多數 MMO 的移動 | 自己的操作立即反應。偶爾短暫拉回 | 與伺服器計算不一致時頻繁校正 |
| 延遲補償 伺服器回溯判定 | 伺服器回溯到攻擊者當時看到的過去時間點,判定是否命中。 | FPS、非鎖定動作遊戲 | 開槍的人覺得公平,被打的人卻會覺得「明明躲到掩體後面還是中彈」 | 被打的一方覺得冤枉。攻擊者 ping 越高,回溯幅度越大,情況越嚴重 |
| 指令/目的地同步 | 只送出「走到這裡」、「攻擊這個目標」之類的意圖,兩端各自計算。 | 點擊移動的 MMO、Tab 鎖定戰鬥、部分 MOBA | 只有起步稍晚,移動與攻擊都很流暢 | 路徑或結果不一致時需要校正 |
| 事件排程 以伺服器時間為準 | 像「伺服器時間 T 開始」這樣連同未來的時間點一起通知,各自在該時間點播放。 | 團隊副本王的招式、過場動畫、整點活動 | 預告比 ping 長的話,實際上沒有影響 | 比排定時間晚抵達時,會跳過開頭部分 |
| 確定性 lockstep | 收集所有人的輸入,在同一回合做完全相同的計算。輸入會加上固定延遲。 | RTS(星海爭霸類)、部分合作、解謎遊戲 | 所有輸入都固定延後(按下時立即用音效、提示掩蓋)。抖動大時所有人一起停住 | 抖動、遺失、最慢的那一個人 |
| Rollback 預測後回溯 | 預測對手的輸入先往下進行,猜錯就回溯到過去重新計算。 | 格鬥遊戲(GGPO 類)、部分動作、運動遊戲 | 操作手感幾乎即時(通常有 1~3 個畫格的輸入延遲)。對手動作偶爾跳掉幾個畫格 | ping 高時回溯幅度變大,看起來像瞬移 |
| 用戶端權威 | 各自決定自己的結果,伺服器只負責轉送與紀錄。 | 部分手機、休閒遊戲,P2P、中繼架構 | 自己的畫面很順。與其他人畫面上的結果不一致 | 外掛,「我明明打中了卻沒算」 |
實際的遊戲會混用這些方式。常見做法是依動作分別選擇:移動用預測,技能先行演出再確定,王的招式用事件排程,交易用請求-回應。
1. 一個動作中完全不夾帶往返。按下按鈕就立即開始播放動畫、音效、特效(先行演出),伺服器結果只用在像傷害數字這種晚一點也看不出來的部分。
2. 遊戲規則容許的時間比 ping 寬裕得多。王的預告有 1~2 秒的話,即使封包晚約 0.2 秒到、人的反應花掉 0.25 秒,還是來得及閃開。自己的技能如果有施放時間,伺服器確認會在施法條跑滿的期間一起完成,等待就藏在施放時間裡。這是 Tab 鎖定 MMO 對 ping 不敏感的最大原因。反過來說,0.5 秒左右的短預告,只要 ping 到 150ms 就很難看到再閃開(見下方判定區間實驗)。
3. 先收下連續動作。有預輸入(技能佇列)的話,即使在冷卻結束前就按下一個技能也會先收下,連段之間就不會夾著往返時間。
4. 吸收抖動。內插緩衝與以伺服器時間為準的演出,會把「大多 150ms、偶爾 250ms」才到的封包,轉換成固定晚約 250ms 的穩定資料流。代價是看到的畫面再舊一點,但動作很流暢。人很快就能習慣固定的延遲,卻很難習慣忽快忽慢。做得好的遊戲會配合抖動變大變小,自動拉長或縮短緩衝。
5. 判定與「自己看到的」一致。以玩家看到的時間點來判定閃避與命中(延遲補償),或者一開始就採用與位置無關的規則(指定目標)。
6. 一個人的延遲不會讓其他人等待。權威伺服器架構下,即使自己的 ping 很差,其他人也不受影響。採用 lockstep 或主機架構時,最慢的那一個人決定所有人的體感。
對 ping 敏感,有時是刻意的設計,有時是做錯造成的。
按下按鈕後,在伺服器回應之前既沒有動畫也沒有音效。ping 直接就是反應速度。
為什麼: 技能、移動、撿取都等伺服器確認後才播放 → 於是: 從按下的瞬間起,在往返時間 + tick 等待這段期間毫無反應 → 畫面上: ping 150ms 時,每個動作都慢 0.2 秒
症狀: 輸入延遲 · 主要負責 遊戲開發團隊(用戶端開發) · 協同 遊戲開發團隊(伺服器開發)
一次操作需要依序與伺服器往返好幾次時,ping 會乘上這個次數。
為什麼: 開啟商店 → 請求清單 → 確認價格 → 購買 → 更新背包,每一步各自請求 → 於是: 收到前一個請求的回應後才送出下一個請求 → 畫面上: ping 150ms 時買一次東西要將近 1 秒。載入特別久
症狀: 輸入延遲, 連不上/無限讀取 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 遊戲開發團隊(用戶端開發)
必須收到伺服器確認前一個技能已結束才能按下一個技能時,每次連段之間都會夾著一段往返時間。
為什麼: 只在「前一個技能確定後」才接受下一個技能的輸入 → 於是: 每個技能之間都空出一段 ping 長度的時間 → 畫面上: 連段之間出現空檔,ping 越高 DPS 越低
症狀: 輸入延遲, 吃指令/回檔 · 主要負責 遊戲開發團隊(用戶端開發) · 協同 遊戲開發團隊(伺服器開發)
閃避、格擋、防禦這類需要反應的時間很短時,ping 會把這段時間吃掉,出現躲不掉的攻擊。
為什麼: 像王的攻擊預兆 0.5 秒、格擋判定 0.2 秒這樣短的判定區間 → 於是: 預兆較晚看到(下行延遲 + 內插),自己的輸入也較晚抵達(上行延遲 + tick 等待) → 畫面上: 明明躲開了卻被打中,格擋被吃掉
症狀: 吃指令/回檔, 輸入延遲 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 遊戲開發團隊(用戶端開發), 基礎設施團隊(伺服器基礎設施)
伺服器只用「伺服器上現在的位置」判定命中時,判定會與自己看到的畫面不一致。
為什麼: 自己畫面上的對手是約 0.2 秒前的位置(ping 150ms、內插 100ms 時) → 於是: 伺服器以目前位置判定,對手早已不在自己瞄準的地方 → 畫面上: 明明打中了卻判定落空。射擊移動中的目標要預留提前量
症狀: 吃指令/回檔 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 遊戲開發團隊(用戶端開發)
以攻擊者為準回溯得太遠時,被打的一方明明已經躲好了還是會中彈。
為什麼: 為了 ping 高的攻擊者,伺服器大幅回溯後判定 → 於是: 在被打的人的畫面上,早已躲進掩體 → 畫面上: 「躲在牆後還被打中」,ping 高的人占優勢
症狀: 吃指令/回檔 · 主要負責 遊戲開發團隊(伺服器開發)
各自決定自己的結果時,自己的畫面很順暢,但結果會與其他人的畫面不一致,也容易被外掛利用。
為什麼: 位置、命中由用戶端決定,伺服器只負責轉送 → 於是: 兩個人都聲稱自己先打中,伺服器無法驗證 → 畫面上: 對手瞬移、穿牆,「我明明打中卻沒中」
症狀: 瞬移, 吃指令/回檔 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 遊戲開發團隊(用戶端開發)
在所有人一起計算同一回合的架構中,只要一個人的輸入晚到,所有人都要等。
為什麼: 每回合要收齊所有玩家的輸入才能計算 → 於是: 某個人的輸入因抖動或封包遺失而晚到 → 畫面上: 所有人同時頓一下,嚴重時跳出「等待玩家中」視窗
症狀: 定格, 卡頓, 輸入延遲 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 遊戲開發團隊(用戶端開發)
先預測對手的輸入並呈現出來,猜錯時就回溯重新計算。ping 越大,回溯的幅度越大。
為什麼: 對手改變輸入(與預測不同) → 於是: 實際輸入晚了 ping 的一半才抵達,於是回溯這段時間重新計算 → 畫面上: 對手的動作跳過幾個畫格或突然改變
症狀: 瞬移 · 主要負責 遊戲開發團隊(用戶端開發)
伺服器事件沒有附上發生時間、一收到就播放時,網路抖動會讓演出時機跟著忽快忽慢。
為什麼: 「開始攻擊」、「播放特效」事件一抵達就執行 → 於是: 每個封包的抵達時間不同,間隔忽長忽短 → 畫面上: 連續攻擊動作忽快忽慢,王的招式時機每次都不一樣
症狀: 卡頓, 快轉 · 主要負責 遊戲開發團隊(用戶端開發) · 協同 遊戲開發團隊(伺服器開發)
請求先累積到下一個 tick 才處理,結果又等到再下一個 tick 才送出時,tick 間隔會被加上兩次。
為什麼: 收到的請求在下一個 tick 處理 → 於是: 處理結果也累積到下一個傳送 tick 才送出 → 畫面上: 線路 ping 很低,反應卻固定慢了約 tick 間隔的 1.5 倍。10 tick 伺服器平均 0.15 秒,最差 0.2 秒
症狀: 輸入延遲 · 主要負責 遊戲開發團隊(伺服器開發)
伺服器對移動速度、冷卻時間、射程檢查得太嚴格時,連因抖動而擠在一起抵達的正常輸入也會被拒絕。
為什麼: 「一個 tick 內可移動的距離」、「冷卻時間容許誤差 0ms」這類嚴格標準 → 於是: 因抖動使兩個指令擠在同一個 tick 抵達時,就被判定為違規 → 畫面上: 拉回,冷卻好了技能卻被拒絕
症狀: 拉回, 吃指令/回檔 · 主要負責 遊戲開發團隊(伺服器開發)
由某個玩家的電腦擔任伺服器時,那個人的線路與電腦效能決定了所有人的體感。
為什麼: 房主的電腦擔任伺服器(P2P、listen server) → 於是: 房主的線路或電腦慢時會影響所有人,房主本人 ping 為 0 → 畫面上: 只有房主占優勢,房主離開時所有人定格、斷線
症狀: 卡頓, 定格, 斷線 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 遊戲開發團隊(用戶端開發), 基礎設施團隊(伺服器基礎設施)
自己畫面上先呈現的打擊、技能,事後沒有被伺服器認可時,明明看到的結果就變成沒發生過。
為什麼: 在伺服器確認前先播放打擊特效與技能動作(先行演出) → 於是: 伺服器重新檢查射程、目標位置、冷卻與資源後拒絕 → 畫面上: 噴血了卻沒有傷害,技能只有動作沒有效果,只有冷卻在跑
症狀: 吃指令/回檔, 拉回 · 主要負責 遊戲開發團隊(用戶端開發) · 協同 遊戲開發團隊(伺服器開發)
只互傳「走到這裡」、路徑由兩邊各自計算時,計算只要有一點不同,角色或怪物就會走上不同的路徑,再被拉回原位。
為什麼: 點擊移動、怪物追擊時只送目的地,路徑由用戶端另外計算 → 於是: 因地形資料差異、與其他角色碰撞、計算順序不同,走上與伺服器不同的路徑 → 畫面上: 怪物穿牆走到一半一下子被移走,點擊移動的角色像滑行般轉向
症狀: 瞬移, 拉回 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 遊戲開發團隊(用戶端開發)
伺服器每秒只送幾次位置更新(快照)時,內插緩衝就得相應拉長,看到的其他角色會是更久以前的狀態。
為什麼: 為了節省傳輸量,每秒只送 5~10 次位置更新 → 於是: 要畫得流暢,緩衝就得設成封包間隔的 2 倍(200~400ms);設得短的話,只漏掉一個封包就會停住 → 畫面上: 對手轉向較晚才看到,與判定不一致。緩衝短時會卡頓,封包遺失時出現瞬移
症狀: 卡頓, 瞬移, 吃指令/回檔 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 遊戲開發團隊(用戶端開發)
Lag 回報中最容易混淆的,是只有少數人或只有一邊遇到的情況。慢的那個人在別人眼中是什麼樣子、會不會連帶拖慢其他人,完全取決於伺服器處理輸入的方式和同步方式。同一台電腦開兩個用戶端、只有其中一個看不見 NPC 的現象,也在本章說明。
現在大部分 MMO 都是伺服器權威架構。伺服器決定所有結果,用戶端繪製收到的結果。在這種架構下,lag 大多只出現在慢的人身上。
不過也有一個慢的人會拖慢所有人的架構。共通點是「有人在等那個人」。
| 伺服器處理輸入的方式 | 慢的本人遇到的狀況 | 慢的人在別人眼中的樣子 | 其他人自己的遊戲 |
|---|---|---|---|
| 每個 tick 集中處理 固定 tick,收到的輸入一次處理 | 技能結果會晚 ping 加上等待 tick 的時間(輸入延遲)。移動驗證嚴格時會拉回 | 頓一下後一次走好幾步(快轉、瞬移)。沒有抖動、只是 ping 高的話很流暢 | 沒有影響 |
| 一抵達就處理 事件驅動,收到立即套用並傳送 | 輸入延遲等於 ping。只省下等待 tick 的那段時間 | 移動忽快忽慢(輕微快轉)。一起湧入的多個技能在一瞬間全部執行 | 沒有影響 |
| 每位玩家各自的輸入緩衝 每人各自暫存,一個 tick 處理一個 | 確定時間會晚一個緩衝的長度 | 相對流暢。緩衝空了會暫時停在原地 | 沒有影響 |
| 延遲補償判定 回溯到攻擊者看到的時間點 | 瞄準哪裡就打中哪裡(在回溯上限內) | 躲起來之後還是會被那個人的攻擊打中 | 冤枉被打中(擴散) |
| Lockstep、回合等待 | 輸入延遲。輸入晚到時定格 | 全員定格 | 定格。光是晚到也會輸入延遲(擴散到全員) |
| 阻塞式傳送、同步處理 伺服器等待那個人 | 定格後快轉 | 該伺服器執行緒負責的所有人都變慢 | 慢動作、定格(擴散到該執行緒負責的人) |
| 慢的人是主機 P2P、listen server | 本人的 ping 是 0 | 所有人的畫面都卡頓 | 全員 lag |
| 由慢的人控制怪物 把怪物移動的計算交給用戶端 | 本人畫面上的怪物正常 | 那個人負責的怪物頓一下後瞬移 | 所有和那些怪物戰鬥的人(擴散) |
同一個人在同一台電腦上開了兩個用戶端,卻只有一邊看不見 NPC 時,原因幾乎不會是線路。兩個用戶端用的是同一台分享器、同一條線路。差異出在以下三個地方。
最有力的線索有三個:名字還在,只有角色模型不見嗎(伺服器有送,繪製失敗)、離開視野再回來就看得到嗎(漏掉了一則出現通知),以及把看不見的那個視窗切到前景會改善嗎(背景視窗的處理限制)。反過來,早已死掉的怪物只在自己畫面上還站著的「幽靈物件」,是漏掉了消失通知。
線路差的人,輸入會忽快忽慢、成批抵達伺服器。伺服器每個 tick 收到多少就套用多少時,在其他人眼中,那個角色會頓一下後一次走好幾步。
為什麼: 慢的人的移動指令,有的 tick 抵達 0 個,有的 tick 一次抵達 2~3 個 → 於是: 伺服器在收到的 tick 一次全部套用,那個角色的位置呈階梯式變化 → 畫面上: 在其他人畫面上,只有那個角色頓一下後一次快轉移動。其他都正常
症狀: 快轉, 瞬移 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 外部(外部)
封包一抵達就立即處理並通知的伺服器,會把慢的人成批湧入的動作接連立即執行。
為什麼: 慢的人的技能、移動請求成批抵達 → 於是: 伺服器一收到就依序執行,並立即通知所有人 → 畫面上: 在其他人眼中,那個人在一瞬間放出好幾個技能,或像快轉一樣移動
症狀: 快轉 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 遊戲開發團隊(用戶端開發)
伺服器替每個人暫存一點輸入、每個 tick 取出一個使用時,在其他人眼中很流暢,但本人的動作在伺服器上確定的時間點也會晚上這麼多。
為什麼: 伺服器把慢的人的輸入存進緩衝,每個 tick 套用一個 → 於是: 緩衝小時經常被取空,那個角色會停在原地,或由伺服器依最後一個輸入推測移動;緩衝大時,本人的輸入會較晚確定 → 畫面上: 緩衝小時在別人眼中會頓一下,緩衝大時本人的技能結果較晚出現(輸入延遲)
症狀: 卡頓, 輸入延遲 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 遊戲開發團隊(用戶端開發)
線路抖動大的人,輸入會成批抵達,因此常被伺服器的速度、冷卻檢查攔下。
為什麼: 特定電信業者、地區線路的抖動在晚間變大 → 於是: 伺服器把成批抵達的正常輸入判定為加速或違反冷卻 → 畫面上: 只有該電信業者的玩家出現拉回、技能被拒,嚴重時被伺服器踢出而斷線
症狀: 拉回, 吃指令/回檔, 斷線 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 基礎設施團隊(網路基礎設施)
在所有人必須在指定瞬間一起反應的團隊副本機制中,一個慢的人反應太晚,就會讓整個隊伍失敗。
為什麼: 「全員同時散開」、「由一人按下按鈕」這類共同機制 → 於是: 慢的人較晚看到預兆,輸入也較晚抵達 → 畫面上: 因為那一個人而團滅,其他隊友覺得「都是 lag 的人害的」
症狀: 吃指令/回檔, 輸入延遲 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 遊戲開發團隊(用戶端開發)
有些遊戲為了減輕伺服器負載,把怪物移動的計算交給附近某位玩家的用戶端。那個人線路差時,該怪物在所有人的畫面上都會動得很怪。
為什麼: 伺服器把怪物移動計算交給最近(或最先到)的玩家用戶端 → 於是: 負責者的結果回報延遲,或成批抵達伺服器 → 畫面上: 只有那隻怪物在周圍所有人畫面上頓一下後瞬移。負責者本人的畫面上卻正常
症狀: 瞬移, 卡頓, 快轉 · 主要負責 遊戲開發團隊(伺服器開發)
累積了數千個道具或信件,或好友、封鎖名單、buff 特別多的角色,在登入、儲存與通知周圍玩家時要處理的量是別人的好幾倍。與線路無關,只有用那個角色時才慢。
為什麼: 練了很久的角色,背包、信箱裡累積了數千個道具或活動獎勵 → 於是: 每次登入、切換地圖、儲存都要讀寫那麼多 DB 資料,要送給周圍的裝備與 buff 資訊也很大 → 畫面上: 只有那個角色進場載入很久,開背包或信箱時會頓一下。若伺服器在遊戲執行緒上等待儲存完成,連周圍的人也會短暫定格
症狀: 連不上/無限讀取, 輸入延遲, 定格 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 基礎設施團隊(DB 基礎設施)
兩個角色在不同的頻道或實例(instance),或處在依任務進度決定能看到哪些 NPC 的不同「相位」時,看到的是不同的世界。
為什麼: 第二個角色被分配到其他頻道,或任務階段不同 → 於是: 伺服器不會把該 NPC 送給那個角色(正常) → 畫面上: 只有一邊沒有 NPC。看起來像 bug,但符合設計
症狀: 看不見/幽靈物件 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 遊戲開發團隊(用戶端開發)
一進入 zone,伺服器就送出周圍 NPC 的出現通知,但用戶端還在載入地圖,於是把通知丟掉。
為什麼: 伺服器在進場處理後立即送出周圍物件的出現通知 → 於是: 用戶端正在載入,訊息處理常式(handler)還沒準備好,於是丟棄通知 → 畫面上: 伺服器當作已經送過,不會再送。在 NPC 離開視野再回來之前都看不見
症狀: 看不見/幽靈物件 · 主要負責 遊戲開發團隊(用戶端開發) · 協同 遊戲開發團隊(伺服器開發)
角色登錄到視野格狀(grid)的瞬間,與 NPC 換到其他格子的瞬間重疊時,該 NPC 的出現通知可能會漏掉。
為什麼: 進場、換頻道、傳送的處理與 NPC 移動在同一瞬間重疊 → 於是: 在「新進入視野的物件」計算中漏掉了那個 NPC → 畫面上: 只有特定幾隻 NPC 看不見,或早已離開的 NPC 還留著
症狀: 看不見/幽靈物件 · 主要負責 遊戲開發團隊(伺服器開發)
在伺服器只送「與上次不同的部分」的方式中,最初送一次的完整資訊(基準)遺失時,之後的變化量就無法套用。
為什麼: 物件的完整資訊(基準)封包遺失,或在處理前被丟棄 → 於是: 用戶端沒有可以套用後續變化量的對象,只好忽略 → 畫面上: 那個物件看不見,或過了很久才突然出現
症狀: 看不見/幽靈物件, 瞬移 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 遊戲開發團隊(用戶端開發)
反過來,漏掉「已消失」的通知時,早已死亡或離開的 NPC、玩家會只留在自己的畫面上。
為什麼: 死亡、離場、離開視野的通知遺失或順序錯亂 → 於是: 用戶端認為那個物件還在 → 畫面上: 打了也沒反應的怪物、早已離開的玩家還站在原地
症狀: 看不見/幽靈物件 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 遊戲開發團隊(用戶端開發)
踏進 zone 的瞬間,伺服器會一次送出周圍數十~數百個物件的出現資訊。若用不可靠(unreliable)通道送出,或在載入中無法讀取 socket 的期間接收緩衝區溢位,就會有一部分消失且不再重送。
為什麼: 剛進場時,出現資訊在短時間內大量湧入 → 於是: 載入中的用戶端太晚讀取 socket,使 OS 接收緩衝區溢位;或大型 UDP 封包被分段,只要遺失一個分段就整個消失。若是不可靠通道,也不會重送 → 畫面上: 只有載入較慢的那個用戶端少了幾隻 NPC。離開視野再回來就看得到
症狀: 看不見/幽靈物件 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 遊戲開發團隊(用戶端開發)
死亡的 NPC 重新出現時,伺服器若再次使用同一個物件 ID,在這段期間漏掉消失通知的用戶端,就會把新的 NPC 誤認成舊的 NPC。
為什麼: NPC 死亡後以同一個物件 ID 重新出現 → 於是: 漏掉消失通知的用戶端認為是「已知的物件」,忽略出現通知或維持死亡狀態 → 畫面上: 只有一邊的畫面上沒有 NPC 或看到它倒在地上,有時還會以其他 NPC 的外觀出現
症狀: 看不見/幽靈物件 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 遊戲開發團隊(用戶端開發)
用戶端若設計成使用固定的本機 port,同一台電腦的第二個用戶端就無法使用該 port,或與第一個用戶端分食封包。
為什麼: 兩個用戶端都要開同一個本機 UDP port(用重複使用選項硬是共用) → 於是: OS 只把進來的封包交給其中一個 socket,或不保證由哪一個接收。分享器與伺服器也把兩個用戶端看成同一個位址 → 畫面上: 其中一邊收不到世界封包,看不見 NPC 與其他玩家,或斷線
症狀: 看不見/幽靈物件, 斷線, 連不上/無限讀取 · 主要負責 遊戲開發團隊(用戶端開發) · 協同 遊戲開發團隊(伺服器開發)
伺服器或中介伺服器以 IP 或裝置 ID 區分連線時,會把同一台電腦(同一個公用 IP)的兩個用戶端視為同一個人。
為什麼: session 表以 IP 或 IP + 裝置 ID 建立 → 於是: 第二個用戶端的資訊覆蓋或混入第一個 session → 畫面上: 一邊看不見 NPC,另一邊斷線或收到別人的資訊
症狀: 看不見/幽靈物件, 斷線 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 遊戲開發團隊(用戶端開發)
安全模組或伺服器政策限制同一台電腦開多個用戶端時,第二個用戶端會無法執行或連線,或者先開的那一個會斷線。有些遊戲只封鎖額外用戶端的功能。
為什麼: 安全模組偵測到重複執行,或伺服器限制同一裝置的額外連線 → 於是: 拒絕第二次執行或連線,或切斷其中一邊。少數情況只封鎖額外用戶端的部分功能 → 畫面上: 連不上,或其中一邊斷線。在只封鎖功能的遊戲中,只有一邊看不見 NPC 或商店
症狀: 連不上/無限讀取, 看不見/幽靈物件, 斷線 · 主要負責 遊戲開發團隊(用戶端開發) · 協同 遊戲開發團隊(伺服器開發)
背景視窗中的用戶端,會被遊戲、引擎、OS 降低畫格數與處理量。收到的封包來不及處理,就會積壓或溢位。
為什麼: 遊戲選項或顯示卡驅動程式的背景畫格限制(例:NVIDIA 驅動程式可在每秒 20~200 之間指定)、省電、引擎的背景暫停設定。OS 也會優先把 CPU、GPU 分配給前景視窗 → 於是: 每個畫格處理的封包數減少,佇列堆積,接收緩衝區溢位時就被丟棄 → 畫面上: 把視窗切到前景時一口氣全部冒出來,或部分 NPC 始終看不見
症狀: 看不見/幽靈物件, 快轉, 斷線 · 主要負責 遊戲開發團隊(用戶端開發) · 協同 外部(外部)
兩個用戶端同時寫入同一個快取資料夾或鎖定檔案時,其中一邊會載入不了 NPC 模型或貼圖。
為什麼: 兩個用戶端同時寫入同一個安裝資料夾裡的快取、更新檔案 → 於是: 檔案鎖定失敗,或讀到寫到一半的檔案而載入失敗 → 畫面上: 名字還在卻沒有角色模型,或 NPC 是透明的
症狀: 看不見/幽靈物件 · 主要負責 遊戲開發團隊(用戶端開發)
兩個用戶端分著用顯示記憶體時,沒有空間載入新需要的模型與貼圖,部分物件就畫不出來。
為什麼: 兩個用戶端分用 VRAM 與 RAM。OS 有時也會先縮減背景視窗的顯示記憶體配額 → 於是: 引擎無法載入新的模型與貼圖,或不斷卸載又重新載入 → 畫面上: NPC 很晚才出現、模糊或看不見,畫面卡頓
症狀: 看不見/幽靈物件, 卡頓 · 主要負責 遊戲開發團隊(用戶端開發) · 協同 外部(外部)
顯示人數上限、隱藏 NPC 名字與模型、低規格模式這類選項,在兩個用戶端設定不同時,看到的東西就不同。
為什麼: 只有一個用戶端開了「周圍角色顯示數量上限」或低規格模式 → 於是: 不繪製遠處或優先順序低的 NPC(正常) → 畫面上: 只有一邊沒有 NPC
症狀: 看不見/幽靈物件 · 主要負責 遊戲開發團隊(用戶端開發)
第二個用戶端是不同的安裝版本或尚未更新完成時,會不認得伺服器送來的新 NPC ID,直接默默忽略。
為什麼: 其他資料夾的安裝版本,或在更新途中執行的用戶端 → 於是: 收到不認得的 NPC ID、模型 ID 就略過 → 畫面上: 只有新加入的 NPC 在一邊看不見
症狀: 看不見/幽靈物件 · 主要負責 遊戲開發團隊(用戶端開發) · 協同 遊戲開發團隊(伺服器開發)
伺服器對每條連線的傳送量設上限、從近的開始送時,上限被設得較低的那一邊,會較晚收到或收不到遠處的 NPC。
為什麼: 在人多的地方,伺服器在每條連線的傳送量上限內依重要度依序傳送 → 於是: 頻寬推估偏低的連線(例:因為是背景視窗而接收確認較慢的那一邊),會一直延後後段的物件 → 畫面上: 遠處的 NPC 只在一邊較晚出現或看不見
症狀: 看不見/幽靈物件, 輸入延遲 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 遊戲開發團隊(用戶端開發)
用戶端推估的伺服器時間有誤時,會把剛抵達的物件資訊當成「還在未來」而暫緩,或當成「太舊」而丟棄。
為什麼: 某一個用戶端的伺服器時間推估大幅偏差(在載入中測量、從省電喚醒) → 於是: 內插的基準時間與物件資訊的時間對不上 → 畫面上: 物件很晚才出現,或看起來停住不動
症狀: 看不見/幽靈物件, 卡頓 · 主要負責 遊戲開發團隊(用戶端開發)
伺服器指標上的「TCP 重傳(Retransmission)」增加時,lag 回報往往也會跟著增加。重傳代表「封包遺失了」或「誤判為遺失」。原因可能出在從 Wi-Fi 到伺服器網路卡的路徑上任何一處,而在遊戲這種稀疏地送出小封包的連線上,只要遺失一個封包,就可能擴大成數百 ms 的定格。本章整理重傳的根本原因、找出原因的方法與解決方向。
| 種類 | 何時發生 | 復原所需時間 | 遊戲中的樣子 |
|---|---|---|---|
| 快速重傳 Fast retransmit | 後面的封包先抵達,接收端通知「中間缺了」(重複 ACK、SACK)時 | 往返時間 + 後面 3 個封包抵達所需的時間 | 短暫頓一下。封包越密集越快 |
| RACK、TLP 以時間判斷遺失,重送尾端封包 | 較晚送出的封包已抵達,但前面的封包過了一定時間仍未到時;或是一段時間沒有 ACK 時,把最後一個封包再送一次 | 後面封包的確認一到就很快重送(RACK。可能只是順序對調,所以會多等約往返時間的 1/4)。沒有後續封包時約為往返時間的 2 倍(TLP),尚未確認的封包只有一個時,考量延遲 ACK 會再多等 200ms | thin stream 上也只是相對短暫地頓一下。新版 Linux 預設 |
| RTO 重傳 Retransmission timeout | 完全沒有訊號、等待時間到期時 | 往返時間 + 至少 200ms,每次失敗加倍 | 數百 ms~數秒的定格後快轉,拖久了會斷線 |
| SYN 重傳 | 連線請求本身遺失時(連線等待佇列(backlog)溢位、防火牆阻擋) | 1 秒、2 秒、4 秒、8 秒 ……(Linux 6.5 以上會先以 1 秒間隔重送最多五次再開始加倍,舊版 Windows 從 3 秒開始) | 按下連線後,延遲剛好落在 1 秒、3 秒這種整秒數,持續失敗就會連不上/無限讀取 |
| 不必要的重傳 Spurious retransmission | 沒有遺失,只是晚到或順序對調,被誤認為遺失而重送 | 沒有需要復原的東西,卻只有傳送量被縮減(Linux 若透過 DSACK 或時間戳記偵測到,有時會撤回縮減;DSACK 是接收端的「已經收到」通知) | 浪費頻寬,大量傳輸速度下降。指標上只有重傳率偏高 |
| Zero window probe 容易與重傳混淆 | 接收端緩衝區滿了、處於「先別送」狀態時,送出端用來確認狀態的封包 | 直到接收端開始讀取 | 定格。線路正常,問題出在接收端程式沒有及時讀取 |
重傳率是「送出的封包中重送的比例」。開機以來的累計值會淹沒近期的變化,所以要用 1 分鐘這類固定間隔內的增加量來計算。沒有公認的標準,但整個伺服器平均值的大致感覺是:低於 0.1% 為健康,0.1~1% 代表部分玩家偶爾頓一下,超過 1% 會有很多玩家感覺到,超過 3% 屬於嚴重。手機、海外玩家多的遊戲,平時的數值會更高。所以比起只看一個數字,更要同時看比平時增加了幾倍。平均值會被少數不良線路拉動,所以按地區、電信業者、伺服器、時段拆開來看,是找出原因的捷徑。如果中間有先接下連線、再重新連到伺服器的設備(proxy、部分負載平衡器與閘道),遊戲伺服器的指標只會反映該設備與伺服器之間的區段。玩家端的重傳要在那台設備上看。
| 在哪裡看 | 看什麼 | 可以知道什麼 |
|---|---|---|
| 整個伺服器(Linux) | 以 1 分鐘間隔執行兩次 nstat 的增加量:TcpRetransSegs ÷ TcpOutSegs,以及 TcpExt 系列的 TCPTimeouts、TCPLossProbes、TCPLossProbeRecovery、TCPLostRetransmit、TCPSpuriousRTOs、TCPDSACKRecv、TCPSynRetrans | 重傳率、走到 RTO 的次數、送出 TLP 的次數與其中實際補上遺失的次數、連重送的封包都再次遺失的次數、連線請求重傳。DSACK、Spurious 多代表「沒有遺失卻重送」。Linux 的 OutSegs 不含重傳的部分,嚴格的比例是 RetransSegs ÷ (OutSegs + RetransSegs),但在 1% 左右差異很小 |
| 每條連線(Linux) | ss -ti 的 retrans(目前復原中/累計)、rto、backoff、rtt、cwnd、lost、reordering、bytes_retrans | 是否只有特定玩家、地區重傳多,RTO 拉長了多少(backoff 是 RTO 連續加倍的次數)。bytes_retrans ÷ bytes_sent 就是該連線的重傳率 |
| 逐筆重傳(Linux) | eBPF 工具 tcpretrans(bcc)。-c 為依連線彙總,-l 包含 TLP | 每發生一次重傳就顯示一行對端 IP、port、連線狀態。不用封包擷取就能輕量確認重傳集中在哪個玩家 IP 網段、哪台伺服器 |
| 伺服器網路卡 | ip -s -s link 的 dropped、missed、crc,ethtool -S 的 rx_missed_errors、rx_no_buffer_count、rx_crc_errors 等(名稱依驅動程式而異,mlx5 是 rx_out_of_buffer、rx_discards_phy),/proc/net/softnet_stat 的第 2 欄(dropped)、第 3 欄(time_squeeze) | 伺服器網路卡是否一收到就丟棄(ring buffer、CPU),或是線材、光模組不良(CRC)。softnet_stat 每顆 CPU 一行,以 16 進位表示。time_squeeze 持續增加,代表負責接收處理的核心無法及時完成工作 |
| 雲端網路 | AWS ENA 看 ethtool -S 的 bw_in_allowance_exceeded、bw_out_allowance_exceeded、pps_allowance_exceeded、conntrack_allowance_exceeded、linklocal_allowance_exceeded。預設的 CloudWatch 畫面沒有這些值,要用 CloudWatch agent 另外收集 | 是否在執行個體上限處被默默丟棄。數值持續增加就是超過上限。其他雲端也有依 VM 規格而定的頻寬、連線數上限 |
| 交換器、路由器、防火牆 | port 的 CRC 與輸入錯誤、輸出丟棄、policer 超量、session 表使用量、丟棄 log | 是否在資料中心設備區段被丟棄。5 分鐘平均使用率很低、輸出丟棄卻增加,就是 microburst(極短時間內的流量湧入) |
| 路徑 | 用 mtr、pathping 看一路延續到終點的遺失。要送數百次以上才看得出 1% 左右的遺失,用與遊戲相同的 TCP port 送(mtr -T -P PORT)會更準確 | 從第幾個躍點開始出現遺失。只有中間某一個躍點顯示遺失、後面都正常時,只是那台設備限制了測量用的回應(ICMP)。去程與回程的路徑可能不同,所以也要從伺服器端往玩家端測量 |
| 封包擷取(兩端) | Wireshark 篩選器 tcp.analysis.retransmission,以及同屬 tcp.analysis. 系列的 fast_retransmission、spurious_retransmission、duplicate_ack、lost_segment、zero_window | 原始封包在送出端的擷取中有、接收端沒有,就是在兩端之間遺失。接收端也有的話,是不必要的重傳,或 ACK 在回程中延遲或遺失。在接收端伺服器 ring buffer 被丟棄的封包,在擷取中也會顯示成「在兩端之間遺失」,所以要搭配網路卡計數器一起看 |
| Windows Server | 效能監視器的 TCPv4\Segments Retransmitted/sec ÷ Segments Sent/sec、Network Interface\Packets Received Discarded、netsh int tcp show global、pktmon(Windows 10 1809、Windows Server 2019 以上內建) | 重傳率趨勢、網路卡是否一收到就丟棄、TCP 設定、在 Windows 內部的哪個環節被丟棄 |
確認順序:和基礎設施團隊一起查時,照以下順序最快。
回報中如果有時間(精確到秒)、玩家的電信業者與地區、連線的伺服器、症狀名稱,基礎設施團隊就能立刻照這個順序查。
1. 不要遺失(根本解決)
2. 快速復原
tcp_thin_linear_timeouts,Linux 6.15 以上可用 TCP_RTO_MAX_MS 降低 RTO 上限TCP_NODELAY(Nagle 把新封包累積起來不送的話,RACK 可用的後續封包就沒了)ip route … rto_min)TCP_USER_TIMEOUT 和遊戲心跳封包快速切斷失效的連線並重新連線3. 降低對重傳的敏感度(架構)
TCP_NOTSENT_LOWAT 等)。長時間定格後的快轉會變短與重傳復原相關的設定大多是作業系統(kernel)設定,能只對遊戲連線個別開啟的 socket 選項只有幾個。常因名稱而被混淆的 TCP_NODELAY 並不會加快復原。不過如果不開啟(使用 Nagle),復原期間新封包會更晚送出。下表以 Linux 為準,Windows 的名稱與支援範圍不同。
| 設定 | 設定位置 | 改變什麼 | 注意 |
|---|---|---|---|
TCP_NODELAY | socket 選項 | 關閉 Nagle。小訊息不累積,立即送出 | 消除即使沒有遺失也會出現的 40~200ms 等待。重傳計時器(RTO)本身不變。不過 Nagle 開著的話,等待復原期間新封包也會被綁住,復原後還要再等一次往返;快速重傳與 RACK 所依賴的後續封包也送不出去,容易走到 RTO。遊戲通常會開啟 |
net.ipv4.tcp_recovery (RACK) | kernel 設定 | 以時間判斷遺失。不怕順序對調,thin stream 也能快速復原 | 預設值 1(開啟)。Linux 4.4 加入,約在 4.18 時成為現在的樣子。從 6.17 起 RACK 是唯一的遺失判斷方式,改成 0 也沒有效果。在沒有 SACK 的連線上無法運作 |
net.ipv4.tcp_early_retrans (TLP) | kernel 設定 | 一段時間(約往返時間的 2 倍)沒有 ACK 時,把最後一個封包再送一次,及早發現尾端遺失(tail loss) | 預設值 3(開啟),0 為關閉。需要 SACK 才能運作。尚未收到 ACK 的封包(in-flight)只有一個時,會再多等 200ms,變得和 RTO 差不多 |
net.ipv4.tcp_sack, tcp_dsack, tcp_timestamps | kernel 設定 | 選擇性 ACK(SACK,通知中間缺少的部分)、重複接收通知(DSACK)、往返時間測量(時間戳記) | 預設全部開啟。有些伺服器在 2019 年 SACK 資安問題時關閉後一直沒恢復。SACK 關閉時,RACK、TLP 也會跟著無法運作 |
net.ipv4.tcp_thin_linear_timeouts / TCP_THIN_LINEAR_TIMEOUTS | kernel 設定/socket 選項 | 尚未收到 ACK 的封包(in-flight)少於 4 個的連線,前 6 次 RTO 不加倍 | 預設關閉。可用 socket 選項只對遊戲連線開啟。不會縮短第一次的 RTO |
TCP_RTO_MAX_MS / net.ipv4.tcp_rto_max_ms | socket 選項/kernel 設定(Linux 6.15 以上) | 降低持續加倍的 RTO 上限(預設 120 秒)。最小 1 秒 | 避免連續遺失後 RTO 膨脹到數十秒。判定失效連線也會跟著變快 |
net.ipv4.tcp_mtu_probing | kernel 設定 | 大封包持續消失時縮小封包尺寸,穿過 MTU 黑洞 | 預設值 0(關閉)。1 = 重傳持續約 3 秒、懷疑有黑洞時才縮小(在那之前會停住)。2 = 一開始就從 1,024 位元組開始,逐步嘗試加大 |
ip route … rto_min | 路由設定 | 降低該路由的 RTO 最小值(預設 200ms) | 只用在伺服器之間的內部網路。在網際網路區段降低會增加不必要的重傳。Linux 6.11 以上的 net.ipv4.tcp_rto_min_us 是整台伺服器的值,網際網路連線也會一起改變。6.15 以上可以用 socket 選項 TCP_RTO_MIN_US 只對內部連線降低 |
TCP_USER_TIMEOUT | socket 選項 | 重傳持續時,放棄連線之前的時間 | 不會加快復原。讓失效的連線及早切斷並重新連線。沒有設定時,Linux 會重傳 15 次左右、持續約 15 分鐘後才切斷(tcp_retries2) |
SO_KEEPALIVE + TCP_KEEPIDLE 等 | socket 選項 | 確認閒置連線是否還活著 | 與重傳無關。用於維持 NAT、LB 的 mapping,以及偵測失效的連線 |
fq 佇列 + SO_MAX_PACING_RATE、BBR | 佇列設定/socket 選項/kernel 設定 | 把封包平均分散送出,減少突發流量(burst,一次集中送出)造成的遺失 | 屬於「預防」遺失。與復原速度無關 |
Wi-Fi 與行動網路在無線區段會先重傳幾次,仍然失敗就丟棄封包。被丟棄的封包,TCP 要過好一段時間才會重送。
為什麼: 訊號弱或干擾嚴重,無線區段的傳輸接連失敗 → 於是: 超過無線設備的重試上限(通常是數次~十幾次)就丟棄封包 → 畫面上: 定格的時間等於 TCP 等待重傳的時間,後面的封包在接收緩衝區等候,之後快轉
症狀: 定格, 快轉, 瞬移 · 主要負責 外部(外部) · 協同 基礎設施團隊(伺服器基礎設施), 遊戲開發團隊(伺服器開發), 遊戲開發團隊(用戶端開發)
分享器、電信業者之間的互連區段、資料中心線路這類最窄的地方,佇列一滿就會丟棄新進來的封包。
為什麼: 影片、下載與其他使用者的流量把瓶頸區段塞滿 → 於是: 佇列滿的期間,新到的封包接連被丟棄(tail drop)。沒被丟棄的封包也要在塞滿的佇列尾端等待 → 畫面上: 多個封包同時消失,長時間定格後快轉,晚間時段常見
症狀: 定格, 快轉, 拉回 · 主要負責 基礎設施團隊(網路基礎設施) · 協同 外部(外部), 遊戲開發團隊(用戶端開發)
伺服器每個 tick 把數千人份的更新在一瞬間集中送出時,交換器的小緩衝區或雲端的瞬間上限不到 1ms 就會溢位,部分封包因此被丟棄。
為什麼: 每個 tick 開始的瞬間,把要送給所有人的封包一次送出 → 於是: 匯集多台伺服器流量的交換器 port 緩衝區(每個 port 數百 KB~數 MB)或雲端執行個體的上限瞬間溢位(平均使用率很低) → 畫面上: 多人同時瞬移、頓一下,看平均指標找不出原因
症狀: 瞬移, 定格, 快轉 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 基礎設施團隊(伺服器基礎設施), 基礎設施團隊(網路基礎設施)
電信業者的資費方案、雲端執行個體的上限、DDoS 防護設備,有時會把超過規定速度的封包直接丟棄,不放進佇列。
為什麼: 瞬間傳送量超過允許速率或允許突發量 → 於是: 超出的封包不經佇列直接丟棄(policing) → 畫面上: 每逢突發量大的瞬間就有多個封包消失,定格後快轉;平均速度看起來低於上限
症狀: 定格, 快轉, 瞬移 · 主要負責 基礎設施團隊(網路基礎設施) · 協同 基礎設施團隊(伺服器基礎設施), 遊戲開發團隊(伺服器開發)
纜線損壞、沾了灰塵的光纖接頭、壽命將盡的光模組會造成位元錯誤,損壞的封包會被設備默默丟棄。
為什麼: 線材、光模組、接頭不良導致位元翻轉 → 於是: 設備丟棄檢查碼(CRC)不符的封包 → 畫面上: 只有經過該路徑的人持續出現短暫頓一下後快轉,與時段無關
症狀: 定格, 快轉, 瞬移 · 主要負責 基礎設施團隊(網路基礎設施) · 協同 基礎設施團隊(伺服器基礎設施), 外部(外部)
一端設為自動協商、另一端把速度與雙工固定時,其中一端會以半雙工運作,每當負載升高就因碰撞而遺失封包。
為什麼: 只有其中一台設備把速度與雙工設成固定值 → 於是: 一端以全雙工運作、另一端以半雙工運作,發生碰撞與延遲碰撞 → 畫面上: 平常一切正常,流量一增加,經過該設備的所有人都會定格後快轉
症狀: 定格, 快轉 · 主要負責 基礎設施團隊(網路基礎設施) · 協同 基礎設施團隊(伺服器基礎設施)
封包已經到了伺服器,卻因 NIC 的 ring buffer(暫存剛抵達封包的緩衝區)溢位,或 kernel 負責接收處理的 CPU 核心滿載而被丟棄。
為什麼: 連線人數暴增、中斷集中在單一核心、虛擬機器 CPU steal、虛擬交換器過載 → 於是: 在 ring buffer(rx_missed_errors 等,名稱依驅動程式而異)或 kernel 接收佇列(softnet dropped)被丟棄 → 畫面上: 人潮湧入時,整個伺服器同時出現輸入慢半拍才生效、頓一下
症狀: 輸入延遲, 定格, 快轉, 瞬移 · 主要負責 基礎設施團隊(伺服器基礎設施)
防火牆或 Linux 連線追蹤(conntrack,把經過的連線記錄在表中的功能),在表已滿或判斷連線狀態不符時,會丟棄封包。
為什麼: 連線追蹤表已滿(table full),或去回程路徑不同,只有單一方向經過防火牆(非對稱路由) → 於是: 防火牆把封包視為「未知連線」或「序號超出視窗範圍」而丟棄 → 畫面上: 表滿了之後新連線會被擋下;路徑不一致時,只有走該路徑的人在反覆重傳後斷線
症狀: 定格, 斷線, 連不上/無限讀取 · 主要負責 基礎設施團隊(網路基礎設施) · 協同 基礎設施團隊(伺服器基礎設施), 遊戲開發團隊(伺服器開發), 遊戲開發團隊(用戶端開發)
防火牆、入侵防禦設備(IPS)、DDoS 防護設備會逐一檢查經過的封包。從超過檢查能力的那一刻起,處理不了的封包就會被丟棄。
為什麼: 尖峰時段或活動期間,每秒湧入數十萬個以上的小型遊戲封包,或是檢查規則太重 → 於是: 設備的 CPU 或每秒封包數達到上限,封包在設備上被丟棄。誤判時連正常封包也會被擋 → 畫面上: 該設備後方的所有伺服器同時出現定格、瞬移,只在人潮湧入時加劇
症狀: 定格, 快轉, 瞬移, 斷線 · 主要負責 基礎設施團隊(網路基礎設施) · 協同 遊戲開發團隊(伺服器開發)
中途區段能接收的大小變小,而「封包太大」的通知(ICMP)又被擋掉時,大封包不管重送幾次都會一直消失。
為什麼: VPN 或通道區段的最大大小變小,大小超過的通知被防火牆擋掉 → 於是: 傳送端不知道原因,持續重傳同一個大封包,RTO 每次加倍 → 畫面上: 平常一切正常,但在背包、人多的地方、進場載入這類有大量資料往來的瞬間,連後面的小封包也全部卡住而定格,最後斷線或無限讀取
症狀: 定格, 斷線, 連不上/無限讀取 · 主要負責 基礎設施團隊(網路基礎設施) · 協同 基礎設施團隊(伺服器基礎設施), 遊戲開發團隊(伺服器開發)
中途設備刪除閒置連線的 mapping(記錄這條連線要轉送到哪裡的項目)後,接下來送出的封包就無法送達。連線會不斷重傳直到斷線,或是設備回傳拒絕連線(RST)而立刻斷線。
為什麼: 有一段時間沒有任何封包往來的連線(暫離、大廳) → 於是: 分享器 NAT、電信業者 CGNAT、防火牆、負載平衡器、雲端安全群組刪除閒置的 mapping → 畫面上: 再次移動的瞬間接連重傳,最後斷線,或是直接斷線
症狀: 斷線, 定格 · 主要負責 遊戲開發團隊(用戶端開發) · 協同 遊戲開發團隊(伺服器開發), 基礎設施團隊(網路基礎設施), 基礎設施團隊(伺服器基礎設施)
網際網路路由變更的那幾秒內,或是多條 ECMP 路徑中被分配到不良路徑的連線,會有封包消失。
為什麼: BGP 重新計算路由,或多條路徑(ECMP、LAG)中某一條的設備或線路不良 → 於是: 切換路由時暫時遺失,或只有走該路徑的連線持續遺失 → 畫面上: 突然定格幾秒後快轉,或是「重新連線就變好」(被分配到其他路徑)
症狀: 定格, 快轉, 瞬移 · 主要負責 基礎設施團隊(網路基礎設施) · 協同 遊戲開發團隊(伺服器開發), 外部(外部)
封包沒有消失,只是短暫地很晚才到;但這段延遲比 RTO 長時,傳送端就會判斷為遺失而重傳。
為什麼: bufferbloat、Wi-Fi 省電、行動網路無線狀態切換、虛擬機器暫停,造成瞬間延遲達數百 ms → 於是: RTO 先到期而重傳,原本的封包也隨即抵達(接收端收到重複的封包) → 畫面上: 定格與快轉是延遲飆升本身造成的。不必要的重傳幾乎不會拉長定格,只會推高重傳指標,因而被誤認為遺失
症狀: 定格, 快轉, 輸入延遲 · 主要負責 外部(外部) · 協同 基礎設施團隊(伺服器基礎設施), 遊戲開發團隊(用戶端開發)
封包經過多條路徑或聚合鏈路時順序被打亂,接收端會以重複 ACK 告知「有封包缺漏」,傳送端就把其實已正常送達的封包再送一次。
為什麼: 依封包分配路徑的設備、依封包分散傳送的 LAG(鏈路聚合)、路由切換的瞬間,都會打亂封包順序 → 於是: 後面的封包先到,累積 3 個重複 ACK → 快速重傳 → 畫面上: 零星往來的遊戲封包幾乎不受影響。人多處的大型狀態更新與更新檔下載會變慢,偶爾出現卡頓
症狀: 卡頓, 輸入延遲 · 主要負責 基礎設施團隊(網路基礎設施) · 協同 基礎設施團隊(伺服器基礎設施)
資料已順利送達,但「收到了」的 ACK 在塞滿的上傳佇列裡延遲或消失時,傳送端會判斷為遺失而重傳。
為什麼: 家中有人上傳影片或做雲端備份,把上傳頻寬塞滿 → 於是: ACK 在分享器佇列裡延遲數百 ms,或因溢位而被丟棄 → 畫面上: 伺服器送來的遊戲封包大致準時到達。自己的輸入堆在同一個上傳佇列裡而晚送出,造成輸入延遲、拉回,偶爾出現不必要的重傳
症狀: 輸入延遲, 拉回 · 主要負責 外部(外部) · 協同 遊戲開發團隊(用戶端開發)
RTO 最小值調得太低時,只要稍微延遲就會產生不必要的重傳;預設值(200ms)對遊戲來說又太長,每遺失一次就要停住很久。
為什麼: 為了資料中心環境把 RTO 最小值大幅調低,或在網際網路區段直接沿用預設值 → 於是: 太低時,瞬間延遲也會引發大量重傳;太高時,每次遺失都要等很久 → 畫面上: 預設值時,遺失一次就定格數百 ms 後快轉;調得太低時定格變短,但不必要的重傳暴增,浪費線路頻寬
症狀: 定格, 快轉, 輸入延遲 · 主要負責 基礎設施團隊(伺服器基礎設施) · 協同 遊戲開發團隊(伺服器開發)
像遊戲這樣零星送出小封包時,在湊齊「後續 3 個封包」之前 RTO 就先到了。同樣的遺失,停住的時間比大量傳輸長得多。
為什麼: 封包間隔約 100ms,尚未收到 ACK 的封包(in-flight)沒有幾個 → 於是: 要湊齊 3 個重複 ACK 得花 300ms 以上,所以 RTO(ping + 200ms)先觸發,連續遺失時每次加倍 → 畫面上: 遺失一次就定格 0.3 秒左右,連重送的封包也遺失時,定格將近 1 秒後快轉
症狀: 定格, 快轉 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 基礎設施團隊(伺服器基礎設施), 遊戲開發團隊(用戶端開發)
部分防火牆或加速設備刪除或改寫 TCP 選項時,遺失多個封包後一個往返只能復原一個,或是視窗(一次可送出的量)變小,傳輸因此變慢。
為什麼: 防火牆的「TCP 正規化」、老舊的加速設備移除 SACK、時間戳記、視窗縮放選項 → 於是: 遺失多個封包時每個往返只能復原一個,視窗被限制在 64KB → 畫面上: 每次遺失時定格的時間都長得多(沒有 SACK 就無法使用 RACK-TLP),恢復後快轉。更新檔這類大量傳輸也會變慢
症狀: 定格, 快轉 · 主要負責 基礎設施團隊(網路基礎設施) · 協同 基礎設施團隊(伺服器基礎設施)
接收端程式沒有及時讀取 socket、緩衝區塞滿時,傳送端會停止傳送,只送出 zero window probe。線路本身沒有問題。
為什麼: 用戶端的畫格更新停住、伺服器執行緒卡住,導致無法讀取 socket → 於是: 接收視窗變成 0,傳送端停止傳送、只送 probe(間隔越來越長) → 畫面上: 定格後快轉。封包擷取中看得到「ZeroWindow」,沒有遺失
症狀: 定格, 快轉 · 主要負責 遊戲開發團隊(用戶端開發) · 協同 遊戲開發團隊(伺服器開發), 基礎設施團隊(伺服器基礎設施)
連線請求因連線等待佇列(backlog)溢位或被防火牆阻擋而消失時,用戶端 OS 會從 1 秒後開始,逐步拉長間隔重送。
為什麼: 維護剛結束時連線暴增,伺服器的連線等待佇列溢位,或是防火牆、DDoS 防護丟棄 SYN → 於是: 用戶端 OS 從 1 秒後開始以固定間隔重傳 SYN(舊版 Linux 為 1 秒 → 2 秒 → 4 秒) → 畫面上: 按下連線按鈕後,延遲剛好是 1 秒、3 秒這種整秒數,持續失敗就連不上/無限讀取
症狀: 連不上/無限讀取 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 基礎設施團隊(伺服器基礎設施), 基礎設施團隊(網路基礎設施), 遊戲開發團隊(用戶端開發)
同樣是 lag,修的地方也不同。用戶端、伺服器程式碼與同步設計由遊戲開發團隊負責,線路、網路設備、伺服器設備、DB 設備由基礎設施團隊負責。玩家的電腦與家用網路、電信業者區段、雲端供應商端的問題,兩個團隊都無法直接修正,只能引導、提出請求或繞過。每張原因卡片都標示了主要負責與協同處理的單位,展開卡片的「數值參考、確認方法、各團隊因應」,就能看到各團隊分別要做的事。
| 負責單位 | 負責範圍 | 常用的解決手段 |
|---|---|---|
| 遊戲開發團隊用戶端 | 遊戲用戶端程式碼:畫格、GC、載入,內插、外插、預測,用戶端的網路處理(包含送出心跳封包、自動重新連線) | 修改程式碼、調整內插緩衝與預測、改變載入方式、心跳間隔與重新連線流程、用戶端更新 |
| 遊戲開發團隊伺服器 | 遊戲伺服器程式碼:tick、執行緒、鎖,同步設計,連線處理(accept 迴圈、listen 參數),心跳回應與斷線連線的清理,socket 選項,查詢與 transaction 設計 | 邏輯最佳化、非同步呼叫、分散 tick 與地圖負載、登入排隊、用 session token 接續、socket 選項(TCP_NODELAY 等)、查詢與索引設計、伺服器更新 |
| 基礎設施團隊網路 | 線路與 IDC 網路設備(交換器、路由器、防火牆、負載平衡器、DDoS 防護),雲端網路 ACL、VPC 路由、負載平衡器,電信業者與 peering | 設備設定與更換、擴充線路與 peering、變更路由、向電信業者升級處理(escalation)、調整負載平衡器與防火牆的閒置逾時與 session 上限 |
| 基礎設施團隊伺服器設備/OS | 伺服器設備與雲端執行個體(包含安全群組、連線追蹤),OS 與 kernel 設定,NIC,部署與監控環境 | 擴充、變更執行個體、kernel 設定(sysctl:somaxconn、conntrack 等)、安全群組配置與連線追蹤時間、NIC ring buffer 與中斷分散、調整 cron 與備份時間 |
| 基礎設施團隊DB 設備 | DB 伺服器與儲存裝置,DB 設定、複寫、備份,快取伺服器 | 擴充 DB、確保儲存裝置 IOPS、DB 參數與複寫設定、調整備份與檢查點 |
| 外部玩家/電信業者/雲端 | 玩家的電腦與家用網路、電信業者區段(我方合約之外)、雲端供應商 | 引導玩家(改用有線連線等)、向電信業者與雲端供應商提出請求、遊戲端繞過與緩解 |
粗體數字是該單位擔任主要負責的原因數,+數字是協同處理的原因數。點選格子,下方會顯示這些原因與該團隊要做的事。
主要負責是根本原因所在之處,或是能消除根本原因的單位。即使原因是線路或設備,遊戲開發團隊在這段期間也要用降低影響的設計(內插緩衝、輸入重複傳送、重新連線)撐住;原因若是伺服器程式碼,基礎設施團隊增加設備也只是暫時延後問題。常被混淆的界線規定如下。
表中的「先找」是第一個要找的單位,依符合該現象的原因卡片中各單位擔任主要負責的次數決定。列出兩個時,前者是擔任主要負責的卡片最多的單位,後者是一開始就要一起找來的單位。
| 現象 | 遊戲開發團隊要做的事 | 基礎設施團隊要做的事 | 優先查看的指標 |
|---|---|---|---|
| 特定電信業者、地區的遺失與抖動大 先找基礎設施團隊網路 | 自適應內插緩衝、輸入重疊傳送、耐遺失的 UDP 傳輸、用每條連線的遺失與重傳統計找出受影響玩家的 IP、port、時間,依線路狀態放寬移動驗證標準 | 用與遊戲相同的協定、port 雙向測量路徑(mtr),避開不良路徑,向電信業者升級處理,增加 peering 與線路 | 各電信業者的遺失率與抖動分布、重傳率 |
| TCP 重傳增加 先找基礎設施團隊網路遊戲開發團隊伺服器 | TCP_NODELAY、把一個 tick 的傳送分散在 tick 內、及時讀取 socket(防止 zero window)、用心跳封包維持 mapping、即時封包改走 UDP 或其他連線、不在傳送緩衝區堆積舊位置(TCP_NOTSENT_LOWAT) | 消除遺失點(線材、光模組、雙工、policer、防火牆連線追蹤、MTU)、調整 MSS、伺服器 ring buffer 與中斷分散、kernel 復原設定(RACK、tcp_mtu_probing) | 重傳增加量(nstat)、zero window 次數、NIC 與交換器 port 的丟棄、CRC 計數器 |
| 伺服器 CPU 飽和導致 tick 超出預算 先找遊戲開發團隊伺服器 | 最佳化視野計算與廣播、把 tick 拆到多個執行緒、拆分擁擠的地圖與頻道、讓工作執行緒數符合 CPU 上限、把 tick 處理時間記錄成指標 | 單核心效能(時脈)高的 CPU 與執行個體、各核心 CPU 使用率警示、確認 CPU steal 與容器 CPU 節流、把處理中斷的核心與 tick 執行緒的核心分開 | tick 處理時間、各核心 CPU 使用率、steal、節流次數(nr_throttled) |
| DB 回應延遲 先找遊戲開發團隊伺服器基礎設施團隊DB 設備 | 查詢、索引、transaction 設計(縮短 transaction、統一鎖定順序)、在遊戲執行緒之外非同步呼叫、合併查詢與快取、調整連線池大小與等待逾時 | 找出慢查詢、執行計畫、鎖等待並分享給遊戲開發團隊,檢查點、複寫、統計資訊更新設定,儲存裝置 IOPS,確認伺服器數 × 連線池大小在最大連線數以內,擴充 DB 設備 | 慢查詢 log、鎖等待、連線等待、複寫延遲、IOPS |
| 維護剛結束時連不上 先找遊戲開發團隊伺服器 | 讓接收連線的執行緒(accept 迴圈)不會因其他工作而停住、加大 listen 的 backlog 參數、登入排隊系統、合併登入查詢(消除 N+1)、用戶端重試間隔逐漸拉長並隨機分散 | kernel somaxconn 與 SYN cookie、防火牆與負載平衡器的 session 上限、伺服器 conntrack 與檔案描述子上限、DB 快取預熱、活動前預先擴展伺服器 | ListenOverflows、session 表與 conntrack 使用率、登入查詢數與連線等待 |
| 閒置一段時間就斷線 先找遊戲開發團隊用戶端 | 用戶端:以最短閒置逾時的一半以下為間隔送出心跳封包(即使有一個晚到或遺失,下一個也能在逾時前抵達),斷線時自動重新連線。伺服器:回應心跳封包,一段時間收不到就先清理連線,再用 session token 接續。 | 彙整路徑上負載平衡器、防火牆的閒置逾時(網路)與雲端安全群組的連線追蹤時間(伺服器設備/OS),分享給遊戲開發團隊,我方設備必要時調高。玩家分享器與電信業者 CGNAT 的逾時無法改變 | 斷線連線的閒置時間分布(集中在某個值附近,就是具有該逾時的設備)、網路類型(行動、有線) |
| 每到固定時間伺服器就停住 先找遊戲開發團隊伺服器基礎設施團隊伺服器設備/OS | 把整點活動、存檔、計時器、快取到期的時間隨機分散,批次查詢拆小、分批少量執行,明確指定暫停時間短的 GC | 分散 cron、備份、log 壓縮的時間並降低 I/O 優先順序,DB 備份在複本上進行、檢查點平均分散,確認磁碟 burst credit,限制備份傳輸速度 | 停住的時間點與工作排程(cron、備份、批次、檢查點)的對照,GC log |
| DDoS、流量暴增 先找基礎設施團隊網路 | 把遊戲流量模式(port、封包大小、每秒封包數)分享給基礎設施團隊,限制各帳號與角色的請求頻率,及早阻擋異常封包 | DDoS 防護(清洗)與配合遊戲流量的防護規則、隱藏伺服器位址、設備的每秒封包上限,以 IP 為準的限制要考慮電信業者共用 IP 與網咖 | 每秒封包數、設備 CPU 與丟棄、各地區與電信業者的連線失敗率(確認誤判) |
| 玩家的 Wi-Fi、電腦問題 先找外部玩家/電信業者/雲端遊戲開發團隊用戶端 | 遊戲內網路狀態顯示(ping、遺失)、依抖動自動調整內插緩衝長度、在出現延遲時的 log 中記錄網路類型與電腦 CPU 使用率、建議改用有線連線等提示文字 | 無法直接修正。同一電信業者、地區的回報集中時,重新歸類為線路問題 | 回報中的線路與裝置資訊、同一電信業者與地區的比例 |
遊戲開發團隊 → 基礎設施團隊
基礎設施團隊 → 遊戲開發團隊
兩個團隊共通:指定一位事故處理負責人,在同一個頻道留下依時間排序的紀錄,並事先告知下一次更新進度的時間。主要負責改由其他團隊擔任時,連同這份紀錄一起交接,避免重複做同樣的確認。結束後,用同一份紀錄修正對應原因卡片的負責單位與要做的事。
在玩家電腦或手機上執行的遊戲程式本身。即使網路完美,這裡的畫格一延遲,畫面就會卡頓。網路不好時能掩蓋得多好,也是在這裡決定。
遊戲 1 秒大約重複 60 次同樣的工作:讀取輸入、處理收到的封包、把遊戲狀態推進一步、繪製畫面。這個迴圈跑一次就是一個畫格,60FPS 的話每個畫格有 16.7ms(以 30FPS 執行的手機遊戲是 33.3ms)。一個畫格延遲多久,畫面就停住多久,接著在下一個畫格一口氣補上落後的部分。
在網路方面,用戶端的工作是「補足缺少的資訊」。其他玩家的位置是從伺服器斷斷續續送來的,所以要把中間連起來畫(內插);封包中斷時要靠推測繼續移動(外插);自己的角色則不等伺服器確認,先行移動顯示(預測)。這些技術失敗的樣子,就是瞬移、拉回、卡頓。
遊戲用戶端就像製作現場直播畫面的電視台副控室。現場(伺服器)的照片斷斷續續送來時,副控室把中間自然地接起來,看起來就像影片。照片晚到時沒有照片可接,畫面就會停住;副控室本身太忙,播出也會中斷。
某一個畫格的計算時間比平常多出好幾倍,畫面短暫停住。
為什麼: 技能特效暴增、大量生成(spawn)、整個 UI 重新整理,全擠在同一個畫格 → 於是: 無法在 16.7ms 內完成,花了 50~300ms → 畫面上: 畫面頓一下,下一個畫格所有東西一次移動到位
症狀: 卡頓, 定格 · 主要負責 遊戲開發團隊(用戶端開發)
回收用過即丟的記憶體(垃圾)期間,整個遊戲會暫停。特徵是以規律的間隔出現卡頓。
為什麼: 每個畫格都建立再丟棄暫時性的字串、陣列、List → 於是: 垃圾累積起來後,GC 暫停主執行緒進行回收 → 畫面上: 每隔幾秒~幾十秒規律地出現卡頓
症狀: 卡頓, 定格 · 主要負責 遊戲開發團隊(用戶端開發)
要繪製第一次出現的地圖、怪物、特效之前,得先讀取檔案並建立著色器,畫面因此停住。
為什麼: 進入新地圖,或第一次出現的技能、裝備、怪物登場 → 於是: 主執行緒等待讀取檔案與著色器編譯 → 畫面上: 只有第一次停住 0.1~1 秒,第二次起就正常
症狀: 定格, 卡頓 · 主要負責 遊戲開發團隊(用戶端開發)
在 HDD 這類較慢的儲存裝置上,讀取開放世界貼圖與模型的速度跟不上移動,物件會晚出現,或遊戲為了等待讀取而卡頓。
為什麼: 騎坐騎、傳送等快速移動,或進入人多的地方,一次需要大量新的貼圖與模型 → 於是: HDD 等較慢的儲存裝置無法以需要的速度讀取,讀取請求越積越多,部分載入還要主執行緒等到完成為止 → 畫面上: 貼圖有一段時間是模糊的,建築與角色晚出現;等待讀取的瞬間出現卡頓、定格
症狀: 看不見/幽靈物件, 卡頓, 定格 · 主要負責 遊戲開發團隊(用戶端開發) · 協同 外部(外部)
攻城戰、世界王這類數百人擠進同一個畫面的場合,光是繪製的成本就負荷不了。
為什麼: 同一個畫面裡數百人與特效重疊 → 於是: 動畫、陰影、名字、特效的成本隨人數成比例增加 → 畫面上: FPS 由 60 → 15 大幅下降,所有動作都卡頓,輸入也跟著延遲
症狀: 卡頓, 輸入延遲 · 主要負責 遊戲開發團隊(用戶端開發)
每個畫格只處理固定數量的已接收封包時,一次湧入的封包會不斷延到下一個畫格。
為什麼: 人多的地方每秒湧入數千筆更新 → 於是: 主執行緒碰到每畫格的處理量上限,讀不完 → 畫面上: 其他人的動作越來越晚反映,而且一次湧入
症狀: 快轉, 輸入延遲 · 主要負責 遊戲開發團隊(用戶端開發) · 協同 遊戲開發團隊(伺服器開發)
一收到伺服器封包就立刻繪製時,抖動(封包抵達間隔忽長忽短)會原封不動地出現在畫面上。
為什麼: 收到位置就直接繪製,或緩衝比抖動還短 → 於是: 封包晚到多久就停多久,一次湧入多少就跳多少 → 畫面上: 其他角色走走停停,一頓一頓地移動
症狀: 卡頓 · 主要負責 遊戲開發團隊(用戶端開發) · 協同 遊戲開發團隊(伺服器開發)
封包沒來的期間,照最後的速度繼續顯示移動,等發現猜錯再拉回來。
為什麼: 收不到封包,就照最後的方向與速度繼續移動 → 於是: 實際上對方已經停下或改變方向 → 畫面上: 對方角色走了好一段後一下子移到真正的位置,或穿牆而過。封包抵達間隔忽長忽短時,會反覆衝過頭又被拉回,看起來像在顫動
症狀: 瞬移, 卡頓 · 主要負責 遊戲開發團隊(用戶端開發)
自己的用戶端已經先顯示移動,伺服器卻算出不同的結果時,自己的角色就會被拉回去。
為什麼: 用戶端在伺服器確認前先移動(預測) → 於是: 伺服器對碰撞、移動速度、buff 的計算結果不同,或沒收到指令 → 畫面上: 確認到達時,自己的角色被往回拉
症狀: 拉回 · 主要負責 遊戲開發團隊(用戶端開發) · 協同 遊戲開發團隊(伺服器開發)
停過一次之後集中補算落後的部分,又因為這些計算而再度落後。
為什麼: 以固定間隔執行的遊戲模擬停了一次 → 於是: 把積欠的每一步集中在一個畫格內算完 → 畫面上: 長畫格接連出現而飆高,或碰到上限使整個世界變慢
症狀: 卡頓, 快轉, 慢動作 · 主要負責 遊戲開發團隊(用戶端開發)
用戶端推估的伺服器時間不準時,內插時間點與冷卻判定就會出現偏差。
為什麼: 只在連線時對時一次,ping 改變了也不重新校正 → 於是: 內插時間點與冷卻結束時間跟伺服器不一致 → 畫面上: 對手偶爾頓一下;冷卻明明結束了,技能卻被拒絕
症狀: 卡頓, 吃指令/回檔 · 主要負責 遊戲開發團隊(用戶端開發)
以精度較低的浮點數格式(float)保存遊戲時間時,開著的時間越久,時間解析度(能分辨的最小時間差)就越差,動作與特效會顫動。
為什麼: 把遊戲啟動後經過的時間累加在 float 裡,或直接傳給著色器 → 於是: 開著的時間越久,float 能表示的最小差距就越大 → 畫面上: 只有連續開了好幾天的用戶端,角色、動畫、流動特效會不停顫動,重新開啟就恢復正常
症狀: 卡頓 · 主要負責 遊戲開發團隊(用戶端開發)
GPU 畫好的畫格會先在佇列裡堆上幾張,再配合螢幕的更新週期送出,這段期間輸入就被延遲。
為什麼: 顯示卡驅動程式預先在佇列中堆積 1~3 張畫格 → 於是: 輸入反映到畫面上也要多花這麼多時間 → 畫面上: ping 很低,操作卻沉重遲鈍
症狀: 輸入延遲, 卡頓 · 主要負責 遊戲開發團隊(用戶端開發) · 協同 外部(外部)
開得越久,記憶體用量越大,遊戲越來越慢,最後被強制關閉。
為什麼: 切換地圖時,貼圖、UI、特效沒有釋放 → 於是: GC 越來越頻繁,OS 記憶體不足而發生 swap → 畫面上: 玩了幾個小時後越來越卡頓,最後強制結束(在玩家看來像斷線)
症狀: 卡頓, 斷線 · 主要負責 遊戲開發團隊(用戶端開發)
遊戲因未處理的錯誤而關閉。在玩家看來像斷線,但伺服器是正常的。
為什麼: null 參考、記憶體不足、顯示卡驅動程式錯誤 → 於是: 遊戲處理程序被強制結束 → 畫面上: 回報「閃退了」。同一時間其他人都正常
症狀: 斷線 · 主要負責 遊戲開發團隊(用戶端開發) · 協同 外部(外部)
為了防範外掛而與遊戲一起執行的安全模組會定期檢查。檢查太重,或與安全伺服器往來的心跳封包(定期確認連線存活的訊號)延遲時,就會卡頓或斷線。
為什麼: 安全模組定期檢查遊戲記憶體、執行中的程式與驅動程式 → 於是: 檢查期間遊戲執行緒停住,或心跳封包沒能準時送出 → 畫面上: 以固定間隔頓一下,嚴重時跳出安全錯誤訊息並斷線
症狀: 卡頓, 定格, 斷線 · 主要負責 遊戲開發團隊(用戶端開發) · 協同 遊戲開發團隊(伺服器開發)
遊戲在 Windows、Android、iOS 上與其他程式共用 CPU、記憶體、網路。作業系統太晚分配 CPU 給遊戲、為了省電而降速,或是把背景 App 暫停時,就會 lag。
作業系統(OS)的排程器(決定輪到誰使用 CPU 的功能)會把 CPU 時間分配給多個程式。遊戲、防毒軟體、瀏覽器、更新程式都在等「輪到自己」,OS 以數 ms 到數十 ms 為單位(時間片段)輪流分配核心。OS 雖然會給前景的遊戲(foreground)稍高的優先順序,但工作比核心多時,遊戲也得等待,這段等待就會拖慢畫格。
網路也要經過 OS。網路卡、Wi-Fi 晶片收到的封包,會先放進驅動程式與 OS 的接收緩衝區,等遊戲來取。遊戲太忙、取得太晚,緩衝區就會溢位;一次全部取出,就會變成快轉。在行動裝置上特別重要的是,OS 為了省電會讓無線連線進入省電狀態,也會頻繁暫停 App 本身。
OS 就像讓多位廚師共用唯一一間廚房的主廚。即使遊戲正在做急件料理,只要名叫「防毒掃描」的廚師占住爐口,遊戲就得等。廚房太熱時(發熱),主廚還會把火力調小。
防毒掃描、Windows Update、直播軟體、瀏覽器影片占住 CPU 核心時,遊戲執行緒分配不到 CPU,只能等待。
為什麼: 其他程式長時間占用 CPU 核心 → 於是: 遊戲執行緒等待排程 → 畫面上: 畫格延遲,已接收封包的處理也跟著變慢
症狀: 卡頓, 快轉 · 主要負責 外部(外部) · 協同 遊戲開發團隊(用戶端開發)
筆電的電池模式、手機的省電模式、裝置過熱,都會讓 CPU、GPU 速度下降。過熱的特徵是一開始正常,過了一段時間才變慢。
為什麼: 處於電池或省電模式,或裝置發燙 → 於是: 依裝置不同,CPU、GPU 時脈降低 30~50% → 畫面上: 省電模式一開就發生,過熱則在玩了幾分鐘~20 分鐘左右後,FPS 下降並出現卡頓
症狀: 卡頓, 輸入延遲 · 主要負責 外部(外部) · 協同 遊戲開發團隊(用戶端開發)
Windows 的預設計時器以 15.6ms 為單位,所以「只休息 1ms」實際上會等到下一個計時器週期,最長拉長到 15.6ms。
為什麼: 用 Sleep(短暫等待)的方式實作畫格限制與封包傳送 → 於是: OS 只以 15.6ms 為單位喚醒 → 畫面上: 畫格間隔與輸入傳送間隔忽長忽短
症狀: 卡頓 · 主要負責 遊戲開發團隊(用戶端開發)
為了看通知而把 App 暫時切到背景時,OS 會在幾秒後暫停(suspend)App,這段期間伺服器就會切斷玩家的連線。
為什麼: 為了看訊息、接電話把遊戲切到背景 → 於是: 遊戲引擎暫停遊戲進行,OS 很快也會停止 App 與網路 → 畫面上: 回到遊戲時早已斷線,只能重新連線
症狀: 斷線 · 主要負責 遊戲開發團隊(用戶端開發) · 協同 遊戲開發團隊(伺服器開發)
走出家門時 Wi-Fi 斷掉、改用 LTE/5G,自己的 IP 位址會改變,原本的連線就此失效。
為什麼: Wi-Fi 訊號變弱,切換到行動網路 → 於是: 自己的 IP 位址改變,用舊位址建立的連線無法再收送資料 → 畫面上: 短暫停住後斷線,或重新連線
症狀: 定格, 斷線 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 遊戲開發團隊(用戶端開發), 基礎設施團隊(網路基礎設施)
防毒軟體、防火牆檢查每一個封包時延遲會增加,太過頭還會把遊戲誤判為攻擊而封鎖。
為什麼: 安全軟體逐一檢查收送的封包 → 於是: 每個封包都多出延遲,檢查來不及時封包會被丟棄 → 畫面上: ping 不規則地飆高,或連線被封鎖
症狀: 卡頓, 連不上/無限讀取 · 主要負責 外部(外部) · 協同 遊戲開發團隊(用戶端開發)
遊戲太忙,太晚從 socket(OS 提供的網路收送介面)取出封包時,OS 緩衝區就會溢位。
為什麼: 畫格延誤,遊戲太晚讀取 socket → 於是: OS 接收緩衝區滿了,UDP 直接丟棄,TCP 則縮小接收視窗讓對方停止傳送 → 畫面上: 瞬移(UDP)或快轉(TCP)
症狀: 瞬移, 快轉 · 主要負責 遊戲開發團隊(用戶端開發)
同時開著數十個瀏覽器分頁和遊戲時,OS 會把遊戲的一部分記憶體移到磁碟上。
為什麼: 整體 RAM 不足 → 於是: OS 把遊戲暫時沒用到的記憶體移到磁碟 → 畫面上: 再次用到那部分的瞬間,依儲存裝置不同會停住數十~數百 ms
症狀: 定格, 卡頓 · 主要負責 外部(外部) · 協同 遊戲開發團隊(用戶端開發)
畫質選項需要的記憶體比顯示卡記憶體還大時,OS 會把貼圖移到電腦的主記憶體再搬回來,因此出現卡頓。
為什麼: 高貼圖選項,加上人多的地方各式各樣的裝備與特效,讓顯示卡記憶體全滿 → 於是: OS 把暫時沒用到的貼圖移到電腦的主記憶體,需要時再透過較慢的 PCIe 匯流排搬回來 → 畫面上: 每次出現新場景或新角色都會頓一下,貼圖有一段時間是模糊的
症狀: 卡頓, 定格 · 主要負責 遊戲開發團隊(用戶端開發) · 協同 外部(外部)
OS 為了尋找周圍的 Wi-Fi,會定期切換到其他頻道,這段期間通訊會短暫停止。
為什麼: OS 或驅動程式以固定週期搜尋周圍的 Wi-Fi → 於是: 搜尋期間收送短暫停止 → 畫面上: ping 以非常固定的間隔(例如每 60 秒)飆高
症狀: 卡頓, 瞬移 · 主要負責 外部(外部) · 協同 遊戲開發團隊(用戶端開發)
有線網路卡或 Wi-Fi 晶片在封包與封包之間進入省電狀態時,要重新喚醒需要時間。
為什麼: 網路裝置的省電功能開著,或驅動程式太舊 → 於是: 喚醒(wake-up)延遲,偶爾裝置會重新啟動 → 畫面上: 不規則的延遲,少數情況會停住數秒
症狀: 卡頓, 定格 · 主要負責 外部(外部) · 協同 遊戲開發團隊(用戶端開發)
雲端同步、大型下載、遊戲更新在同一台電腦上執行時,遊戲封包得在佇列中等待。
為什麼: 其他 App 把上傳、下載用到滿 → 於是: 遊戲封包堆積在電腦與分享器的佇列中 → 畫面上: ping 暴增、輸入延遲、快轉
症狀: 輸入延遲, 快轉 · 主要負責 外部(外部) · 協同 遊戲開發團隊(用戶端開發)
切到其他視窗或把遊戲最小化時,遊戲與 Windows 會為了省電讓遊戲跑得比較慢。回到遊戲時,累積的封包會一口氣湧入,或者早已斷線。
為什麼: 用 Alt+Tab 切到其他視窗,或把遊戲最小化 → 於是: 遊戲看不見的期間,FPS 會大幅降低或停止,Windows 也會調低看不見的程式的優先順序 → 畫面上: 切回來的瞬間出現快轉,切出去太久則會斷線
症狀: 快轉, 卡頓, 斷線 · 主要負責 遊戲開發團隊(用戶端開發)
通訊軟體、啟動器、錄影、FPS 顯示程式為了在遊戲畫面上疊加自己的 UI,會介入遊戲的渲染流程(hook)。每個畫格的工作量因此增加,偶爾還會與遊戲衝突,造成頓一下或遊戲被強制關閉。
為什麼: 通訊軟體、遊戲啟動器、顯示卡工具、錄影程式的 overlay 開著 → 於是: 每次把畫格輸出到畫面時,overlay 都會介入疊加自己的 UI → 畫面上: 畫格一點一點變慢,通知跳出的瞬間頓一下,或出現畫面錯誤、強制關閉(玩家看起來像斷線)
症狀: 卡頓, 定格, 斷線 · 主要負責 外部(外部) · 協同 遊戲開發團隊(用戶端開發)
ping 正常、操作卻很沉重時,可能是電視的影像處理、無線控制器或畫格生成功能在輸入與畫面之間增加了延遲。
為什麼: 電視的遊戲模式沒開、使用藍牙或無線控制器,或開啟了畫格生成(DLSS、FSR 畫格生成) → 於是: 電視在進行畫質處理時會延後輸出畫格,無線輸入會因傳輸週期與干擾而晚到,畫格生成則要等下一個畫格才能做出中間的畫格 → 畫面上: ping 與 FPS 數字都很好,按下按鍵後卻要過一會兒才反映在畫面上,形成輸入延遲
症狀: 輸入延遲 · 主要負責 外部(外部) · 協同 遊戲開發團隊(用戶端開發)
封包離開家之前的最後幾公尺。距離雖短,但相當多的 lag 回報都源自這裡。因為 Wi-Fi 是多台裝置共用同一個無線頻道,分享器則把全家人的流量排進同一個佇列送出。
Wi-Fi 與鄰居的分享器共用同一個無線頻道(頻段),2.4GHz 頻段還會與藍牙、微波爐重疊。傳送時發生碰撞就稍等一下再重送,這些重傳累積起來,封包就會忽快忽慢地抵達。ping 的平均值看起來沒問題,卻時不時飆高,是 Wi-Fi 的典型樣子。
分享器是家中所有裝置連上網際網路時必經的設備。送出的量超過網際網路線路能承接的速度時,分享器或數據機裡就會形成佇列,而沒有佇列管理功能(SQM)的設備,會讓這個佇列堆積到數百 ms 的長度。昂貴的分享器若關閉了這個功能也一樣。弟弟妹妹上傳影片的那一刻,遊戲封包也得在那個佇列的最尾端等待。這種現象稱為 bufferbloat。
分享器還會把「內部裝置 ↔ 外部伺服器」的連線記錄在 NAT 表中,一段時間沒有封包往來就會從表中刪除。這是閒置後斷線的常見原因。行動網路則還要再加上基地台換手、無線省電狀態、訊號微弱等因素。
分享器就像社區唯一的出入口。搬家卡車(影片上傳)排成一列時,急件機車快遞(遊戲封包)也得在卡車後面等。聰明的分享器(SQM)會另外開一條快遞專用道。
訊號弱或有干擾時,無線區段要反覆重送好幾次,封包抵達的時間就會忽快忽慢。
為什麼: 牆壁、距離、微波爐、藍牙、鄰居的分享器讓無線訊號品質變差 → 於是: 無線區段傳送失敗 → 重傳好幾次 → 畫面上: 封包抵達忽快忽慢(抖動),角色走走停停,嚴重時封包遺失而出現瞬移
症狀: 卡頓, 瞬移, 拉回 · 主要負責 外部(外部) · 協同 遊戲開發團隊(用戶端開發)
在公寓大樓這類有數十台分享器的地方,大家共用同一個頻道,只能等待傳送機會。
為什麼: 數十台分享器使用同一個 2.4GHz 頻道 → 於是: 要傳送就得等其他裝置傳完、頻道空出來 → 畫面上: 大家回到家的晚間時段抖動(封包抵達間隔忽長忽短)增加,出現卡頓
症狀: 卡頓, 輸入延遲 · 主要負責 外部(外部) · 協同 遊戲開發團隊(用戶端開發)
家人上傳影片或下載大型檔案時,分享器佇列會堆積相當於數百 ms 的封包,遊戲封包也得排在後面等待。
為什麼: 家人上傳影片、雲端備份、自己的直播推流、大量下載把線路塞滿 → 於是: 分享器或數據機把滿出來的封包堆進很大的佇列 → 畫面上: 遊戲封包也排在佇列後面等待,ping 暴增到數百 ms
症狀: 輸入延遲, 快轉, 瞬移 · 主要負責 外部(外部) · 協同 遊戲開發團隊(用戶端開發)
分享器會把一段時間沒有封包往來的閒置連線從 NAT 表中刪除。這是閒置一段時間後一有動作就斷線的常見原因。
為什麼: 分享器把「內部裝置 ↔ 外部伺服器」的連線記錄在 NAT 表(位址轉換表)中 → 於是: 一段時間沒有封包就從表中刪除(UDP 通常為 30~120 秒) → 畫面上: 伺服器的封包進不了家中網路,造成斷線
症狀: 斷線 · 主要負責 遊戲開發團隊(用戶端開發) · 協同 遊戲開發團隊(伺服器開發)
便宜的分享器同時接了數十台裝置、數千條連線時,分享器本身就處理不過來。
為什麼: 數十台裝置,加上 P2P、BT(torrent)開啟數千條連線 → 於是: 分享器的 CPU 與 session 表飽和 → 畫面上: 封包處理延遲、封包遺失,新連線失敗
症狀: 卡頓, 連不上/無限讀取, 斷線 · 主要負責 外部(外部)
搭公車、捷運移動時,切換基地台的期間通訊會中斷。
為什麼: 移動中連線的基地台改變 → 於是: 通常只空白數十 ms,但訊號差導致切換失敗時,有時會中斷數百 ms~數秒 → 畫面上: 停住後瞬移,時間長的話會斷線
症狀: 定格, 瞬移, 斷線 · 主要負責 外部(外部) · 協同 遊戲開發團隊(用戶端開發), 遊戲開發團隊(伺服器開發)
手機一段時間沒有通訊時,會把無線連線降到低耗電狀態,下一個封包要送出時得重新拉高,因此變慢。
為什麼: 短暫沒有通訊時,手機把無線連線切換到省電狀態 → 於是: 要送出下一個封包,必須重新拉高連線 → 畫面上: 閒置一段時間後的第一個動作特別慢
症狀: 輸入延遲 · 主要負責 遊戲開發團隊(用戶端開發)
在電梯、地下室、建築物深處,重傳會增加、速度下降,最後斷線。
為什麼: 移動到訊號弱的地方 → 於是: 無線重傳增加、速度下降、瞬間斷訊 → 畫面上: 抖動與封包遺失造成卡頓、瞬移,最後斷線
症狀: 卡頓, 瞬移, 斷線 · 主要負責 外部(外部) · 協同 遊戲開發團隊(用戶端開發)
在 5G 訊號弱的建築物內或 5G 涵蓋邊緣,手機會頻繁在 5G 與 LTE 之間切換,每次切換 ping 都會飆高或通訊短暫中斷。
為什麼: 身處 5G 訊號時好時壞的地方(建築物內、5G 涵蓋邊緣) → 於是: 手機隨時在 5G 與 LTE 之間切換,每次都會出現短暫空白 → 畫面上: 即使靜止不動,ping 也會不規則地飆高,偶爾定格、瞬移
症狀: 卡頓, 瞬移, 定格 · 主要負責 外部(外部) · 協同 遊戲開發團隊(用戶端開發)
咖啡廳 Wi-Fi 的登入頁面或公司防火牆會擋掉遊戲連線。
為什麼: 尚未通過登入頁面認證,或防火牆封鎖遊戲 port 與 UDP → 於是: 連線嘗試本身被擋,或只有部分通過 → 畫面上: 無法連線,或能登入卻進不了遊戲
症狀: 連不上/無限讀取 · 主要負責 外部(外部) · 協同 遊戲開發團隊(用戶端開發), 遊戲開發團隊(伺服器開發)
離開家的封包,會經過電信業者網路、多家電信業者之間的互連區段,有時還要經過海底電纜,才抵達伺服器所在的資料中心。這段區段的延遲大多由距離與路徑選擇(路由)決定,很多時候遊戲公司無法直接修正。
光在光纖中 1 秒約可前進 20 萬 km。距離 1,000km 的伺服器,往返至少需要 10ms,只要還是走光纖,這個數字再怎麼升級伺服器或設備都無法縮短。實際的封包不走直線,會沿著電信業者之間的互連點(peering)繞行,所以通常要花理論值的 1.5~2 倍。像韓國–歐洲這種直線上幾乎沒有大型電纜的區段,會繞經東南亞與蘇伊士或美國,達到 2.5~3 倍(往返約 230~270ms)。
問題在於這條路徑會隨時間與狀況改變。晚上 9~11 點左右大家都在看影片,電信業者之間的互連區段容易壅塞;路由資訊(BGP)改變時,封包會有幾秒到幾十秒(少數情況下幾分鐘)無法抵達目的地;海底電纜斷了,則會有好幾週繞遠路。如果 lag 是「只有特定電信業者的玩家」、「只在晚上」、「只在海外」,就先懷疑這一層。
電信業者網路就像高速公路網。從首爾到釜山的路即使不塞,距離本身還是需要時間;下班時間的收費站(peering 區段)會塞車;發生事故時,導航會指引你繞一大段遠路。
光在光纖中 1 秒也只能前進約 20 萬 km。伺服器在遠方,效能再好也會慢。
為什麼: 伺服器位在遠方(海外伺服器、其他大陸) → 於是: 距離越遠,往返時間越長(每 1,000km 至少 10ms) → 畫面上: 所有動作都有固定的輸入延遲,判定上也吃虧
症狀: 輸入延遲 · 主要負責 基礎設施團隊(伺服器基礎設施) · 協同 基礎設施團隊(網路基礎設施), 遊戲開發團隊(伺服器開發)
衛星網路的電波必須往返太空。同步軌道衛星光是往返就超過 0.5 秒;Starlink 這類低軌衛星平時很快,但在重新分配路徑的瞬間延遲會忽高忽低,有時還會短暫中斷。
為什麼: 在家中、船上或飛機上,透過同步軌道衛星、低軌衛星網路,或使用衛星的機上 Wi-Fi 連線 → 於是: 同步軌道衛星高度約 36,000km,往返距離本身就很長;低軌衛星則以很短的週期重新分配終端設備、衛星、地面站之間的路徑,在那一瞬間會短暫出現延遲與封包遺失 → 畫面上: 同步軌道衛星讓所有動作都有很大的輸入延遲;低軌衛星平時正常,但會每隔固定時間出現卡頓、瞬移
症狀: 輸入延遲, 卡頓, 瞬移 · 主要負責 外部(外部) · 協同 遊戲開發團隊(用戶端開發), 遊戲開發團隊(伺服器開發)
受電信業者之間互連合約的影響,連到附近的伺服器也可能繞到遠處再回來。
為什麼: 自己的電信業者與伺服器端的電信業者沒有直接互連 → 於是: 經過其他國家或其他城市,距離與經過的設備都增加 → 畫面上: 只有特定電信業者的使用者 ping 特別高
症狀: 輸入延遲 · 主要負責 基礎設施團隊(網路基礎設施) · 協同 外部(外部)
晚上 9~11 點左右影音流量暴增,電信業者之間的互連區段(peering)容易壅塞。
為什麼: 晚上串流與下載的流量集中湧入 → 於是: peering 區段出現排隊與封包遺失 → 畫面上: 只在晚上,特定電信業者的使用者出現卡頓、瞬移
症狀: 卡頓, 瞬移, 拉回 · 主要負責 基礎設施團隊(網路基礎設施) · 協同 外部(外部)
海底電纜一旦斷裂,修復前的幾週(長則幾個月)流量都得繞遠路,剩下的線路也會壅塞。
為什麼: 電纜被切斷或設備故障 → 於是: 流量集中到繞遠路的路徑與剩下的線路上 → 畫面上: 海外玩家的 ping 暴增並出現封包遺失,持續幾天到幾週
症狀: 輸入延遲, 瞬移 · 主要負責 外部(外部) · 協同 基礎設施團隊(網路基礎設施)
網際網路的路由資訊改變後重新收斂的幾秒到幾十秒(偶爾幾分鐘)之間,封包會遺失。
為什麼: 某個電信業者區段的路由資訊改變 → 於是: 幾秒到幾十秒之間封包消失,或切換到新路徑 → 畫面上: 突然停住幾秒,之後 ping 值改變(例:40 → 70ms)
症狀: 定格, 瞬移 · 主要負責 基礎設施團隊(網路基礎設施) · 協同 遊戲開發團隊(伺服器開發), 外部(外部)
電信業者與資料中心會為同一個目的地準備多條路徑,並替每條連線指定其中一條傳送。只要其中一條路徑故障,被分配到那條路徑的人就會持續遇到 lag。
為什麼: 在多條線路綁在一起的區段中,其中一條線路或一台設備異常或壅塞 → 於是: 路徑由位址與 port 的組合(雜湊)決定,只有被分配到那條路徑的連線出現封包遺失與延遲 → 畫面上: 同一地區、同一電信業者,卻只有部分人持續瞬移。重新連線後有時就恢復正常
症狀: 瞬移, 拉回, 卡頓 · 主要負責 基礎設施團隊(網路基礎設施) · 協同 遊戲開發團隊(伺服器開發), 外部(外部)
在用量超過上限或會管理特定流量的資費方案中,封包會被延後或丟棄。
為什麼: 資費方案的行動數據用完後被限速,或特定流量受到限制 → 於是: 封包排隊等待或被丟棄 → 畫面上: 用到一定用量之後開始 lag,行動網路特別常見
症狀: 輸入延遲, 瞬移 · 主要負責 外部(外部) · 協同 遊戲開發團隊(伺服器開發), 基礎設施團隊(網路基礎設施)
部分網路會封鎖特定 UDP 位址與 port,或限制 UDP 速度;封包檢測設備也會過濾掉無法辨識的協定。用 UDP 通訊的遊戲在這類網路上會連不上或經常斷線。
為什麼: 從限制 UDP 速度的部分電信業者網路,或設有國家/電信業者層級流量檢測(審查)設備的網路連線 → 於是: 封鎖特定 UDP 位址與 port、在壅塞時段限制 UDP 速度、過濾不在允許清單上的 port 與協定,或只放行前幾個封包後就封鎖 → 畫面上: 只有特定國家或電信業者的玩家連不上/無限讀取,或連上後很快斷線,壅塞時段因封包遺失而瞬移
症狀: 連不上/無限讀取, 斷線, 瞬移 · 主要負責 外部(外部) · 協同 遊戲開發團隊(用戶端開發), 遊戲開發團隊(伺服器開發), 基礎設施團隊(網路基礎設施)
接頭接觸不良、老舊線路或數據機異常,會造成持續的封包遺失與週期性的線路中斷。
為什麼: 纜線受損、接觸不良,或數據機、光纖終端設備異常 → 於是: 位元錯誤導致封包被丟棄,有時線路會重新連線,因而中斷數秒到 1 分鐘左右 → 畫面上: 持續少量的封包遺失,偶爾定格數秒或斷線
症狀: 瞬移, 定格, 斷線 · 主要負責 外部(外部)
負責把伺服器名稱轉成位址的 DNS 一旦變慢或失敗,就找不到登入伺服器與更新伺服器。
為什麼: 電信業者的 DNS 故障或設定錯誤 → 於是: 找不到登入伺服器、更新伺服器的位址 → 畫面上: 按下連線按鈕後要等很久,或連不上。已經連上的人不受影響
症狀: 連不上/無限讀取 · 主要負責 外部(外部) · 協同 遊戲開發團隊(用戶端開發)
針對遊戲公司,或同一網路上其他對象的大量攻擊,會塞滿共用的線路。
為什麼: 出現大量攻擊流量 → 於是: 連走同一條線路的正常流量也被擠壓、丟棄 → 畫面上: 許多人同時瞬移、斷線、連不上
症狀: 瞬移, 斷線, 連不上/無限讀取 · 主要負責 基礎設施團隊(網路基礎設施) · 協同 外部(外部)
行動網路與部分電信業者讓多位用戶共用一個 IP,並在短時間內清除閒置連線的 NAT mapping。
為什麼: 電信業者的設備要管理大量用戶的 session 表 → 於是: session 表有上限,閒置逾時很短 → 畫面上: 閒置一陣子後斷線;共用同一個 IP 的人被誤判而一起封鎖
症狀: 斷線, 連不上/無限讀取 · 主要負責 遊戲開發團隊(用戶端開發) · 協同 遊戲開發團隊(伺服器開發), 基礎設施團隊(網路基礎設施)
開啟 VPN 或遊戲加速器後,封包會經過該公司的中繼伺服器。中繼伺服器很遠或壅塞時,反而會變慢。
為什麼: VPN 或加速器把遊戲封包全部轉送到中繼伺服器 → 於是: 加上到中繼伺服器的距離與壅塞,通道標頭也讓 MTU(一次能傳送的封包大小)變小 → 畫面上: ping 升高並出現封包遺失;與使用同一中繼位址的人一起被封鎖而連不上
症狀: 輸入延遲, 瞬移, 連不上/無限讀取 · 主要負責 外部(外部) · 協同 基礎設施團隊(網路基礎設施), 遊戲開發團隊(伺服器開發)
抵達伺服器之前,封包會依序通過路由器、DDoS 防護設備、防火牆、負載平衡器、交換器。這段區段平時連 1ms 都不到,但只要一台設備容量滿載或故障,整個伺服器的數千名玩家就會同時受影響。
每台設備的角色不同。路由器決定路徑,DDoS 防護設備過濾攻擊流量,防火牆只讓允許的連線通過,並用 session 表追蹤所有連線。負載平衡器把進來的連線分配給多台伺服器,交換器則把伺服器彼此連接起來。
這些設備的共同弱點是表格大小與緩衝區大小。防火牆的 session 表滿了就無法接受新連線;負載平衡器會在一段時間後刪除閒置連線;當多台伺服器在同一瞬間對數千人一起送出封包時(世界王出現),交換器的小緩衝區不到 1ms 就會溢位。還有,一台設備故障、切換到備援設備(容錯移轉)的那幾秒內,所有人都會停住。
資料中心的入口就像機場的安檢與登機門。安檢(防火牆)只讓名單上的人通過,名單欄位填滿就再也收不了人。登機門人員(負載平衡器)會把安靜坐了很久的乘客當成「已經離開的人」,從名單上劃掉。
防火牆會把放行的每條連線記錄在 session 表中追蹤。表一旦滿了,就無法接受新連線。
為什麼: 連線暴增或遭受攻擊,使 session 數達到上限 → 於是: 沒有空的項目可以記錄新連線,因此拒絕連線 → 畫面上: 想新連進來的人連不上/無限讀取,部分既有連線也會斷線
症狀: 連不上/無限讀取, 斷線 · 主要負責 基礎設施團隊(網路基礎設施) · 協同 遊戲開發團隊(伺服器開發), 遊戲開發團隊(用戶端開發)
為了擋下攻擊而把流量導向清洗中心時,路徑會變長,有時還會把正常使用者誤判為攻擊而封鎖。
為什麼: 偵測到攻擊後(或常態性地)把進來的流量導向清洗中心 → 於是: 路徑變長,部分正常封包被判定為攻擊 → 畫面上: 整體 ping 上升,只有特定地區/電信業者連不上
症狀: 輸入延遲, 連不上/無限讀取, 瞬移 · 主要負責 基礎設施團隊(網路基礎設施) · 協同 遊戲開發團隊(伺服器開發)
負載平衡器會在一段時間後清除閒置連線。遊戲還以為連線仍然維持著,結果就斷線了。
為什麼: 玩家有一段時間完全沒送出任何封包(開著對話視窗、暫離) → 於是: 負載平衡器清理閒置連線(常見的預設值為 60~350 秒) → 畫面上: 再次移動的瞬間斷線
症狀: 斷線 · 主要負責 基礎設施團隊(網路基礎設施) · 協同 遊戲開發團隊(用戶端開發), 遊戲開發團隊(伺服器開發)
雲端伺服器上的防火牆(安全群組)也會追蹤連線,閒置連線的追蹤項目會在一定時間後過期。即使是沒有經過負載平衡器、直接連線的伺服器,靜止不動的玩家也可能斷線。
為什麼: 安全群組處於會追蹤遊戲連線的設定(只允許特定位址、限制輸出規則、經由 NLB 等) → 於是: 連線閒置一段時間後追蹤項目過期,之後進來的封包被安全群組默默丟棄 → 畫面上: 暫離後再移動時沒有反應,接著斷線。伺服器程式很長一段時間都沒察覺
症狀: 斷線 · 主要負責 基礎設施團隊(伺服器基礎設施) · 協同 遊戲開發團隊(用戶端開發), 遊戲開發團隊(伺服器開發)
私有子網路中的伺服器對外(平台驗證、付款、外部 API)的連線,由 NAT 閘道轉換位址與 port 後送出。送往同一目的地的同時連線超過閘道的 port 上限時,新連線就會失敗。
為什麼: 伺服器對平台驗證、付款這類同一個外部位址大量開啟短連線,或長時間開著連線 → 於是: NAT 閘道無法再分配該目的地可用的來源 port,新連線失敗 → 畫面上: 遊戲內一切正常,只有登入、付款、發放獎勵這類呼叫外部的功能失敗或變慢(連不上/無限讀取、吃指令/回檔)
症狀: 連不上/無限讀取, 吃指令/回檔 · 主要負責 基礎設施團隊(網路基礎設施) · 協同 遊戲開發團隊(伺服器開發)
連線全集中到一台伺服器,或持續把人送往已經掛掉的伺服器。
為什麼: 分配規則不合適,或健康檢查(health check)看不到實際狀態 → 於是: 只有一台伺服器過載,或連線被送往掛掉的伺服器 → 畫面上: 只有部分頻道、部分人出現慢動作,或連不上/無限讀取
症狀: 慢動作, 連不上/無限讀取 · 主要負責 基礎設施團隊(網路基礎設施) · 協同 遊戲開發團隊(伺服器開發)
多台伺服器在同一瞬間同時對數千人送出封包時,這些流量匯集的交換器 port 上的小緩衝區,不到 1ms 就會滿溢。
為什麼: 世界王出現、大範圍技能,或多台伺服器的 tick 在同一瞬間對齊,同時送出封包 → 於是: 多個 port 匯入一個 port,或從快速 port 轉到慢速 port 的地方,緩衝區(每個 port 數百 KB~數 MB)瞬間塞滿 → 畫面上: 部分封包被丟棄,許多人同時瞬移、技能被吃
症狀: 瞬移, 吃指令/回檔 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 基礎設施團隊(網路基礎設施), 基礎設施團隊(伺服器基礎設施)
更新檔發布、log 傳送、備份與遊戲共用同一條線路時,線路會被塞滿。
為什麼: 大量傳輸佔用同一條線路 → 於是: 線路上的排隊與封包遺失增加 → 畫面上: 整個伺服器的 ping 上升並出現瞬移
症狀: 輸入延遲, 瞬移 · 主要負責 基礎設施團隊(網路基礎設施) · 協同 基礎設施團隊(伺服器基礎設施)
路由器或防火牆有一台故障、切換到備援設備(failover)的幾秒之間,所有人的畫面都會停住。
為什麼: 設備故障或維護,切換到備援設備 → 於是: 切換需要數秒;session 資訊沒有同步時,連線會被重置 → 畫面上: 伺服器上的所有玩家同時定格,大量斷線
症狀: 定格, 斷線 · 主要負責 基礎設施團隊(網路基礎設施) · 協同 遊戲開發團隊(伺服器開發), 遊戲開發團隊(用戶端開發)
光模組或纜線不良時,經過該路徑的封包會有一定比例損毀。
為什麼: 光模組或纜線不良造成位元錯誤 → 於是: 損毀的封包被設備默默丟棄 → 畫面上: 只有使用該路徑的部分伺服器與使用者因持續的封包遺失而瞬移、拉回
症狀: 瞬移, 拉回 · 主要負責 基礎設施團隊(網路基礎設施)
中途區段的 MTU(一次能傳送的大小)變小,而大小超過的通知又被擋掉時,只有大封包會一直消失。
為什麼: 在通道或 VPN 區段中 MTU 變小 → 於是: 大小超過的通知(ICMP)被防火牆擋下,傳送端不知道 → 畫面上: 只有打開背包、角色清單這類大畫面時,畫面會停住,接著斷線
症狀: 定格, 斷線, 連不上/無限讀取 · 主要負責 基礎設施團隊(網路基礎設施) · 協同 基礎設施團隊(伺服器基礎設施), 遊戲開發團隊(伺服器開發)
插在伺服器上的網路卡每秒要接收數十萬到數百萬個封包並交給 CPU。這裡的處理一旦落後,伺服器程式連封包來過都不知道,封包就遺失了。
NIC 把抵達的封包依序放進 ring buffer(輪流重複使用固定數量 slot 的接收緩衝區),並通知 CPU「封包來了」(中斷,interrupt)。CPU 從 ring buffer 取出封包交給 OS。封包進來的速度比 CPU 取出的速度快時,slot 會全部填滿,之後來的封包就被丟棄。只有網路卡統計(ethtool -S)的數字默默上升,遊戲伺服器 log 不會留下任何錯誤,因此是很難找的 lag。
現在的 NIC 有多個接收佇列(ring buffer),並具備把通知分散到多個 CPU 核心的功能(RSS),但若沒有設定,或流量集中到同一個佇列,就只有一個核心跑到 100%,成為瓶頸。雲端伺服器在 NIC 前面另有每秒封包數、頻寬、連線數上限,超出的部分在抵達伺服器之前就被丟棄。這在 CPU、ring buffer 這類平常的指標上看不出來,AWS 的話只會留在 ENA 驅動程式的統計(ethtool -S 的 pps_allowance_exceeded 等)裡。
NIC 是公寓的信箱,ring buffer 是信箱的格數,中斷是郵差按的門鈴。郵件大量湧入、取信的人卻只有一個時,信箱滿出來,信就掉到地上。RSS 就是安排多個人取信。
NIC 只把封包到達的中斷(interrupt)送給一個 CPU 核心時,那個核心就會成為瓶頸。
為什麼: 只有一個接收佇列,或是負責分散到多個核心的 RSS 被關閉 → 於是: 單一核心達到 100%,無法及時取出封包 → 畫面上: 人潮湧入時,整個伺服器出現封包遺失與延遲(瞬移、輸入延遲)
症狀: 瞬移, 拉回, 輸入延遲 · 主要負責 基礎設施團隊(伺服器基礎設施)
NIC 暫存封包的 ring buffer 太小時,封包瞬間湧入就會讓緩衝區滿溢而被丟棄。
為什麼: ring buffer 仍是預設值(依驅動程式為 256~2,048 個 slot),容量偏小 → 於是: 突發流量來時,CPU 還沒取走封包,緩衝區就滿了 → 畫面上: 只在突發流量的瞬間出現封包遺失(瞬移、技能被吃)。遊戲伺服器 log 中看不到任何痕跡
症狀: 瞬移, 吃指令/回檔 · 主要負責 基礎設施團隊(伺服器基礎設施)
為了減輕 CPU 負擔,NIC 把封包累積起來再一次通知 CPU(中斷合併,interrupt coalescing)時,累積多久就會晚多久。
為什麼: NIC 累積一定時間或一定數量的封包後才通知 → 於是: 累積期間,封包只能等待 → 畫面上: 延遲些微增加。通常很小,設定過度時會達到 ms 等級
症狀: 輸入延遲 · 主要負責 基礎設施團隊(伺服器基礎設施)
雲端伺服器依類型各有每秒封包數與頻寬的上限,超過時會默默丟棄。
為什麼: 同時上線人數增加,每秒封包數超過執行個體上限 → 於是: 雲端網路丟棄超出的部分 → 畫面上: 找不出原因的封包遺失造成瞬移、技能被吃。伺服器 CPU 還有餘裕
症狀: 瞬移, 吃指令/回檔 · 主要負責 基礎設施團隊(伺服器基礎設施) · 協同 遊戲開發團隊(伺服器開發), 外部(外部)
把 1Gbps、10Gbps 網路卡用到極限時,傳送佇列會變長,最後封包被丟棄。
為什麼: 廣播增加,使傳輸量達到網路卡的極限 → 於是: 傳送佇列變長,滿了就丟棄 → 畫面上: 整個伺服器出現延遲與封包遺失(輸入延遲、瞬移)
症狀: 輸入延遲, 瞬移 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 基礎設施團隊(伺服器基礎設施)
同一台實體伺服器上的其他虛擬機器大量使用網路或 CPU 時,自己伺服器的處理會不規則地被延後。
為什麼: 同一台實體伺服器上的其他虛擬機器大量使用資源 → 於是: 自己虛擬機器的封包處理不規則地延遲 → 畫面上: 沒有明顯原因,偶爾出現抖動(封包抵達間隔忽長忽短)而卡頓
症狀: 卡頓 · 主要負責 基礎設施團隊(伺服器基礎設施) · 協同 外部(外部)
雲端供應商維護實體伺服器(主機)時,會把虛擬機器搬到其他主機(即時遷移),或暫時停住。這段期間整台伺服器都會停住,停住的時間一長,連線就會中斷。
為什麼: 供應商因主機維護或故障預測,把虛擬機器搬到其他主機,或短暫暫停 → 於是: 搬移期間 CPU、記憶體、網路變慢,最後虛擬機器會短暫完全停住(依供應商與方式,從不到 1 秒到 30 秒左右) → 畫面上: 伺服器上所有人的畫面同時停住,接著快轉、瞬移;停住的時間比逾時長時,會大量斷線
症狀: 定格, 快轉, 瞬移, 斷線 · 主要負責 基礎設施團隊(伺服器基礎設施) · 協同 遊戲開發團隊(伺服器開發), 外部(外部)
驅動程式 bug 或功能異常讓網路卡停住並重新啟動,這段期間所有收發都會中斷。
為什麼: 驅動程式 bug、offload 功能異常 → 於是: NIC 停住後重新啟動(數秒) → 畫面上: 那台伺服器上所有人的畫面一起停住,接著瞬移或斷線
症狀: 定格, 斷線 · 主要負責 基礎設施團隊(伺服器基礎設施)
這是把多個封包合併成一個來減輕 CPU 負擔的功能。依設定不同,小型遊戲封包有時會短暫等待下一個可以一起合併的封包。
為什麼: NIC 或 kernel 把到達的封包合併後處理 → 於是: 開啟硬體合併(LRO)或合併等待時間設定時,會短暫等待下一個封包 → 畫面上: 延遲些微增加(大多在數十 µs 以下)
症狀: 輸入延遲 · 主要負責 基礎設施團隊(伺服器基礎設施)
伺服器的 Linux、Windows kernel 負責接受連線、管理 socket 緩衝區,並把 CPU 與記憶體分配給遊戲伺服器程式。大部分預設值都是為了兼顧多種用途而設定的保守值,對於數萬人長時間連線的遊戲伺服器,往往不能直接沿用。
新連線進來時,kernel 會把連線請求放進連線等待佇列(backlog),由遊戲伺服器逐一取走。佇列滿了時,Linux 會默默丟棄新請求,Windows 則回傳拒絕回應。在 Linux 上,每條連線都需要一個檔案描述子(fd),也就是附在每個開啟的檔案或連線上的編號,而一個處理程序能持有的 fd 數也有上限。維護剛結束、數萬人同時按下連線按鈕時,連線等待佇列與 fd 會最先耗盡。
記憶體不足時,kernel 還會把資料移到磁碟(swap,有開啟的話);真的耗盡時,Linux 會挑出使用最多記憶體的處理程序強制終止(OOM killer)。遊戲伺服器通常是那台伺服器上使用最多記憶體的處理程序,所以會最先被終止。如果對容器設了記憶體上限,即使整台伺服器還有餘裕,一碰到上限也會發生同樣的事。時間同步(NTP)、排程工作、容器 CPU 上限、虛擬機器的 CPU steal(其他虛擬機器使用實體 CPU 時的等待時間)這類看似與遊戲無關的事,也會讓伺服器短暫停住或讓計時器出現偏差。
伺服器 OS 就像遊樂園的入口閘門與管理處。開園時間(維護結束)大家一擁而上,閘門前的隊伍(backlog)就會滿出來;可發的手環(檔案描述子)發完了,就再也進不去。
維護剛結束時數萬人同時連線,kernel 的連線等待佇列(backlog)會溢位,連線請求因此被丟棄。
為什麼: 維護一結束,連線湧入的速度就超過遊戲伺服器用 accept 處理連線的速度 → 於是: kernel 的連線等待佇列(backlog,取伺服器程式碼傳給 listen 的值與 kernel 上限中較小者)已滿 → 畫面上: 連線請求被丟棄後不斷重試,結果連不上/無限讀取
症狀: 連不上/無限讀取 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 基礎設施團隊(伺服器基礎設施), 遊戲開發團隊(用戶端開發)
每個連線都需要一個檔案描述子(fd,OS 為開啟的檔案或 socket 指定的編號),而一個處理程序能開啟的 fd 數量有上限。
為什麼: 同時上線人數達到處理程序的檔案描述子上限 → 於是: 伺服器無法接受新連線(Too many open files),開啟 log 檔與 DB 連線也跟著失敗 → 畫面上: 從某個固定人數起誰都進不來,出現連不上/無限讀取
症狀: 連不上/無限讀取 · 主要負責 基礎設施團隊(伺服器基礎設施) · 協同 遊戲開發團隊(伺服器開發)
傳送與接收緩衝區太小時,一遇到突發流量,UDP 收到的封包就會被丟棄,TCP 則因緩衝區沒有空間而無法傳送。
為什麼: SO_SNDBUF、SO_RCVBUF 維持預設值或設得太小 → 於是: 突發流量或接收執行緒短暫停住時,UDP 接收緩衝區溢位而丟棄封包;TCP 則因傳送緩衝區沒有空間而等待 → 畫面上: 瞬移(UDP 遺失)或快轉(TCP 等待)
症狀: 瞬移, 快轉 · 主要負責 基礎設施團隊(伺服器基礎設施) · 協同 遊戲開發團隊(伺服器開發)
執行緒數量遠多於 CPU 核心時,OS 會把 CPU 花在輪流切換執行緒上。
為什麼: 例如每個連線各開一個執行緒,讓執行緒多達數百~數千個 → 於是: context switch(切換執行中的執行緒)的成本與快取未命中增加 → 畫面上: CPU 很忙,處理量卻很低,tick 忽長忽短,出現卡頓、慢動作
症狀: 卡頓, 慢動作 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 基礎設施團隊(伺服器基礎設施)
實體伺服器(hypervisor)把虛擬機器的 CPU 時間暫時讓給其他虛擬機器(CPU steal)時,遊戲伺服器會停住。
為什麼: 同一台主機上的其他虛擬機器大量使用 CPU → 於是: 自己的虛擬機器每次失去數 ms~數十 ms 的執行機會 → 畫面上: tick 時間莫名暴增,出現卡頓、定格
症狀: 卡頓, 定格 · 主要負責 基礎設施團隊(伺服器基礎設施) · 協同 外部(外部)
容器設了 CPU 上限時,只要在固定週期(通常是 100ms)內用完配額,剩下的時間就會被強制暫停(節流)。
為什麼: 在 Kubernetes 等平台上為遊戲伺服器容器設定 CPU 上限(limit) → 於是: tick 計算集中的瞬間用完配額,暫停數十 ms 直到下一個週期 → 畫面上: 平均 CPU 很低,tick 卻週期性飆高,出現卡頓、慢動作
症狀: 卡頓, 慢動作 · 主要負責 基礎設施團隊(伺服器基礎設施) · 協同 遊戲開發團隊(伺服器開發)
閒置的 CPU 核心為了省電,會進入深層省電狀態(C-state)並降低頻率。封包或計時器到來時,喚醒核心並拉高頻率需要時間,處理小封包時就會多出延遲。
為什麼: OS 的頻率調整策略(governor)或 BIOS 電源設定允許深層 C-state 與低頻率 → 於是: 閒置核心每次從深層省電狀態喚醒都會慢上最多數百 µs;頻率被鎖在低檔時,tick 計算本身也會變慢 → 畫面上: 通常很難察覺,但伺服器之間的呼叫一多就會累積起來,變成人少時回應反而變慢的輸入延遲。頻率被鎖在低檔時,人潮湧入就會讓 tick 延後,出現慢動作
症狀: 輸入延遲, 慢動作 · 主要負責 基礎設施團隊(伺服器基礎設施)
Linux 在記憶體耗盡時,會挑出使用最多記憶體的處理程序強制終止,通常就是遊戲伺服器。
為什麼: 洩漏或用量暴增導致記憶體耗盡,或容器達到記憶體上限 → 於是: kernel 強制終止遊戲伺服器處理程序 → 畫面上: 該伺服器上的所有人同時斷線,最近的進度可能回檔
症狀: 斷線, 吃指令/回檔 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 基礎設施團隊(伺服器基礎設施)
OS 為了組出大分頁(huge page)而壓縮記憶體,或回收可用記憶體時,處理程序會停住。
為什麼: 可用記憶體減少,或大分頁功能(THP)執行記憶體壓縮 → 於是: 要求記憶體的執行緒一直等到回收或壓縮結束 → 畫面上: 不規則的伺服器暫停(數 ms~數百 ms)
症狀: 定格, 卡頓 · 主要負責 基礎設施團隊(伺服器基礎設施) · 協同 遊戲開發團隊(伺服器開發)
伺服器時鐘一次被往前或往後調整幾秒時,依賴系統時鐘的計時器會同時觸發或停住。
為什麼: 時間同步一次大幅調整時鐘 → 於是: 計時器集中觸發或停住,逾時判定出錯 → 畫面上: buff 與冷卻時間異常、大家同時斷線、快轉
症狀: 快轉, 斷線, 吃指令/回檔 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 基礎設施團隊(伺服器基礎設施)
每天固定時間執行的 log 壓縮、備份、安全掃描會占用 CPU 與磁碟。
為什麼: OS 工作在排定的時間執行 → 於是: 與遊戲伺服器共用 CPU 與磁碟 → 畫面上: 像每天凌晨 4 點這樣的固定時間出現卡頓、慢動作
症狀: 卡頓, 慢動作 · 主要負責 基礎設施團隊(伺服器基礎設施)
遊戲程式碼沒變,但伺服器 OS、kernel、驅動程式、韌體更新後就變慢。更新有時會改變預設值、排程器、CPU 漏洞緩解措施(mitigations)與驅動程式的行為。
為什麼: 定期安全性更新或新的伺服器映像檔讓 kernel、驅動程式、韌體有所改變 → 於是: 預設值或排程器改變,或啟用了新的漏洞緩解措施,同樣的工作要花更多 CPU 時間,執行緒分配到 CPU 的順序也跟著改變 → 畫面上: 原本正常的伺服器從更新那天起一直都慢一點,出現輸入延遲;人潮湧入時出現卡頓、慢動作
症狀: 輸入延遲, 卡頓, 慢動作 · 主要負責 基礎設施團隊(伺服器基礎設施)
Linux 防火牆會把每條連線記錄在連線追蹤(conntrack)表中,這個表碰到上限時就會丟棄新封包。
為什麼: 連線暴增或反覆建立短連線,讓連線紀錄增加 → 於是: 表已滿,新連線與部分封包被丟棄 → 畫面上: 連不上,或因原因不明的遺失出現瞬移
症狀: 連不上/無限讀取, 瞬移 · 主要負責 基礎設施團隊(伺服器基礎設施) · 協同 遊戲開發團隊(伺服器開發), 遊戲開發團隊(用戶端開發)
遊戲伺服器頻繁地對 DB 或其他伺服器建立又關閉短連線時,已關閉的連線會占用 port 一段時間,導致無法開啟新連線。
為什麼: 每個請求都開啟再關閉一條新連線 → 於是: 先關閉的一方會以 TIME_WAIT 狀態占用 port 約 60 秒(Linux),可用的 port 因此耗盡 → 畫面上: 內部請求失敗,導致存檔失敗、功能異常
症狀: 吃指令/回檔, 連不上/無限讀取 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 基礎設施團隊(伺服器基礎設施)
這部分決定遊戲如何透過網路收送資料。即使是同一條線路,依使用哪種協定、socket 選項怎麼設,遺失一個封包時,可能只是「稍微跳一下」,也可能變成「停住 1 秒再快轉」。
TCP 保證依送出的順序完整交付。代價是遺失一個時,在重新收到之前,連後面抵達的資料也全部不交給遊戲,一直等待。UDP 什麼都不保證。抵達的資料會立即交出、不用等待,但遺失的部分要由遊戲自行處理。因此動作性強的遊戲會在 UDP 上自行實作所需程度的可靠性(可靠 UDP),而許多 MMO 則採用容易實作的 TCP,並承受它的弱點。
Socket 選項是這些行為的細部設定:小封包要不要累積後再送(TCP_NODELAY)、收送緩衝區要多大(SO_SNDBUF、SO_RCVBUF)、何時察覺失效的連線(SO_KEEPALIVE、TCP_USER_TIMEOUT)、關閉時剩下的資料怎麼處理(SO_LINGER)。預設值大多是為了用較少的封包有效率地傳送大量資料,對頻繁收送小封包的遊戲往往不利。
TCP 只會依送出的順序把收到的資料交給遊戲。17 號封包遺失時,即使 18~30 號已經抵達,也全部要等到 17 號重新送達(HOL 阻塞)。UDP 是一抵達就交出,所以即使 17 號始終沒來,其餘的也能及時處理。
重傳為什麼會發生(Wi-Fi、壅塞、MTU 黑洞、不必要的重傳等)以及找出原因的方法,在 06 TCP 重傳中依原因分別說明。
TCP 為了維持順序,在重新收到遺失的那一個封包之前,不會把之後到達的封包交給遊戲。
為什麼: 一個封包遺失 → 於是: 後面的封包都已抵達,卻只能在接收緩衝區等待 → 畫面上: 停住之後一口氣全部放行,出現快轉
症狀: 定格, 快轉 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 遊戲開發團隊(用戶端開發)
每次重傳又失敗,等待時間就加倍,於是線路短暫中斷會變成長時間停住。
為什麼: 線路短暫中斷,重傳也接連失敗 → 於是: 到下一次嘗試的等待時間以 0.3 → 0.6 → 1.2 → 2.4 秒逐次加倍(以 ping 100ms 計) → 畫面上: 線路只斷了 1 秒,遊戲卻停住 2 秒以上。斷得更久最後就會斷線
症狀: 定格, 斷線 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 遊戲開發團隊(用戶端開發)
把小封包集中起來送的 Nagle 演算法,與延後送出 ACK 的延遲 ACK 互相牽制,每次把訊息分段寫入就會延遲 40~200ms。
為什麼: 沒有開啟 TCP_NODELAY 就分段寫入小訊息 → 於是: 傳送端在等 ACK,接收端卻延後送出 ACK → 畫面上: 線路 ping 很低,每個動作卻都固定慢半拍,出現輸入延遲
症狀: 輸入延遲 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 遊戲開發團隊(用戶端開發)
線路很慢的某位玩家傳送緩衝區已滿時,若用阻塞方式(緩衝區有空間之前呼叫不會返回的傳送)送出,伺服器執行緒就會一直等那一個人。
為什麼: 慢速用戶端的傳送緩衝區已滿 → 於是: 由於是阻塞式傳送,伺服器執行緒會等到緩衝區有空間為止 → 畫面上: 該執行緒負責的所有人都出現定格、慢動作
症狀: 定格, 慢動作 · 主要負責 遊戲開發團隊(伺服器開發)
對於待傳送資料不斷累積的用戶端,伺服器會丟棄過時的狀態更新,或直接切斷連線。
為什麼: 用戶端的線路跟不上伺服器送出的資料量 → 於是: 伺服器丟棄過時的狀態更新,或在超過上限時切斷連線 → 畫面上: 只有那個人出現瞬移或斷線
症狀: 瞬移, 斷線 · 主要負責 遊戲開發團隊(伺服器開發)
對方沒有送出關閉訊號就消失時,TCP 要過很久才會察覺。keepalive(確認閒置連線是否仍存活的 TCP 功能)預設是關閉的,即使開啟,也要閒置 2 小時才開始確認。
為什麼: 用戶端因斷電或線路中斷,沒有送出關閉訊號就消失 → 於是: 伺服器認為連線仍存活(keepalive 預設 7,200 秒;若有正在傳送的資料,約 15 分鐘後才放棄重傳) → 畫面上: 留下幽靈角色,重新連線時出現「帳號已登入」錯誤
症狀: 連不上/無限讀取, 看不見/幽靈物件 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 遊戲開發團隊(用戶端開發), 基礎設施團隊(伺服器基礎設施)
超過 MTU(一次能傳送的最大大小)的 UDP 封包會在 IP 層被分段(fragmentation),只要遺失其中一個分段,整個封包就會被丟棄。
為什麼: 人多之處的快照超過 1,500 位元組 → 於是: 拆成多個分段傳送,只要遺失一個就整個丟棄 → 畫面上: 封包越大,遺失率高出好幾倍。只在人多的地方出現瞬移
症狀: 瞬移 · 主要負責 遊戲開發團隊(伺服器開發)
在 UDP 上自行實作的重傳規則太保守時復原會變慢,太積極時反而讓線路更壅塞。
為什麼: 重傳間隔、次數、視窗大小的設定與線路不相符 → 於是: 復原太慢,或重複傳送讓壅塞惡化 → 畫面上: 技能被吃、快轉、壅塞時 lag 更嚴重
症狀: 吃指令/回檔, 快轉 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 遊戲開發團隊(用戶端開發)
連線閒置一段時間後,TCP 會再次縮小壅塞視窗(一次能送出的量),所以突然要送大量資料時,得分成好幾次送出。
為什麼: 透過原本閒置的連線送出進入城鎮等大量資料 → 於是: 壅塞視窗已縮小,只能分成多個往返傳送 → 畫面上: 剛進入時,周圍的角色與 NPC 晚了好幾個往返才出現(伺服器越遠越明顯)
症狀: 輸入延遲, 看不見/幽靈物件 · 主要負責 基礎設施團隊(伺服器基礎設施) · 協同 遊戲開發團隊(伺服器開發)
TCP 把封包遺失視為壅塞的訊號,會把傳送速率降低 30~50%。對 Wi-Fi 造成的遺失也會做出同樣的反應。
為什麼: 要送的資料量大時,Wi-Fi 或線路上發生少量封包遺失 → 於是: TCP 大幅降低傳送速率後慢慢回升(Linux、Windows 預設的 CUBIC 會降低 30%) → 畫面上: 人多的地方狀態更新積壓,出現快轉、輸入延遲
症狀: 快轉, 輸入延遲 · 主要負責 基礎設施團隊(伺服器基礎設施) · 協同 遊戲開發團隊(伺服器開發)
伺服器倉促切斷連線時,最後送出的通知或存檔完成訊號會消失。
為什麼: 伺服器以強制關閉(RST)結束連線。把 SO_LINGER 設為 0 秒,或沒讀完收到的資料就關閉時會發生 → 於是: 仍在傳送中的踢出原因與最後的資料被丟棄 → 畫面上: 沒來由地出現「因不明錯誤中斷連線」
症狀: 斷線 · 主要負責 遊戲開發團隊(伺服器開發)
執行緒在等待某個 socket 時無法做其他事的架構,玩家越多整體就越慢。
為什麼: 每條連線各自等待讀寫的方式 → 於是: 一條連線的延遲擴散到同一執行緒的其他連線 → 畫面上: 同時上線人數越多,所有人都出現慢動作、輸入延遲
症狀: 慢動作, 輸入延遲 · 主要負責 遊戲開發團隊(伺服器開發)
多個處理程序共用同一個 port 接收時,kernel 會依位址雜湊為每個連線指定負責的處理程序,之後不再更換。其中一個處理程序停住時,只有分配給它的人要等待。
為什麼: 閘道或登入伺服器用 SO_REUSEPORT 啟動多個處理程序 → 於是: 即使某個處理程序因 GC 或過載停住,分配給它的新連線與 UDP 封包也不會轉給其他處理程序 → 畫面上: 只有部分人連不上或定格。在處理程序數量改變的重新啟動時,部分 UDP session 會中斷
症狀: 連不上/無限讀取, 定格, 斷線 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 基礎設施團隊(伺服器基礎設施)
Windows 伺服器向已經離開的用戶端送出 UDP 時,會收到「port 無法到達」(ICMP)的通知。這個通知會讓下一次接收呼叫以錯誤結束;如果伺服器程式碼把這個錯誤當成 socket 本身故障來處理,所有使用該 socket 的人都會受到影響。
為什麼: 持續向剛離開的用戶端位址送出 UDP,收到「port 無法到達」(ICMP)通知 → 於是: Windows 讓下一次接收呼叫以 WSAECONNRESET(10054)錯誤結束,伺服器程式碼因此停止接收或關閉 socket → 畫面上: 使用該 socket 的所有人同時定格、斷線
症狀: 斷線, 定格 · 主要負責 遊戲開發團隊(伺服器開發)
實際計算遊戲邏輯的程式。移動、戰鬥、怪物 AI、視野計算、廣播,全都必須在一次「tick」內完成。人越集中在同一個地方,視野計算量與要送出的封包就會以人數的平方增加。
伺服器以固定的 tick 間隔計算遊戲狀態。20 tick 伺服器每 50ms 計算一次,要在這段時間內套用所有玩家的輸入、移動怪物、計算誰看得到誰(視野、AOI),再把變化的內容送給所有看得到的人。這 50ms 就是 tick 預算。超出預算,下一個 tick 就會延後。每個 tick 只推進固定遊戲時間的伺服器,整個遊戲世界的時間會變慢(慢動作);依實際經過的時間一次推進的伺服器,雖然能維持速度,但封包變得稀疏,會卡頓、瞬移。無論哪種,反應都會變慢。一個遊戲執行緒負責整個伺服器(頻道)時,該伺服器的所有人都會受影響;如果每個地圖分配一個執行緒,就是那個地圖的人一起受影響。
問題在於人數。所有人兩兩比較的話,100 人每個 tick 要比較約 1 萬次,1,000 人約 100 萬次。因此伺服器會把地圖切成格狀(grid),只比較鄰近的格子,但像世界王、攻城戰、城鎮廣場活動這樣所有人擠到同一個格子附近時,格狀的效果就會降低,計算量與要送出的資料會暴增。再加上多個執行緒等待同一份資料的鎖,以及在 tick 中途等待 DB 回應的同步呼叫,等待期間該執行緒負責的所有人都會一起停住。
伺服器的 tick 就像指揮的拍子。管弦樂團的團員(玩家)越多,一拍之內要顧及的樂譜就越多,拍子一亂,整首曲子都會變慢。如果有人跑去倉庫(DB)找一張樂譜,所有人都得等他。
一個 tick 內要做的事超出預算時,伺服器的 tick 週期會拉長,整個區域的遊戲進行變慢,或出現卡頓。
為什麼: 一個 tick(例如 50ms)要處理的事超出預算 → 於是: 每秒應計算 20 次的遊戲狀態只算了 8 次 → 畫面上: 整個區域出現慢動作(依伺服器設計也可能是卡頓),技能反應變慢
症狀: 慢動作, 輸入延遲, 卡頓 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 基礎設施團隊(伺服器基礎設施)
若要判斷誰能看到誰而讓所有人兩兩比較,人數變成 10 倍時,運算量會變成 100 倍。
為什麼: 所有角色兩兩比較距離,或即使切成格狀(grid),一個格子附近仍擠了數百人 → 於是: 100 人約比較 1 萬次,1,000 人約 100 萬次 → 畫面上: 在世界王、攻城戰等人潮聚集的地方 tick 時間暴增,出現慢動作、卡頓
症狀: 慢動作, 卡頓 · 主要負責 遊戲開發團隊(伺服器開發)
把一個人的移動送給所有看得到他的人時,要送出的狀態更新數量會是聚集人數的平方。
為什麼: 把一個人的變化傳送給所有看得到的人 → 於是: 1,000 人互相看得到時,每個 tick 有 100 萬筆狀態更新 → 畫面上: 傳送佇列與頻寬飽和,造成延遲與遺失(輸入延遲、快轉、瞬移)
症狀: 輸入延遲, 瞬移, 快轉 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 基礎設施團隊(伺服器基礎設施)
在每個區域由一個執行緒負責的架構中,人潮擠到同一處時,只有那一個核心會到 100%。
為什麼: 一個區域(頻道)由一個執行緒負責 → 於是: 人潮擠到同一處時只有那個核心飽和,其餘核心還有餘裕 → 畫面上: 只有那個區域 lag,其他區域正常
症狀: 慢動作, 輸入延遲 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 基礎設施團隊(伺服器基礎設施)
多個執行緒為了寫入同一份資料而等待同一個鎖時,執行緒再多也只能一次執行一個。
為什麼: 拍賣場、公會倉庫這類共用資料由多個執行緒同時使用 → 於是: 拿到鎖的執行緒完成之前,其餘執行緒都在等待 → 畫面上: 只有特定功能變慢,嚴重時整個 tick 延遲
症狀: 輸入延遲, 定格 · 主要負責 遊戲開發團隊(伺服器開發)
兩個執行緒各自等待對方持有的鎖時,就會永遠停住。
為什麼: 執行緒 A 持有鎖 1 並等待鎖 2,B 持有鎖 2 並等待鎖 1 → 於是: 兩者都永遠停住,相關的執行緒也接連停住 → 畫面上: 整台伺服器停止運作,看門狗(watchdog)重新啟動時所有人斷線
症狀: 定格, 斷線 · 主要負責 遊戲開發團隊(伺服器開發)
在 tick 進行中等待 DB 回應或檔案寫入時,伺服器上整個遊戲進行就會停住同樣長的時間。
為什麼: 在 tick 內等待 DB 查詢與儲存、log 寫入、外部 API 呼叫 → 於是: DB 花 100ms,tick 也停住 100ms → 畫面上: 每當 DB 或磁碟變慢,整個野外就頓一下
症狀: 定格, 卡頓 · 主要負責 遊戲開發團隊(伺服器開發)
請求進來的速度比處理速度快而在佇列中累積時,排在後面的請求要幾秒後才會處理,或被丟棄。
為什麼: 請求抵達的速度比處理速度快 → 於是: 佇列變長,超過上限就丟棄 → 畫面上: 技能、交易反應變慢或被吃掉
症狀: 輸入延遲, 吃指令/回檔 · 主要負責 遊戲開發團隊(伺服器開發)
所有怪物重生、所有 buff 到期、整點獎勵都擠在同一個 tick 時,那一個 tick 的負擔會變成平常的數十倍。
為什麼: 重生、到期、獎勵、自動存檔的計時器都設在同一時刻 → 於是: 那一個 tick 的工作量是平常的數十倍 → 畫面上: 每到固定時刻就頓一下
症狀: 定格, 卡頓 · 主要負責 遊戲開發團隊(伺服器開發)
數百隻怪物同時追擊玩家並計算路徑時,會耗用大量 CPU。
為什麼: 拉一大群怪或大量生成時,許多怪物同時追擊玩家 → 於是: 每隻怪物各自計算尋路 → 畫面上: 只有那個練功區出現慢動作
症狀: 慢動作 · 主要負責 遊戲開發團隊(伺服器開發)
把要送出的資料轉成位元組並壓縮也要耗用 CPU,人多時這項成本會暴增。
為什麼: 每筆狀態更新都要把結構轉成位元組並壓縮 → 於是: 成本與人數的平方成正比增加 → 畫面上: 傳送變慢,出現輸入延遲
症狀: 輸入延遲 · 主要負責 遊戲開發團隊(伺服器開發)
伺服器處理程序因未處理的錯誤而終止時,該伺服器上所有人會同時斷線。
為什麼: 指向不存在對象的錯誤(null 參照)、錯誤的資料、記憶體不足等致命錯誤 → 於是: 伺服器(或 zone)處理程序結束 → 畫面上: 所有人同時斷線,上次存檔後的進度可能回檔
症狀: 斷線, 吃指令/回檔 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 基礎設施團隊(伺服器基礎設施)
負責處理工作的工作執行緒全被慢的工作綁住時,新請求只能無止盡地等待。
為什麼: 工作執行緒都被綁在等待外部 API 或 DB 回應上 → 於是: 沒有執行緒可以接新請求 → 畫面上: 登入、商城等特定功能出現無限讀取
症狀: 連不上/無限讀取, 輸入延遲, 定格 · 主要負責 遊戲開發團隊(伺服器開發)
因 bug 導致一個 tick 結束不了時,伺服器就會停住,看門狗會強制重新啟動。
為什麼: 條件寫錯導致迴圈結束不了,或遞迴失控 → 於是: tick 結束不了,伺服器停止 → 畫面上: 定格後所有人斷線
症狀: 定格, 斷線 · 主要負責 遊戲開發團隊(伺服器開發)
數百人同時攻擊同一隻王時,這隻王的運算全集中在一處,打擊資訊也要傳送給所有看得到的人。
為什麼: 數百人不停地對同一隻王施放技能、buff、debuff → 於是: 王的血量、仇恨列表、debuff 計算集中在一處,每次打擊都要把傷害數字與特效封包傳送給所有看得到的人 → 畫面上: 技能延遲命中,傷害數字一口氣跳出來,只有王的周圍出現慢動作
症狀: 輸入延遲, 快轉, 慢動作 · 主要負責 遊戲開發團隊(伺服器開發)
用傳送移動到擠滿人的城鎮時,伺服器必須一次送出數百位新進入視野者的外觀、裝備與狀態。
為什麼: 因傳送移動、登入或切換頻道,突然出現在人多的地方 → 於是: 伺服器一次產生並送出數百人份的完整資訊,自己的電腦也要一次載入 → 畫面上: 剛抵達時畫面短暫停住,角色一個一個慢慢出現,輸入也有延遲
症狀: 定格, 輸入延遲, 快轉 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 遊戲開發團隊(用戶端開發)
該消失的地上道具、召喚物、已結束的計時器沒有清理而不斷累積時,伺服器開得越久,每個 tick 要做的事就越多。
為什麼: 地上道具、召喚物、已到期的計時器、空隊伍資訊沒有及時刪除 → 於是: 每個 tick 要走訪的清單一天比一天長 → 畫面上: 維護剛結束時正常,過了幾天後只有那台伺服器或那個區域越來越遲鈍
症狀: 慢動作, 卡頓, 輸入延遲 · 主要負責 遊戲開發團隊(伺服器開發)
新內容、特效、同步項目讓封包變大、變頻繁時,原本運作正常的伺服器在更新後就會碰到 MTU、頻寬、封包數上限。
為什麼: 更新增加了新的技能特效、同步項目、道具資訊,封包變大或變頻繁 → 於是: 大封包超過 MTU 而被分段,增加的流量碰到頻寬、雲端 PPS 上限與傳送緩衝區的限制 → 畫面上: 更新後人多的地方開始出現瞬移、技能被吃、輸入延遲。基礎設施沒有任何變更,封包遺失卻增加
症狀: 瞬移, 吃指令/回檔, 輸入延遲 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 基礎設施團隊(伺服器基礎設施), 基礎設施團隊(網路基礎設施)
伺服器記住的一切,也就是角色、怪物、道具、地圖,都放在記憶體裡。記憶體本身很快,但只要因 GC(回收不再使用的記憶體)而暫停、一點一點地流失(洩漏),或因為不足而發生 swap(把部分記憶體移到磁碟),就會 lag。
Java、C#、Go 這類自動管理記憶體的語言,會由垃圾回收器(GC)收集並回收用完丟棄的記憶體。依 GC 方式不同,有時會讓所有執行緒短暫停下。一次對整個 heap(程式執行中配置使用的記憶體區域)做 GC 時,存活的資料越多就越久,可達數百 ms 到數秒。ZGC 這類新型 GC 能把暫停縮短到 1ms 以下,代價是使用更多 CPU 與記憶體。C++ 伺服器沒有 GC,卻要面對忘記釋放的記憶體不斷累積的洩漏,以及可用空間被切成零碎小塊、無法使用大區塊的碎片化。即使有 GC,用完的物件如果某處仍持續參照,一樣會發生洩漏。
記憶體變慢的另一個原因是記憶體階層(儲存裝置離 CPU 多遠)。CPU 旁邊的快取是 1ns,RAM 是 100ns,把移到磁碟的記憶體(swap)讀回來,則要花 RAM 的 1,000 倍以上。請在下方「數值參考」表中,把這些差異放大成人的時間尺度來看看。
記憶體就像廚師的工作台。食材在伸手可及處(快取)就很快,走到冰箱(RAM)稍慢一點,工作台滿了、食材放到倉庫(磁碟、swap),每拿一次都要花很久。洗碗(GC)的時候,就得停下烹飪。
Java、C# 伺服器為了回收垃圾而暫停所有執行緒(stop-the-world)的期間,整個伺服器都會停住。
為什麼: heap 滿了,開始 GC → 於是: 暫停所有遊戲執行緒進行回收(存活資料越多,暫停越久) → 畫面上: 伺服器上的所有人同時定格,接著出現快轉
症狀: 定格, 快轉 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 基礎設施團隊(伺服器基礎設施)
即使是 C++ 伺服器,只要任務、AI、技能是用 Lua 等腳本執行,腳本引擎進行 GC 的期間,該 zone 就會停住。
為什麼: 每個 zone 的腳本引擎執行任務、AI、事件時,大量產生暫時物件 → 於是: 腳本引擎的 GC 一次回收大量物件時,該 zone 的 tick 會停住 → 畫面上: 只在特定 zone 或特定活動期間週期性地頓一下
症狀: 卡頓, 定格 · 主要負責 遊戲開發團隊(伺服器開發)
活動期間大量產生暫時物件時,GC 執行的頻率會比平常高出許多。
為什麼: 道具掉落、戰鬥 log、活動獎勵讓暫時物件暴增 → 於是: GC 頻率增加數倍,還沒來得及變成垃圾的物件被移到 Old 區,Full GC 也跟著提早發生 → 畫面上: 只在活動期間週期性地頓一下
症狀: 卡頓, 定格 · 主要負責 遊戲開發團隊(伺服器開發)
沒有釋放的記憶體一點一點累積,幾天後演變成 GC 暴增、swap 或處理程序被強制終止。
為什麼: 已登出角色的資料、事件處理常式(event handler)沒有被釋放 → 於是: 可用記憶體在幾天內逐漸減少 → 畫面上: 維護剛結束時一切正常,之後一天比一天 lag,最後伺服器當機
症狀: 慢動作, 定格, 斷線 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 基礎設施團隊(伺服器基礎設施)
存活資料逼近 heap 上限時,每次 GC 幾乎回收不到東西,GC 就會不停反覆執行。
為什麼: 活動湧入的人潮或記憶體洩漏,讓存活資料逼近 heap 上限 → 於是: GC 只能回收一點點,馬上又進行 Full GC,CPU 大部分都被 GC 占用 → 畫面上: 整個伺服器在幾分鐘內反覆出現慢動作與定格,最後因記憶體不足而終止
症狀: 慢動作, 定格, 斷線 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 基礎設施團隊(伺服器基礎設施)
記憶體不足、OS 把部分內容移到磁碟後,每次用到那塊記憶體,都得等待慢上 1,000 倍以上的磁碟。
為什麼: 使用中的記憶體超過實體 RAM → 於是: OS 把一部分移到磁碟,需要時再讀回來 → 畫面上: tick 暴增到數百 ms,伺服器上所有玩家都遇到慢動作與定格
症狀: 慢動作, 定格 · 主要負責 基礎設施團隊(伺服器基礎設施) · 協同 遊戲開發團隊(伺服器開發)
資料散落在記憶體各處時,CPU 每次都必須到較慢的 RAM 讀取並等待資料。
為什麼: 物件透過指標散落各處,存取順序雜亂 → 於是: CPU 快取裡沒有,每次都要從 RAM 讀取(慢 100 倍左右) → 畫面上: 做同樣的事,tick 成本卻高出數倍,嚴重時出現慢動作
症狀: 慢動作 · 主要負責 遊戲開發團隊(伺服器開發)
反覆配置與釋放,讓閒置空間被切得零碎時,占用的記憶體會遠多於實際使用量。
為什麼: 多個執行緒長時間配置、釋放大小不一的記憶體 → 於是: 閒置空間零散分布,無法歸還給 OS,用量像洩漏一樣持續增加 → 畫面上: 開越久越會因 swap 與記憶體不足而變慢,最後被強制終止
症狀: 慢動作, 斷線 · 主要負責 遊戲開發團隊(伺服器開發)
在有兩顆 CPU 的伺服器上,使用接在另一顆 CPU 上的記憶體時,存取會變慢。
為什麼: 執行緒與記憶體位於不同的 CPU 插槽(socket) → 於是: 記憶體存取變慢(依設備不同,約慢 1.5~2 倍) → 畫面上: 規格相同,各處理程序的效能卻有落差
症狀: 慢動作 · 主要負責 基礎設施團隊(伺服器基礎設施)
Log、角色存檔、地圖資料、DB 檔案都在磁碟上。磁碟比記憶體慢數百倍(SSD)到 10 萬倍(HDD),所以如果遊戲伺服器的架構會等待磁碟,磁碟一忙,遊戲也會跟著停住。
磁碟的效能以「1 秒能讀寫幾次」(IOPS)來衡量。老舊 HDD 約 150 次出頭,SSD 則是數萬到數十萬次。雲端磁碟的上限依付費多寡而定(AWS 預設的 gp3 是 3,000 次)。部分雲端磁碟與小規格伺服器,除了平時速度之外,還會提供可短暫發揮更高效能的 burst credit(突發額度),但忙碌時段一拉長,額度耗盡,速度就會突然下降。「每天晚上過了幾個小時就會 lag」這類回報就是這個樣子。
關鍵在於誰在等。一般的檔案寫入會先由 OS 收進記憶體,之後再寫到磁碟,所以通常立刻就完成。問題出在要求等到「實際寫入磁碟為止」(fsync),或是 OS 能暫存在記憶體中的上限滿了的時候。這時若由遊戲執行緒直接等待(同步),磁碟延遲 100ms,tick 也會停住 100ms。把寫入交給另一個執行緒(非同步),遊戲就不會停住,但伺服器突然當機時,還沒寫入的內容可能會消失(吃指令/回檔)。
磁碟是倉庫,IOPS 是倉庫門的數量。門少的話,搬進搬出的人就得排隊。Burst credit 就像能短暫衝刺的體力,用完就回到走路的速度。
遊戲執行緒每寫一行 log 都要等磁碟寫完的話,磁碟一忙,遊戲進行也會跟著停住。
為什麼: 在遊戲執行緒上直接把戰鬥、交易 log 寫入檔案 → 於是: 要求確實寫入(fsync),或 OS 的寫入緩衝區(頁面快取)達到上限時,磁碟一忙,一次寫入就要數十 ms → 畫面上: log 量大的戰鬥中會頓一下
症狀: 卡頓, 定格 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 基礎設施團隊(伺服器基礎設施)
要求把資料「確實」寫入磁碟時,依磁碟不同,每次需要 0.1ms~數十 ms;請求一多,佇列就會變長。
為什麼: 定期存檔、登出潮使確實寫入的請求大量湧入 → 於是: 磁碟佇列變長 → 畫面上: 每到存檔時間就 lag,登出、切換頻道變慢
症狀: 卡頓, 輸入延遲 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 基礎設施團隊(伺服器基礎設施), 基礎設施團隊(DB 基礎設施)
部分雲端磁碟與小規格伺服器有 burst credit(突發額度),可以短時間跑得比基準效能快;忙碌時段一拉長、額度用完,速度就會突然下降。
為什麼: 長時間以高於基準效能的速度使用 → 於是: burst credit 用完,效能驟降回基準值 → 畫面上: 每天晚上過了幾個小時後開始 lag
症狀: 卡頓, 慢動作, 輸入延遲 · 主要負責 基礎設施團隊(伺服器基礎設施) · 協同 基礎設施團隊(DB 基礎設施)
請求數超過磁碟每秒能處理的數量時,佇列會變長,延遲急遽增加。
為什麼: 讀寫請求接近磁碟的處理能力 → 於是: 佇列變長(通常在使用率 90% 以上時急遽增加) → 畫面上: 存檔、載入變慢;若是同步呼叫則會定格
症狀: 輸入延遲, 定格 · 主要負責 基礎設施團隊(伺服器基礎設施) · 協同 遊戲開發團隊(伺服器開發), 基礎設施團隊(DB 基礎設施)
log 與 dump 越積越多、把磁碟塞滿時,寫入會失敗;沒有防範措施的話,伺服器會當機。
為什麼: log、dump、暫存檔累積到 100% → 於是: 寫入失敗。沒有錯誤處理就當機,有的話則存檔失敗 → 畫面上: 斷線、進度回檔
症狀: 斷線, 吃指令/回檔 · 主要負責 基礎設施團隊(伺服器基礎設施) · 協同 基礎設施團隊(DB 基礎設施), 遊戲開發團隊(伺服器開發)
凌晨的備份、log 壓縮、安全掃描獨占磁碟時,遊戲伺服器的讀寫就會被擠到後面。
為什麼: 排定的備份、壓縮作業開始 → 於是: 占用大部分的磁碟頻寬與 IOPS → 畫面上: 每天同一時間 lag
症狀: 卡頓, 輸入延遲 · 主要負責 基礎設施團隊(伺服器基礎設施) · 協同 基礎設施團隊(DB 基礎設施)
伺服器在副本、地圖資料第一次被請求時才從磁碟讀取的話,那個 tick 期間所有人都會停住。
為什麼: 有人第一次進入副本或地圖 → 於是: 伺服器在遊戲執行緒上從磁碟讀取資料 → 畫面上: 那台伺服器上的所有人短暫定格
症狀: 定格 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 基礎設施團隊(伺服器基礎設施)
伺服器當機時要把數 GB 的記憶體寫入磁碟,有時會讓重新啟動延後好幾分鐘。
為什麼: 伺服器當機,把整個記憶體寫成檔案 → 於是: 寫入數 GB 的期間無法重新啟動 → 畫面上: 伺服器當機造成斷線後,很長一段時間連不上
症狀: 連不上/無限讀取 · 主要負責 基礎設施團隊(伺服器基礎設施) · 協同 遊戲開發團隊(伺服器開發)
HDD 的讀寫頭必須在碟片上移動(尋軌,seek),所以讀寫分散各處的資料時,每次要花將近 10ms。
為什麼: 老舊伺服器或低價儲存裝置使用 HDD → 於是: 每次分散的讀寫約 10ms → 畫面上: 存檔、載入全面變慢
症狀: 輸入延遲 · 主要負責 基礎設施團隊(伺服器基礎設施) · 協同 基礎設施團隊(DB 基礎設施), 遊戲開發團隊(伺服器開發)
這裡存放角色、道具、貨幣、交易紀錄這類絕對不能遺失的東西。DB 變慢時,戰鬥正常,但道具很晚才入帳、交易失敗、登入一直跑不完。如果遊戲伺服器的架構會等待 DB,整個野外都會停住。
遊戲伺服器會預先與 DB 建立幾條連線(連線池)輪流使用。一個查詢(送給 DB 的請求)花很久時,那條連線就會一直被占用;連線池的連線全被占用時,其餘請求就在佇列中等待。查詢變慢的常見原因有兩個:沒有索引(就像書的索引)而讀取整張資料表(全表掃描),或是多個請求同時想修改同一筆資料列而等待鎖定。
DB 為了可靠,也為了承接大量請求,會使用多種機制:分擔讀取的複本、故障時接手的備援 DB、定期把變更集中寫入磁碟的檢查點。檢查點集中時會暫時變慢。複本落後時會出現「剛買的道具看不到」;複寫落後的狀態下切換到備援 DB,則會出現「登入後回到了稍早的狀態」這類吃指令/回檔症狀。如果遊戲伺服器幾分鐘才儲存一次角色,伺服器當機時就會變成「回到 10 分鐘前」。
DB 就像銀行櫃台。櫃台數量(連線池)是固定的,一個請求要翻遍整本帳簿(全表掃描)時,後面的請求都得等。所有人都想打開同一個金庫(熱點資料列)時,一次只能進去一個人。
沒有索引時,要找出符合條件的資料列,就必須讀完整張資料表(全表掃描)。
為什麼: 部署新功能時,加入了沒有索引的條件查詢 → 於是: 掃描數百萬筆資料列,一個查詢就要數百 ms~數秒 → 畫面上: 信箱、交易紀錄載入變慢,連線被占住,連其他請求也要等待
症狀: 輸入延遲, 連不上/無限讀取 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 基礎設施團隊(DB 基礎設施)
所有人都要修改同一筆資料列(公會倉庫、拍賣場熱門道具、全伺服器共用的計數器)時,一次只有一個請求能取得鎖定。
為什麼: 活動、熱門道具讓修改集中在同一筆資料列 → 於是: 請求一直等到取得鎖定為止 → 畫面上: 交易失敗、「請稍後再試」、逾時
症狀: 吃指令/回檔, 輸入延遲 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 基礎設施團隊(DB 基礎設施)
兩個 transaction(當成一個整體處理的一組 DB 操作)互相等待對方鎖住的資料列時,DB 會強制取消其中一方。
為什麼: 交易 A 依道具 → 貨幣的順序鎖定,交易 B 依貨幣 → 道具的順序鎖定 → 於是: DB 偵測到死結,回滾其中一方 → 畫面上: 交易、製作偶爾失敗,道具被退回
症狀: 吃指令/回檔, 輸入延遲 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 基礎設施團隊(DB 基礎設施)
與 DB 建立的連線數是固定的,慢查詢占住連線時,其餘請求只能等待。
為什麼: 慢查詢或請求暴增,使所有連線都在使用中 → 於是: 新請求要等到有連線空出來 → 畫面上: 登入時無限讀取、存檔變慢、逾時
症狀: 連不上/無限讀取, 輸入延遲 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 基礎設施團隊(DB 基礎設施)
寫入走主 DB、讀取走複本的架構下,複本跟得慢時,剛寫入的內容就會看不到。
為什麼: 寫入集中湧向主 DB,複本落後數秒 → 於是: 從複本讀取剛存檔的內容時,資料還不存在 → 畫面上: 剛買的道具看不到、交易所價格還是舊的、重複發放的 bug
症狀: 吃指令/回檔 · 主要負責 基礎設施團隊(DB 基礎設施) · 協同 遊戲開發團隊(伺服器開發)
DB 定期把記憶體中的變更一次大量寫入磁碟的瞬間,查詢會變慢。
為什麼: 變更累積後定期寫入磁碟 → 於是: 那一刻磁碟變忙,查詢變慢 → 畫面上: 存檔、載入週期性變慢
症狀: 輸入延遲, 卡頓 · 主要負責 基礎設施團隊(DB 基礎設施)
DB 重新啟動後記憶體快取是空的,有一段時間所有查詢都要從磁碟讀取。
為什麼: 因維護而重新啟動 DB → 於是: 常用資料不在記憶體中,只能從磁碟讀取 → 畫面上: 維護剛結束的一段時間內,登入、載入很慢
症狀: 連不上/無限讀取, 輸入延遲 · 主要負責 基礎設施團隊(DB 基礎設施) · 協同 遊戲開發團隊(伺服器開發)
載入一個角色要分開查詢數十次的話,數萬人同時登入就會變成數百萬個查詢。
為什麼: 載入角色時,道具、技能、任務各自分開查詢 → 於是: 維護剛結束時大量同時登入,查詢暴增 → 畫面上: 登入時無限讀取,連正在遊戲中的玩家存檔都被拖慢
症狀: 連不上/無限讀取, 輸入延遲 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 基礎設施團隊(DB 基礎設施)
在營運時段執行排行榜彙總、信件批次發送、舊資料清理時,會占用鎖定與磁碟。
為什麼: 在營運時段執行大量作業 → 於是: 大範圍鎖定,占用磁碟與 CPU → 畫面上: 特定時段交易、存檔失敗,載入變慢
症狀: 輸入延遲, 吃指令/回檔 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 基礎設施團隊(DB 基礎設施)
主 DB 故障、切換到備援 DB 的期間無法寫入,最後一批還沒複寫過去的資料可能會遺失。
為什麼: 主 DB 故障,備援 DB 升格為主 DB → 於是: 切換期間有數秒~幾分鐘無法寫入;若是非同步複寫,尚未複寫的資料可能遺失 → 畫面上: 短時間內所有存檔失敗,道具、經驗值回檔
症狀: 吃指令/回檔, 定格, 斷線, 連不上/無限讀取 · 主要負責 基礎設施團隊(DB 基礎設施) · 協同 遊戲開發團隊(伺服器開發)
為了減輕負載而每隔幾分鐘才存檔一次的話,伺服器在兩次存檔之間當機時,進度就會消失。
為什麼: 每隔幾分鐘才儲存一次角色狀態 → 於是: 在兩次存檔之間發生伺服器當機或故障 → 畫面上: 重新連線後回到幾分鐘前的狀態(回檔)
症狀: 吃指令/回檔 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 基礎設施團隊(DB 基礎設施)
熱門資料的快取同時過期時,數千個請求會一口氣湧向 DB。
為什麼: 存放在 Redis 等快取中的熱門資料同時過期 → 於是: 要重新產生同一筆資料的請求一口氣湧向 DB → 畫面上: DB 過載,多項功能接連變慢甚至停住
症狀: 輸入延遲, 定格, 連不上/無限讀取 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 基礎設施團隊(DB 基礎設施)
一個 transaction 長時間不結束時,會一直持有鎖定,DB 也無法清理(purge)舊版本的資料,整體會越來越慢。
為什麼: 開著 transaction 等待其他伺服器的回應,或在營運中於主 DB 上執行長時間的彙總查詢 → 於是: 持有的鎖定一直不釋放,待清理的舊版本資料持續累積 → 畫面上: 使用那些資料列的功能逾時,存檔與查詢在幾個小時內全面變慢
症狀: 輸入延遲, 吃指令/回檔 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 基礎設施團隊(DB 基礎設施)
Redis 一次只處理一個指令,因此一個慢指令就會擋住後面所有的請求。
為什麼: 營運中用 KEYS 搜尋全部 key,或一次讀取、刪除含數百萬個元素的排行榜或清單 → 於是: 在該指令結束前,其他所有請求都要等待(數十 ms~數秒) → 畫面上: 使用 session、排行榜、快取的功能同時頓一下,登入變慢
症狀: 定格, 輸入延遲, 連不上/無限讀取 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 基礎設施團隊(DB 基礎設施)
程式碼沒變,DB 卻改變了處理同一個查詢的方式(執行計畫)時,昨天只要 2ms 的查詢,今天會變成數百 ms。
為什麼: 統計資訊自動更新、DB 重新啟動或資料分布改變,使 DB 重新建立執行計畫 → 於是: 選中了不走索引的計畫,同一個查詢慢了數十~數百倍,連線被占住 → 畫面上: 明明沒有部署,特定功能的載入卻突然變慢,連其他請求也要等待
症狀: 輸入延遲, 連不上/無限讀取 · 主要負責 基礎設施團隊(DB 基礎設施) · 協同 遊戲開發團隊(伺服器開發)
在服務中替資料表新增欄位或索引時,可能因為一個只需要短暫持有的鎖定,讓所有使用該資料表的請求都在等待。
為什麼: 以 hotfix 替營運中的資料表新增欄位或索引 → 於是: schema 變更在等待先前開啟的長 transaction,後面進來的所有請求又在等待這個 schema 變更 → 畫面上: 使用該資料表的功能(背包、信件等)整個停住並逾時
症狀: 輸入延遲, 吃指令/回檔, 連不上/無限讀取 · 主要負責 基礎設施團隊(DB 基礎設施) · 協同 遊戲開發團隊(伺服器開發)
現在的 MMO 大多是登入、閘道、野外、副本、聊天、隊伍、拍賣場、快取、DB 等伺服器互相呼叫運作的架構。一處故障就會擴散到相連的地方,部署、擴展、維護這類維運作業也會造成 lag。
把伺服器拆開,可以防止一處故障擴散到全體,但相對地會產生呼叫鏈(伺服器依序呼叫其他伺服器的連結)。例如遊戲伺服器呼叫拍賣場伺服器,拍賣場伺服器再呼叫快取與 DB。鏈尾的伺服器一變慢,前面的伺服器就會一邊等待回應、一邊持續占用執行緒與連線,最後連看似無關的功能也停擺。這稱為連鎖故障,要用逾時與斷路器(暫時阻擋持續失敗之呼叫的機制)防止擴散。
維運作業也是 lag 的原因。部署更新時的重新啟動、人潮湧入時自動增加伺服器所需的幾分鐘、切換 zone 時把角色搬到其他伺服器的過程、bot 與巨集造成的隱形負載,在玩家看來都是「lag」。
伺服器架構就像多個部門互相傳遞簽核的公司。簽核流程末端的部門(DB)一變慢,前面的部門就拿著文件排隊,最後整間公司的業務都停擺。逾時是「超過 10 分鐘沒回覆就先退件」的規則,斷路器則是「持續退件時,一段時間內不再把文件送到那個部門,直接退回」的規則。
在用戶端與遊戲伺服器之間放一台中介伺服器時,每經過一次就多一段處理時間,而且這台伺服器會成為單點故障。
為什麼: 用戶端 ↔ 閘道 ↔ 遊戲伺服器的架構 → 於是: 多了中介伺服器的處理與等待時間,過載時影響所有人 → 畫面上: 整體 ping 上升;閘道故障時,經過它的玩家全部斷線
症狀: 輸入延遲, 斷線 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 基礎設施團隊(伺服器基礎設施), 遊戲開發團隊(用戶端開發)
進入其他地圖或副本時,把角色資料交給另一台伺服器的過程中會產生延遲與失敗。
為什麼: 進入副本、移動到其他大陸,負責的伺服器因此改變 → 於是: 儲存 → 傳遞 → 載入;目標伺服器擁擠或沒有空的副本實例時要等待 → 畫面上: 載入很久、進場失敗、移動途中斷線
症狀: 連不上/無限讀取, 定格, 斷線, 拉回 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 基礎設施團隊(伺服器基礎設施)
一個服務變慢時,呼叫它的伺服器會卡在等待回應,連不相關的功能也跟著停擺。
為什麼: DB、認證等某一個服務變慢 → 於是: 呼叫端伺服器的執行緒與連線卡在等待回應,失敗請求的重試又加重負載 → 畫面上: 看似無關的功能也全部變慢或停擺
症狀: 定格, 輸入延遲, 連不上/無限讀取 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 基礎設施團隊(網路基礎設施)
聊天、隊伍、拍賣場這類與遊戲伺服器分開運作的伺服器故障時,只有那個功能無法運作。
為什麼: 功能專用伺服器變慢或掛掉 → 於是: 只有該功能的請求沒有回應 → 畫面上: 無法聊天、組隊邀請沒反應、交易所無限讀取(戰鬥正常)
症狀: 吃指令/回檔, 連不上/無限讀取 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 基礎設施團隊(伺服器基礎設施)
為了更新而重新啟動伺服器時,如果不先把連線移走,原本在那台伺服器上的人就會斷線,關閉前的存檔與重新連線也會同時湧入。
為什麼: 部署 hotfix,依序重新啟動伺服器 → 於是: 沒有把連線移到其他伺服器就關閉,那台伺服器上所有玩家的存檔同時湧向 DB → 畫面上: 沒有公告就斷線、重新連線暴增
症狀: 斷線, 連不上/無限讀取, 輸入延遲 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 基礎設施團隊(伺服器基礎設施)
人潮湧入時會自動增加伺服器,但準備要花幾分鐘,這段期間既有的伺服器處於過載。
為什麼: 活動開始,連線急增 → 於是: 新伺服器從啟動到準備好需要數分鐘 → 畫面上: 活動剛開始的幾分鐘內出現慢動作、連不上
症狀: 慢動作, 連不上/無限讀取 · 主要負責 基礎設施團隊(伺服器基礎設施) · 協同 遊戲開發團隊(伺服器開發)
發生故障時 log 會暴增,以同步方式送出 log 的伺服器會因為 log 變得更慢。
為什麼: 發生錯誤,log 與指標的傳送量暴增 → 於是: log 收集器消化不及,同步傳送的伺服器只能等待 → 畫面上: 故障時的卡頓、定格因為 log 變得更嚴重
症狀: 卡頓, 定格 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 基礎設施團隊(伺服器基礎設施)
各伺服器的時鐘各差一點時,冷卻時間、buff、活動開始的判定就會在伺服器之間對不上。
為什麼: 時間同步停止的伺服器,時鐘與其他伺服器相差數百 ms~數秒 → 於是: 在伺服器之間傳遞 buff 結束時間這類絕對時間,判定就會對不上 → 畫面上: 一移動 buff 就消失,或冷卻重新開始計算
症狀: 吃指令/回檔 · 主要負責 基礎設施團隊(伺服器基礎設施) · 協同 遊戲開發團隊(伺服器開發)
機器人送出請求的頻率遠高於真人,會吃掉伺服器的處理量。
為什麼: 大量連線的機器人不停重複打怪、移動、交易 → 於是: 伺服器處理量與 DB 負載增加 → 畫面上: 特定練功區或整個伺服器變慢(慢動作、輸入延遲)
症狀: 慢動作, 輸入延遲 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 基礎設施團隊(網路基礎設施)
平台登入、付款、身分驗證這類外部服務變慢或停擺時,會卡在那個步驟。
為什麼: 外部認證、付款服務故障或延遲 → 於是: 在該步驟等待回應 → 畫面上: 無法登入、付款失敗。已在遊戲中的人正常
症狀: 連不上/無限讀取, 吃指令/回檔 · 主要負責 外部(外部) · 協同 遊戲開發團隊(伺服器開發)
沒被分到近的區域、卻被分到遠方區域的伺服器時,即使線路正常,也只有那位玩家的 ping 一直偏高。
為什麼: GeoIP 資料錯誤、VPN、以隊友平均 ping 分配整個隊伍、人數不足時擴大到遠方區域的規則、依 DNS 解析器(resolver)位置分配 → 於是: 明明有近的區域,卻連到海外區域的伺服器 → 畫面上: 在多個區域設有伺服器的遊戲中,只有我(或只有我們隊伍)ping 一直偏高,出現輸入延遲、拉回、技能被吃
症狀: 輸入延遲, 拉回, 吃指令/回檔 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 遊戲開發團隊(用戶端開發), 基礎設施團隊(網路基礎設施), 外部(外部)
登入、API、更新伺服器的憑證過期或缺少中繼憑證時,從那一刻起新連線的用戶端 TLS 連線都會失敗。
為什麼: 憑證超過有效期限、伺服器送出時少了中繼憑證,或玩家裝置的日期時間錯誤 → 於是: 用戶端憑證驗證失敗,中斷 TLS 連線 → 畫面上: 在登入、更新階段連不上/無限讀取,只有商城等 HTTPS 功能失敗。已經連線的人大多正常
症狀: 連不上/無限讀取, 吃指令/回檔 · 主要負責 基礎設施團隊(網路基礎設施) · 協同 基礎設施團隊(伺服器基礎設施), 遊戲開發團隊(用戶端開發)
上市或維護剛結束時連線湧入,登入排隊碰到上限就拒絕新的排隊,正在排隊的玩家只要短暫斷線就會失去位置,回到隊伍最後面。
為什麼: 想連線的人比登入伺服器一次能接受的人數多,所以設排隊;排隊太長時,為了保護伺服器會拒絕新的排隊 → 於是: 排隊越長等待時間越久,這段期間 Wi-Fi、行動網路只要短暫斷線就會失去排隊位置 → 畫面上: 連不上/無限讀取、排隊中出現錯誤並關閉遊戲、又要從最後面重新排隊
症狀: 連不上/無限讀取, 斷線 · 主要負責 遊戲開發團隊(伺服器開發) · 協同 遊戲開發團隊(用戶端開發), 基礎設施團隊(伺服器基礎設施)
收到 lag 回報時,只要選出「誰、何時、什麼樣子」這三項。小幫手會依分數高低,列出本白皮書中最符合的候選原因。這不是確定診斷,但足以決定要先找哪個團隊。
收到回報或警示時,依範圍 → 時間點 → 層級的順序縮小範圍。異常集中在哪裡最能決定負責單位,與什麼事件重疊能縮小原因範圍,再以哪一層的指標異常來確認。搭配每張原因卡片上的「圖表上」與「確認方法」,就能從圖表形狀挑出候選原因,並立刻找到要查看的位置。
異常集中在哪裡
與什麼重疊
哪一層的指標異常
| 要確認的項目 | 看起來是這樣時 | 優先聯絡 |
|---|---|---|
| 伺服器 socket 接收佇列(Recv-Q) | 伺服器處理程序來不及讀取而堆積 | 遊戲開發團隊伺服器(tick 停住、GC、鎖) |
| 每條連線的重傳與 RTT | 只有部分連線,集中在特定 ASN | 基礎設施團隊網路 外部電信業者/玩家線路 |
| 一台主機上的所有連線 | 基礎設施團隊伺服器設備/OS(NIC、kernel) | |
| 更新剛結束後整個伺服器的重傳與頻寬增加 | 封包大小或頻率改變 | 遊戲開發團隊伺服器 基礎設施團隊網路(MTU、上限) |
| CPU steal、節流、softirq、NIC 丟棄 | 增加 | 基礎設施團隊伺服器設備/OS |
| 只有一個執行緒 100%、run queue 延遲、GC 暫停 | 增加 | 遊戲開發團隊伺服器 |
| DB 延遲增加,查詢數不變 | IOPS、鎖定、其他作業 | 基礎設施團隊DB 設備 |
| DB 查詢數或形態在更新後改變 | N+1、新查詢 | 遊戲開發團隊伺服器 |
| 海外據點的合成監控(RTT、遺失) | 不佳 | 基礎設施團隊網路 外部電信業者 |
| 合成監控正常,只有玩家端不佳 | 玩家環境或用戶端 | 外部玩家環境 遊戲開發團隊用戶端 |
| 斷線原因分布 | 心跳逾時↑/RST↑/伺服器主動斷線↑ | NAT、路徑/設備/伺服器 |
| 精確的週期(整點、N 分鐘) | 排程工作、備份、GC、活動 | 建立該排程的單位 |
只要知道監控圖表是什麼形狀,候選原因就會大幅減少。下面 13 種形狀,各自彙整了會造成該形狀的原因。原因卡片上的小圖也是同樣的形狀。實線是主要觀察的指標,虛線是一起觀察的指標(人數、等待、錯誤等),淺色虛線是平時的水準。
平時很低,每隔幾秒、幾分鐘或每逢整點,以相同間隔往上衝。
用戶端垃圾回收, 遊戲安全模組(反作弊)檢查, Wi-Fi 背景掃描, 排程工作, 計時器集中同時觸發, 伺服器 GC 全面暫停, 腳本引擎的 GC 暫停, fsync 暴增, 備份、壓縮、掃描作業, 檢查點與 log flush, 大量批次作業, Cache stampede
沒有固定間隔,不規則地往上衝,隨即回落。
畫格時間尖峰, 過度外插(dead reckoning), 用戶端預測不一致, 固定時間步長追趕失控, 背景處理程序占用 CPU, 用戶端記憶體不足、swap, 網路卡省電、驅動程式問題, Wi-Fi 干擾與訊號減弱, 5G↔LTE 頻繁切換(5G 涵蓋邊緣), 線路品質不良, Ring buffer 不足, 虛擬化額外開銷與 noisy neighbor, Kernel socket 緩衝區不足, CPU steal(虛擬機器), 記憶體回收與壓縮(compaction)造成的暫停, 系統時鐘跳動(NTP step), 慢速用戶端造成的阻塞式傳送, 可靠 UDP 的重傳設定, RST 強制關閉造成最後的資料遺失, 遊戲執行緒上的同步呼叫, 同步寫入 log, 伺服器端的延遲載入, DB 死結, Redis 慢指令, log 與監控過載, Lockstep 中等待最慢的玩家, Rollback netcode 預測失敗, 沒有時間戳記、一到就播放, 過於嚴格的伺服器驗證, 指令同步的路徑計算不一致, 視野登錄順序錯亂, 基準快照遺失, 消失通知遺失(幽靈物件), 物件 ID 重複使用造成誤認, 延遲飆升造成的不必要重傳
以更新、設定變更、路由變更這類特定時間點為界,升高一階後就維持在那裡。
海底電纜/國際線路故障, BGP 路由變更與收斂, DDoS 防護導流與誤判, OS、kernel、驅動程式、韌體更新後的效能變化, 更新改變了流量模式, 沒有索引的查詢, 執行計畫改變造成的查詢延遲, 營運中 schema 變更(DDL)的鎖定, 依賴外部服務, 路由變更、ECMP 不良路徑
在幾小時到幾天內一點一點往上升,隨運行時間拉長而變大。
時鐘同步誤差, float 時間精度損失, 用戶端記憶體洩漏, 省電模式、過熱降頻, 記憶體洩漏, Swap, 記憶體碎片化, 磁碟已滿, 長時間未結束的 transaction, 伺服器之間的時鐘差異
慢慢爬升,到重啟或清理的瞬間驟降,如此反覆。
像晚間尖峰一樣,每天只在同一時段隆起成一座小山。
Wi-Fi 頻道壅塞, 尖峰時段 peering 區段壅塞, 集中在特定電信業者玩家的驗證誤判, 瓶頸佇列溢位(壅塞遺失)
同時上線人數或聚集在同一處的人數增加時,會以比人數更陡的斜率跟著上升。
大量角色同畫面的渲染負載, 主執行緒封包處理瓶頸, 接收緩衝區溢位, 同一台裝置上的其他 App 占用頻寬, Bufferbloat(分享器佇列), 交換器 microburst, 執行緒過多與 context switch, 容器 CPU 節流(CFS 配額), UDP 封包的 IP 分段, 阻塞式 I/O 架構, 超出 tick 預算, 視野(AOI)計算量暴增(N²), 廣播量暴增, 單執行緒區域過載(熱點), 鎖競爭, 尋路運算暴增, 序列化與壓縮的成本, 集中在單一目標的戰鬥(世界王), 記憶體配置暴增, 熱點資料列鎖定競爭, 複寫延遲, 經由閘道/proxy, 切換 zone(跨伺服器轉移), 各連線的傳送預算與優先順序, 突發傳送造成淺緩衝區溢位, 雙工模式不一致
處理量或連線數到達某個值後就上不去,從那時起等待與錯誤開始增加。
顯示記憶體(VRAM)不足, 分享器效能不足、過熱, 電信業者限速與流量管理, DDoS 造成共用線路飽和, 防火牆 session 表飽和, 雲端 NAT 閘道的連線與 port 上限, 資料中心線路飽和, NIC 中斷集中在單一核心, 超過雲端 PPS 上限, NIC 頻寬飽和, 檔案描述子上限, 伺服器 conntrack 表飽和, 伺服器間連線的臨時 port 耗盡, 訊息佇列積壓, 執行緒池耗盡, GC thrashing(heap 可用空間不足), 雲端磁碟 burst credit 耗盡, IOPS 上限與佇列飽和, 連線池耗盡, 連鎖故障, 登入排隊上限與重新連線寬限不足, 記憶體/VRAM 不足導致串流失敗, Policer 丟棄超額流量, 接收端伺服器主機丟棄封包, 防火牆與連線追蹤丟棄封包, 中間設備超過處理上限(防火牆、IPS、DDoS 防護)
持續停在高檔,不會突然飆高。原因來自距離、路由、設計這類結構性因素。
沒有內插緩衝或緩衝太短, 垂直同步(V-Sync)與渲染佇列, 計時器解析度, 顯示器、輸入裝置與畫格生成的延遲, 傳播延遲(物理距離), 繞遠路的路由, 中斷合併過度, GRO/LRO 合併等待延遲, 伺服器電源管理(C-state、頻率調整)造成延遲飆高, Nagle 演算法 + 延遲 ACK, 快取未命中, HDD 尋軌延遲, 伺服器回應後才演出(請求-回應方式), 依序往返過多的協定(chatty), 沒有技能預輸入, 用戶端權威, 雙重 tick 等待, 快照傳送頻率太低, 封包亂序造成的不必要快速重傳, RTO 設定不符合環境, 中間設備移除 TCP 選項
大部分正常,只有特定玩家、地區、電信業者或裝置特別高。
儲存裝置太慢,資源串流跟不上, 用戶端閃退, 安全軟體的封包檢查, overlay 程式干擾, RRC 狀態切換延遲(行動網路無線省電), 行動網路訊號弱、收訊死角, 公共 Wi-Fi/公司網路限制, 衛星網路(低軌/同步軌道), ECMP 其中一條路徑異常, 國家/電信業者層級的 UDP 限制與封包檢測, DNS 故障與延遲, 經由 VPN/遊戲加速器, 負載平衡器分配不均與健康檢查誤判, 纜線不良與 port 錯誤, MTU 不一致(只有大封包消失), 慢速用戶端(slow consumer)的處理策略, keepalive 預設值 2 小時, 閒置後的慢啟動(slow start), SO_REUSEPORT 分配不均, NUMA 遠端記憶體, 巨集/機器人過多, 配對與區域分配錯誤, 被 ping 吃掉的短判定區間, 沒有延遲補償的判定, 延遲補償過度, 主機(房主)架構, 先行演出後遭伺服器拒絕, 慢的人在別人畫面上快轉移動, 一到就處理的伺服器造成快轉, 各玩家的輸入緩衝大小, 一個慢的隊友與王的機制, 怪物控制權在慢速用戶端上, 特定角色的資料過於龐大, 頻道、實例、相位不同, 載入中抵達的出現通知被丟棄, 固定 UDP port 衝突, 以 IP/裝置區分 session 的 bug, 多開限制, 快取/資源檔案同時存取衝突, 顯示選項不同, 用戶端版本、資料不一致, 時鐘推估誤差導致物件暫緩顯示, 無線區段的封包遺失, 實體層錯誤(線材、光模組、接頭不良), MTU 黑洞(只有大封包反覆遺失), ACK 延遲或消失(上傳飽和)
有一段時間收到的量掉到 0,接著一次全部湧進來。
視窗最小化或非作用中時的處理限制, 基地台換手(移動中), 雲端主機維護與即時遷移, NIC 驅動程式/韌體問題, TCP HOL 阻塞, TCP RTO 與指數退避, 背景視窗的處理限制, thin stream 復原緩慢, Zero window(看起來像重傳的停頓)
連線數驟降,或斷線次數瞬間暴增。
手機 App 切到背景, Wi-Fi ↔ LTE/5G 切換, NAT mapping 過期, 電信業者共用 IP(CGNAT), 負載平衡器閒置逾時, 雲端安全群組的連線追蹤過期, 網路設備容錯移轉(failover), OOM killer, Windows UDP socket 的 WSAECONNRESET 錯誤, 死結, 伺服器當機, 無窮迴圈、邏輯失控, 寫入 core dump, DB 容錯移轉, 存檔週期過長造成的進度遺失, 附屬伺服器故障, 部署與重新啟動, TLS 憑證過期/設定錯誤, 連線途中 NAT 或負載平衡器的 mapping 過期
伺服器剛開放或活動剛開始時大幅衝高,然後慢慢回落。
主執行緒同步載入、著色器編譯, 連線等待佇列(backlog)溢位, 進入密集區域時的生成暴增, 冷快取(剛重新啟動時), 登入暴增與 N+1 查詢, 自動擴展延遲, 剛進場時湧入的出現資訊遺失, 連線請求(SYN)重傳
我們統計了每個原因最容易的確認方式。基礎設施工具指可以用 OS、網路、雲端、DB 工具與執行環境的啟動選項(GC log 等)確認,不必修改遊戲程式碼。遊戲 log 與指標指 tick 時間、斷線原因這類必須由遊戲記錄下來才看得到的東西。這一欄越多的層,越有理由請開發團隊加上量測。
平均值會掩蓋飆高。每秒 20 tick 的伺服器,即使只有 1% 的 tick 變慢,大約每 5 秒就會讓所有人頓一下,但平均 tick 時間幾乎不變。所以要一起看百分位數。p50(中位數)是一半樣本比它快的值,p99 是 100 次中最慢那 1 次附近的值。玩家記得的「lag」,通常是 p99 那一端。
彙總間隔也會掩蓋飆高。在 1 分鐘平均的圖表上,1 秒的停頓會被稀釋成 1/60。找停頓時,要一起看同一張圖表的最大值或 p99,或是更短的間隔。
抖動(jitter)是封包抵達間隔的變動程度。即使平均 ping 很低,抖動大時內插緩衝也會被耗空,出現卡頓、瞬移。
| 測量方法 | 測量什麼 | 注意事項 |
|---|---|---|
ping (ICMP) | 到設備的往返時間 | 路由器、伺服器可能延後處理 ICMP 回應或限制數量,結果可能與遊戲封包不同。被阻擋時完全沒有回應 |
mtr·traceroute | 各躍點的延遲與遺失 | 只有中間某台設備遺失率高、之後的躍點正常時,很可能只是那台設備減少了 ICMP 回應。只有一路延續到終點的遺失才是真正的遺失 |
TCP RTT(ss -ti 的 rtt) | kernel 對每條連線測得的往返時間 | 是實際遊戲連線的數值,所以最可信。可在伺服器端逐一查看每位玩家 |
| 遊戲內 ping | 遊戲用自己的訊息測得的往返時間 | 在遊戲迴圈內測量時,會混入畫格、tick 的等待時間。即使線路正常,伺服器或電腦忙碌時也會升高 |
ss -ti 或 eBPF 工具收集每條連線的 RTT 與重傳,依 ASN 分組查看。pidstat -t)、run queue 延遲、只需啟動選項就能開啟的 GC log。mtr。彙整了兩種常見情境的確認順序,以及原開發商、營運商親自公開的實際事故案例。每個步驟與案例都連到相關的原因卡片。
特定更新或部署之後,lag 回報增加時。適用於「這次更新後就怪怪的」這類回報集中出現,或圖表從某個時間點起階梯式上升並維持不降的情況。
開放新的服務國家,或新增區域、資料中心時。開服前的檢查,以及分辨「國內沒問題,只有新國家的玩家 lag」這類回報時,都可以使用。
只挑選遊戲公司與基礎設施公司自行公開的事後檢討(postmortem)。摘要只寫原文揭露的內容,詳細經過請看原文。
2014 年 1 月的一篇回顧文章談到 HED-GP 星系的大規模艦隊戰,當時伺服器嚴重過載。Time Dilation(過載時放慢遊戲時間的功能)降到下限 10%、整個戰場進入慢動作之後,負載仍持續堆積;處理模組停止與重複運作的工作積壓程度(Dogma Lateness)最高達到遊戲時間 193 秒,換算成實際時間約 32 分鐘。規模幾乎相同的 2013 年 7 月 6VDT 戰役,最高為 42 秒(實際約 7 分鐘)。 CCP 先聲明,效能分析工具本身會增加負載,這種情況下不會執行,所以無法確定原因,接著舉出兩個可能的主因。第一是戰鬥拖長,來不及處理的負載不斷累積。第二是無人機使用量增加:戰鬥期間部署的無人機數量(不重複計算)在 6VDT 為 21,123 架,在 HED-GP 為 38,852 架,多了 84%。一個人的行動必須通知所有看得到的人,這類傳送量會隨人數的平方(O(n²))增加,而無人機每次攻擊產生的訊息更多。無人機挑選攻擊目標的程式碼也常把同一戰場上所有可攻擊的目標全部掃過一遍,成本接近 n² 成長。
人潮集中的單一區域處理量超過上限時,整個區域都會變成慢動作;戰鬥越久,積壓的處理越多,輸入延遲也越大。確認訊號是負責該區域的伺服器(節點)的 tick 時間、積壓工作量,以及人數與物件數;其他區域都正常是這類問題的特徵。主要負責單位是遊戲開發團隊(伺服器),要修改的是一個行動需要通知的對象範圍,以及 AI 搜尋目標的成本。放慢遊戲時間的設計無法消除過載,但能讓所有人以相同速度變慢,避免只有部分行動無止盡地積壓。 原文
這是 Riot Games 說明網際網路為何不適合即時遊戲的技術文章。一位 League of Legends 玩家回報的實際流量本來應該從舊金山直接送到波特蘭,卻經過洛杉磯、丹佛、西雅圖,直接走只要 14ms 的路程花了 70ms。Riot 說明,路由器滿載而丟棄封包時,其他英雄會在畫面上亂跳,投射物看起來像瞬移一樣。 Riot 指出原因在路徑與路由器。比起延遲最短的路徑,骨幹業者與電信業者更傾向把流量送往成本最低的路徑;BGP 決定的路徑若繞遠路,經過的路由器也會變多。路由器的處理負擔取決於封包數量,與封包大小無關。遊戲封包約 55 位元組左右,同樣的資料量下,封包數是 1,500 位元組封包的 27 倍,填滿路由器輸入緩衝區的速度也快上同樣倍數。依 Riot 的說明,許多路由器在過載時會先丟棄 UDP 封包。Riot 的解決方案是建立自有網路 Riot Direct:在美國 10 個大型網際網路據點架設路由器,並與盡可能多的電信業者直接互連(peering)。根據系列第 2 篇,以 ping 不到 80ms 遊玩的玩家比例在 9 個多月內從 31% 升到 50%,遊戲伺服器搬到芝加哥後,一夜之間達到 80%。
即使在同一個國家,如果只有特定電信業者的用戶 ping 特別高,就要懷疑路徑。確認訊號是各電信業者(ASN)的 RTT 分布,以及 traceroute 顯示的途經城市。主要負責單位是基礎設施團隊(網路),修正方式是與電信業者直接 peering、連接 IX(網際網路交換中心),以及選擇伺服器位置。電信業者端的路由政策需要與外部(電信業者)協商。這個案例也顯示,光是把伺服器移到靠近玩家分布中心的位置,效果就很大。 原文
2020 年 2 月底,League of Legends 的 EUW、EUNE、BR 伺服器多次發生事故,新開始的對局數大幅減少。配對、遊戲伺服器等後端服務狀態全都正常,但幾乎沒有流量進來。Riot 為了避免在可能不穩定的叢集上開放錦標賽模式(Clash),把時程延後一週。事後檢討報告沒有寫出每次事故持續多久。 三件事疊在一起。送往某個服務的請求格式有誤,在特定情況下不斷失敗、不斷重試,請求量因此暴增。容器系統與 OS 版本之間有已知的相容性問題,造成 OS 內部記憶體洩漏,而升級只在 Riot 全部容器環境中約 60% 完成,歐洲與拉丁美洲叢集還在進行中。接收網際網路流量、過濾後轉送到後端的邊緣(edge)容器,在同一個 shard(伺服器群)內會分散部署,但不同 shard 之間沒有這種限制,所以每次事故時,至少有三個 shard 的邊緣容器集中在同一台主機上。暴增的重試壓在這台主機上,記憶體洩漏則讓這台主機停擺。
後端服務都回報「正常,但流量進不來」時,就要查它們前面那一層(邊緣、閘道、負載平衡器)。確認訊號是各主機傳入連線數是否偏向少數主機,以及特定請求的失敗與重試比率。主要負責單位是遊戲開發團隊(伺服器:錯誤的請求與重試方式),容器部署規則、OS 升級與連線集中警示則由基礎設施團隊(伺服器設備/OS)負責。Riot 修正了請求程式碼,讓重試不再暴增,並在實作 shard 之間的分散部署之前,先加上連線集中警示。 原文
2021 年 1 月 22 日,League of Legends EUW 伺服器有 5 個多小時無法正常運作。登入玩家數與遊戲中玩家數兩項指標同時中斷,兩次重新啟動之間,登入人數雖然增加,對局卻幾乎沒有開始。 負責非關鍵功能的 DB 主伺服器發生硬體故障,而這個 DB 沒有設定自動容錯移轉到備用伺服器。各 DB 的連線池雖然分開,所有連線池卻共用同一個執行緒池;送往故障 DB 的工作一直沒有結束、持續佔用執行緒,整個系統可用的執行緒因此耗盡。大量警示湧入時,團隊先懷疑最近遭遇的惡意網路攻擊與其他地區的硬體作業,故障 DB 的警示約 1 小時後才被注意到。所有系統都在同一個 JVM 中執行,重新啟動後在重新連線的負載下,GC 每次讓處理程序暫停幾秒,指標收集也出現很大的空白。登入排隊也沒有遵守設定的上限,湧入的玩家忽多忽少。
一個被認為不重要的附屬 DB,也可能透過執行緒池這類共用資源讓整體停擺。確認訊號是各 DB 等待中的請求數、執行緒池使用率,以及對局開始數相對於登入數過低的比例。負責單位是遊戲開發團隊(伺服器:執行緒池隔離、逾時)與基礎設施團隊(DB:自動容錯移轉)。大量警示湧入時,很容易先懷疑最近遇過的問題(例如攻擊),所以要依判定順序(範圍 → 時間點 → 層級)逐一排除。重新啟動後,也要一併確認登入排隊是否依設定限制湧入量。 原文
事故始於 2021 年 10 月 28 日下午(太平洋時間)一台 Consul 伺服器的 CPU 高負載;16:35 在線玩家數掉到平時的一半,接著整個服務停擺。直到 10 月 31 日 16:45,所有玩家才能重新進入,距事故開始已過了 73 小時。Roblox 表示每天有 5 千萬人使用。 Roblox 用 HashiCorp Consul 處理服務探索(服務之間互相找到對方位址的功能)、健康檢查與 KV 儲存,而一個 Consul 叢集同時承擔多種工作負載。根本原因有兩個。第一,Consul 新的 streaming 功能在幾個月間逐步擴大使用範圍,事故前一天也在流量路由服務上啟用,並把該服務的節點數增加 50%;在讀寫都非常大量的負載下,這項功能在單一共用資源(Go channel)上造成競爭。事故期間換上了核心數更多的雙插槽(NUMA)伺服器,在這些伺服器上競爭更嚴重。第二,Consul 用來儲存 Raft log 的 BoltDB,其空閒頁面清單(freelist)管理變得異常緩慢,每追加 16kB 以下的資料就要寫入 7.8MB 到磁碟。平時不到 300ms 的 KV 寫入延遲中位數升到 2 秒,在緩慢的 leader 伺服器上還觀察到 TCP 緩衝區塞滿的 zero window。遙測(telemetry)依賴 Consul,找原因所需的指標也跟著消失。
許多服務共同依賴的基礎系統(服務探索、設定儲存、驗證)一旦變慢,所有功能會同時停擺。確認訊號是該系統的寫入延遲、leader 切換與 CPU,以及事故前剛做的設定變更。負責單位是遊戲開發團隊(伺服器)與基礎設施團隊(伺服器設備/OS)雙方。監控必須與受監控的系統分開、不依賴它,事故期間才看得到指標。復原時快取是空的,一次全部放人進來可能再次崩潰,所以 Roblox 透過 DNS 調整可進入的玩家比例,每次增加約 10%。 原文
2021 年 12 月,從資料片 Endwalker 的搶先體驗開始,各 World 都極度壅塞。登入排隊變長,在角色選擇畫面登入時或排隊等候中,經常出現 Error 2002。部分 World 與 zone 也曾當機(Error 3001),也出現過排隊逾時(Error 4004)。到 12 月 11 日公告時,搶先體驗進入第 8 天,壅塞仍在持續。 Error 2002 會在兩種情況下發生。第一種是每個邏輯資料中心的排隊人數超過 17,000 人時;這是為了避免排隊過長導致登入伺服器當機而設的上限,此時用戶端會完全關閉。12 月 7 日把開發用的備用設備投入大廳伺服器、提高上限後,這個錯誤減少了,排隊卻反而變得更長。第二種是排隊中玩家的線路不穩定時。等待時間拉長後,網際網路路徑上的封包遺失或 Wi-Fi 不穩造成短暫斷線的情況變多。大廳伺服器會等待重新連線數十秒到 1 分鐘左右,在這段時間內連回就能從排隊中原本的位置接續,超過時間就得排到最後面。Square Enix 表示大部分回報都屬於這種情況。由於半導體短缺,也無法立即增加 World。
排隊越長,排隊中玩家短暫的線路中斷就越容易變成連線錯誤。即使是同樣的壅塞,錯誤也會集中在使用 Wi-Fi 或不穩定線路的人身上,成為「只有部分人」遇到的問題。確認訊號是排隊長度、等待時間,以及斷線原因中排隊時斷線的比例。主要負責單位是遊戲開發團隊(伺服器:排隊上限與重新連線寬限時間),大廳與 World 伺服器擴充則由基礎設施團隊協同處理。重新連線寬限時間留得充裕,就能減少玩家線路短暫中斷演變成失去排隊順位的情況。 原文
許多遊戲把網站、API 與 DDoS 防護交給 CDN 業者,因此這是遊戲也會一起受影響的基礎設施事故類型。2020 年 7 月 17 日 21:12 到 21:39(UTC)的 27 分鐘內,Cloudflare 整個網路的流量減少約 50%。影響僅限於連接骨幹的美國、歐洲、俄羅斯、巴西部分城市據點,其他據點正常。 紐華克–芝加哥骨幹區段故障,使亞特蘭大–華盛頓區段壅塞,工程師為了減輕亞特蘭大的骨幹流量而修改路由器設定。原本應該停用整個政策項目(term),卻只停用了其中的條件(prefix-list),結果亞特蘭大路由器以更高的優先順序(local-preference 200)把所有 BGP 路由散布到整個骨幹。各據點為通往自家伺服器的路由所設定的優先順序是 100,因此連接骨幹的據點流量全都湧向亞特蘭大。亞特蘭大過載,受影響的據點則幾乎沒有流量可處理。把亞特蘭大路由器從骨幹移除後即恢復。Cloudflare 表示這與攻擊或入侵無關。
如果只有特定城市或地區的玩家同時遇到斷線或連不上/無限讀取,其他人都正常,就先懷疑剛做過的路由設定變更。圖表上只有一個據點的 CPU 與流量往上衝,受影響的據點反而掉到接近 0。主要負責單位是基礎設施團隊(網路),若是業者端的故障則屬於外部。Cloudflare 決定為骨幹 BGP session 設定可接收的路由數上限(maximum-prefix),並調整優先順序,讓單一據點無法把其他據點的流量拉走。 原文
許多遊戲透過 CDN 發送更新檔、啟動器與網頁,因此這是遊戲也會一起受影響的基礎設施事故類型。2021 年 6 月 8 日 09:47(UTC)起,Fastly 網路有 85% 回傳錯誤。49 分鐘內網路的 95% 恢復正常,12:35 事故平息。 5 月 12 日開始的軟體部署中,含有一個特定客戶設定遇到特定條件時就會觸發的 bug。6 月 8 日,一位客戶推送了正常的設定變更,剛好符合這個條件。Fastly 在 1 分鐘內偵測到異常,找出並停用造成問題的客戶設定後開始恢復。修正 bug 的部署在同一天 17:25 開始。
已部署好幾週的程式碼,遇到罕見條件時也可能瞬間變成全球事故。遊戲端的確認訊號是更新、啟動器、網頁請求的 HTTP 錯誤率在所有地區同時升高,以及 CDN 業者的狀態頁面。已建立的遊戲連線若不經過 CDN 就不受影響,只有新連線、更新下載與網頁登入受阻,這是此類事故的特徵。主要負責單位是外部(CDN 業者),遊戲開發團隊與基礎設施團隊要預先準備替代路徑,例如同時使用兩家以上的 CDN,或直接從來源伺服器(origin)下載。 原文
這是一起同樣適用於遊戲公司自有網路與 DNS 的基礎設施事故。2021 年 10 月 4 日,Facebook(現為 Meta)的服務在全球都無法連線。連接各資料中心的骨幹全部中斷,網際網路端也找不到 Facebook 的 DNS 伺服器。事後檢討報告沒有寫出事故持續了多久。 例行維護作業中,為了確認全球骨幹容量而下的指令,意外切斷了骨幹的所有連線,而原本應該擋下這類指令的稽核工具因為 bug 沒有擋住。小型據點的 DNS 伺服器設計成無法與資料中心通訊時,會判定自己異常並撤回 BGP 宣告,所以 DNS 伺服器明明還在運作,網際網路上卻連不到。平常的連線路徑與頻外(out-of-band)管理連線都中斷,內部工具也失去 DNS,只好派工程師親自到資料中心,又因為安全程序多花了時間。復原時,各資料中心的用電量都少了數十 MW,研判一次全部恢復可能讓電力設備到快取都陷入風險,因此分階段提高負載。
所有地區、所有電信業者同時出現連不上/無限讀取時,要先查 DNS 與 BGP 路由,再看遊戲伺服器。透過外部 DNS 查詢與公開的 BGP 路由資訊,在公司外部也能確認。主要負責單位是基礎設施團隊(網路)。要事先檢查事故時使用的頻外連線路徑與內部工具是否依賴同一套 DNS 與網路;復原時分階段提高負載,避免重新連線一次湧入。 原文
許多遊戲把伺服器、登入與資料放在公有雲上,因此這是遊戲也會一起受影響的基礎設施事故類型。2021 年 12 月 7 日上午 7:30(太平洋標準時間),北維吉尼亞區域(us-east-1)的內部網路發生壅塞。上午 7:33 起 EC2 API 錯誤與延遲增加,難以啟動新的執行個體(執行個體啟動於下午 2:40 恢復),接著出現主控台登入失敗、無法變更 Route 53 設定、CloudWatch 指標延遲與部分遺失。網路設備在下午 2:22 完全恢復。已在執行的 EC2 執行個體與既有的 DNS 回應不受影響。 一項為主網路中某個服務擴充容量的自動作業,在內部網路的大量用戶端上引發非預期的行為,連線嘗試因此暴增。連接內部網路與主網路的設備超出負荷,通訊出現延遲,延遲又讓連線嘗試與重試增加,壅塞因此持續。用戶端本來具有在這種壅塞時拉長請求間隔的退避(backoff)機制,卻因為潛在缺陷而沒有正常運作。內部監控也依賴同一個網路,維運團隊只能在沒有即時指標的情況下靠 log 應變。
重試無法拉長間隔時,短暫的壅塞就會變成長達幾小時的事故。從遊戲端來看,已在執行的遊戲伺服器可能正常,但新伺服器擴充(自動擴展)、使用雲端 API 的登入、配對、付款,以及監控,可能會同時受阻。確認訊號是雲端供應商的狀態頁面、雲端 API 錯誤率與執行個體啟動失敗。主要負責單位是外部(雲端供應商);遊戲開發團隊要為所有重試加上隨機間隔的指數退避與次數限制,基礎設施團隊則要準備好即使無法擴充也撐得住的餘裕容量,以及其他區域的備案。 原文
這是玩家自己在裝置或分享器上設定使用的公用 DNS 解析器(resolver)發生的事故。這類事故的特點是只有使用這項設定的玩家會遇到,而且這些玩家的所有遊戲與服務會同時受阻。2025 年 7 月 14 日 21:52 到 22:54(UTC)的 62 分鐘內,1.1.1.1 解析器在全球都沒有回應。Cloudflare 表示,對許多使用者來說,這等於幾乎所有網際網路服務都無法使用。UDP、TCP、DNS over TLS 查詢受到影響,以網域名稱連線的 DNS over HTTPS 則相對穩定。 6 月 6 日為日後要用的另一個服務準備服務拓撲(決定在哪些據點宣告哪些 IP 網段的組態)時,1.1.1.1 解析器的 IP 網段被誤綁進這份組態。7 月 14 日變更這個服務的組態後,宣告解析器網段的據點從全部據點縮減為一個離線據點,全球的 BGP 路由因此撤回。這項變更沒有經過金絲雀(canary)部署,直接擴散到所有資料中心。22:20 還原設定後,流量恢復到約 77%,但這段期間約 23% 的邊緣伺服器上必要的 IP 設定已被刪除,重新設定又花了時間,直到 22:54 才恢復正常。Cloudflare 表示這是與攻擊或 BGP 劫持無關的內部設定錯誤。
遊戲伺服器與其他玩家都正常,只有部分玩家在登入、更新伺服器上出現連不上/無限讀取時,要懷疑這些玩家使用的 DNS。已連線的 session 維持不變、只有新連線失敗,是這類問題的特徵。請玩家更換 DNS 設定,或直接查詢伺服器位址試試,馬上就能分辨。主要負責單位是外部(DNS 營運者、電信業者);遊戲開發團隊(用戶端)若把名稱解析失敗與其他錯誤分開提示,客服就能立刻判定。 原文
許多遊戲把伺服器、登入與資料放在公有雲上,因此這是遊戲也會一起受影響的基礎設施事故類型。從 2025 年 10 月 19 日晚上 11:48 到 20 日下午 2:20(太平洋夏令時間),北維吉尼亞區域分三個階段持續受到影響。到 20 日凌晨 2:40 為止 DynamoDB API 錯誤增加;凌晨 2:25 到上午 10:36 新的 EC2 執行個體啟動失敗(部分新執行個體的連線問題於下午 1:50 解除);上午 5:30 到下午 2:09 部分 Network Load Balancer(NLB)的連線錯誤增加。 管理 DynamoDB DNS 的自動化系統中存在潛在的競爭條件(race condition)。在不同可用區域套用 DNS 計畫的執行器(DNS Enactor)中,有一個延遲得特別嚴重,它用舊計畫覆蓋了新計畫;緊接著,另一個執行器的清理作業刪除了這份舊計畫,區域端點(dynamodb.us-east-1.amazonaws.com)的 DNS 記錄因此變成空值。自動化無法修復這個狀況,只能由人工復原。EC2 的實體伺服器管理系統依賴 DynamoDB,這段期間每台實體伺服器維持的租約(lease)都過期了。DynamoDB 恢復後,實體伺服器數量太多,重新建立租約的作業還沒完成就逾時,重試的工作又再次堆積,陷入「壅塞崩潰(congestive collapse)」狀態。新啟動執行個體的網路設定傳播得慢,NLB 健康檢查在成功與失敗之間反覆,連正常節點也一再從 DNS 移除又加回。
一處的 DNS 記錄錯誤會擴散到依賴該服務的其他服務,原因排除後,積壓的工作與反覆波動的健康檢查仍會讓復原多拖好幾個小時。從遊戲端來看,已在執行的伺服器或許撐得住,但無法啟動新伺服器,自動擴展會停擺;健康檢查不穩時,負載平衡器還可能把正常的伺服器移出。確認訊號是雲端狀態頁面、受管服務的 API 錯誤率、執行個體啟動失敗,以及負載平衡器的健康目標數。主要負責單位是外部(雲端供應商),基礎設施團隊要限制因健康檢查失敗而一次被移出的伺服器數量,並準備其他區域的備案。 原文
遊戲開發團隊與基礎設施團隊在找原因時,最花時間的就是查出「何時、何地、誰」。填好下面的項目,就能直接在 log 與圖表中找到那個時刻。
這些是和遊戲開發團隊、基礎設施團隊溝通時常出現的詞。可以在搜尋框輸入中文或英文試試。
這是本白皮書中數值、預設值、行為說明的依據。只收錄標準文件(RFC),kernel 與 OS 文件,雲端、引擎、DB 官方文件,演講與論文等可信的資料。原因卡片與各章末尾的「出處」也連到同樣的資料。版本改變時預設值也可能改變,實際套用前請確認所用版本的文件。
資料 616 筆,發行者 83 家。清單請見純文字版的參考文獻。