Skip to content
All writing Part 09 of 11 · 從 19 個 goroutine 到 exit 137
Engineering · 1 min read

用 TryAcquire 做 load shedding:快速拒絕勝過緩慢排隊

Acquire 排隊只是把 OOM 搬到等待區。TryAcquire + HTTP 429 才是真正的 load shedding。佔住資源本身就是問題時,快速拒絕勝過排隊。

Acquire 排隊只是把 OOM 搬到等待區。TryAcquire + 429 才是真正的 load shedding。

Acquire vs TryAcquire

request arrives slots.TryAcquire(1) true false do the work release(1) return 429 (load-shed) caller retries new conn + jitter (L4 re-pick)
  • Acquire(blocking):超限的 caller 排進 FIFO queue。每個 waiter 佔一個 goroutine + 一條連線 + 記憶體,無上限的 queue 只是把 OOM 搬到了等待區。
  • TryAcquire(non-blocking):立刻 falseHTTP 429。這是 load shedding,快速拒絕,讓 caller 重試到別台(keep-alive 讓重試卡在同一台)。
  • 選 shedding:當佔住資源本身就是問題(如瀏覽器分頁)。選 bounded queue:當短暫突增可以吸收。

semaphore.Weighted 內部

想像一個停車場,6 個車位(size: 6),目前停了 4 台(cur: 4):

TryAcquire(不等):看一眼,有空位就停進去,沒有就掉頭走。永遠不排隊。

Acquire(願意等):沒空位就排隊等,等到有人開走才進去。排太久可以透過 context 放棄。

Release:開走,空出位子。有人在排隊的話,按順序讓下一台進來。

TryAcquire(1):             Acquire(ctx, 1):
  lock                       lock
  剩餘 >= 1 且沒人排隊?       剩餘 < 1?
    → cur++, return true       → 排進 waiters, 等 channel
    (永遠不 block)             (block 直到有人 Release)

Release(1):
  cur-- → 按排隊順序喚醒下一個 waiter
  • 這裡 n=1(一次拿一個位子),size=6 → 同時最多 6 個截圖。
  • TryAcquire 回 false 時,handler 直接回 429——不排隊、不佔 goroutine、不佔記憶體。
  • waiters.Len()==0 這個條件是為了公平:有人在排隊時,新來的不能插隊。

在這個 OOM 裡的角色

截圖服務的 /captureslots.TryAcquire(1) 限制同時截圖數 = 6。第 7 個以上的請求立刻拿到 429,不會再開瀏覽器分頁,瀏覽器 sidecar 的記憶體就被 bound 住了(共享資源的影響範圍)。

快速的 429 比緩慢的 500 便宜。shed 掉的請求還有機會;排隊排到 OOM 的請求一個都沒救。

參考來源:

Related: 參見共享資源的影響範圍keep-alive 讓重試卡在同一台GOMAXPROCS 限制平行不限並發,或回到系列總覽

Tags #go #concurrency #reliability
// connect

Be brave | Be wise | Be grateful

21 BreakinCode

// elsewhere
LinkedInMedium (lang: en)Youtube
wh:~$William Hung· © 2026 Taipei · GMT+8 · Available for collaboration