<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Token-Bucket on Tarragon</title><link>https://tarrragon.github.io/blog/tags/token-bucket/</link><description>Recent content in Token-Bucket on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Sat, 20 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/token-bucket/index.xml" rel="self" type="application/rss+xml"/><item><title>Rate Limiting</title><link>https://tarrragon.github.io/blog/devops/03-traffic-management/rate-limiting/</link><pubDate>Sat, 20 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/devops/03-traffic-management/rate-limiting/</guid><description>&lt;p>Rate limiting 是主動的流量控制 — 在系統還沒過載之前，就限制每個來源的請求速率。和背壓不同，rate limit 的觸發依據是預設的速率上限，而非實際的系統負載。&lt;/p>
&lt;h2 id="兩個粒度">兩個粒度&lt;/h2>
&lt;h3 id="per-client每來源限速">Per-client（每來源限速）&lt;/h3>
&lt;p>限制每個 client（by API key / IP / SDK instance）的請求速率。防止單一來源打爆系統。&lt;/p>
&lt;p>自用場景下 per-client 限速的價值不高（只有自己的 SDK），但開源工具被多人部署後，per-client 限速防止某個失控的 SDK 影響其他來源。&lt;/p>
&lt;h3 id="global全局限速">Global（全局限速）&lt;/h3>
&lt;p>限制系統的總吞吐量。不管多少個 client，collector 每秒最多處理 N 個事件。&lt;/p>
&lt;p>Global 限速是系統保護的最後一道線 — 即使每個 client 都在限速內，所有 client 加起來可能超過系統承載。Global 限速確保總量不超過系統能力。&lt;/p>
&lt;h2 id="演算法">演算法&lt;/h2>
&lt;h3 id="token-bucket">Token Bucket&lt;/h3>
&lt;p>桶裡有固定數量的 token，每個請求消耗一個 token，token 按固定速率補充。桶空了就拒絕。&lt;/p>
&lt;p>特點：允許短暫 burst（桶滿時一次消耗多個 token），但長期平均不超過補充速率。適合「允許偶爾的高峰但長期平均要在限制內」的場景。&lt;/p>
&lt;h3 id="sliding-window">Sliding Window&lt;/h3>
&lt;p>在固定的時間窗口（如 1 分鐘）內計數請求。超過上限就拒絕。窗口結束時計數重設。&lt;/p>
&lt;p>特點：嚴格的速率限制（窗口內不會超過 N 個），但窗口邊界有突增風險（上一個窗口末尾 + 下一個窗口開頭各 N 個 = 瞬間 2N）。滑動窗口（sliding window log / counter）解決邊界問題但記憶體較高。&lt;/p>
&lt;h3 id="選擇">選擇&lt;/h3>
&lt;p>自架監控系統推薦 token bucket — 允許 SDK 的 flush burst（一次送 100 個事件是正常行為），但限制長期平均速率。&lt;/p>
&lt;h2 id="http-429--retry-after">HTTP 429 + Retry-After&lt;/h2>
&lt;p>限速觸發時回 HTTP 429 Too Many Requests，帶 &lt;code>Retry-After&lt;/code> header 和 rate limit 相關 header：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">HTTP/1.1 429 Too Many Requests
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">Retry-After: 5
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">X-RateLimit-Limit: 1000
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">X-RateLimit-Remaining: 0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">X-RateLimit-Reset: 1719302400&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>SDK 收到 429 後觸發離線 buffer 暫存事件，&lt;code>Retry-After&lt;/code> 秒後重試。&lt;/p>
&lt;h2 id="優先級豁免">優先級豁免&lt;/h2>
&lt;p>某些請求不應被限速：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>請求類型&lt;/th>
 &lt;th>限速？&lt;/th>
 &lt;th>理由&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Health check&lt;/td>
 &lt;td>不限&lt;/td>
 &lt;td>探活請求被限速等於 LB 誤判服務掛了&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Error 事件&lt;/td>
 &lt;td>不限或較寬&lt;/td>
 &lt;td>Debug 價值最高、丟了就查不到&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Event 事件&lt;/td>
 &lt;td>限速&lt;/td>
 &lt;td>量大、行為分析可以接受取樣&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Metric 事件&lt;/td>
 &lt;td>限速&lt;/td>
 &lt;td>高頻取樣可以降頻&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>優先級的判斷依據是「這個事件丟了的代價」。Error 事件丟了影響 debug 能力，event 事件丟了影響行為分析精度 — 前者的代價更高。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;ul>
&lt;li>被動的流量控制 → &lt;a href="https://tarrragon.github.io/blog/devops/03-traffic-management/backpressure/" data-link-title="背壓機制" data-link-desc="下游處理慢時上游怎麼減速 — 有限 buffer &amp;#43; 回壓訊號的設計、和 rate limit 的區別">背壓機制&lt;/a>&lt;/li>
&lt;li>依賴失敗時的快速失敗 → &lt;a href="https://tarrragon.github.io/blog/devops/03-traffic-management/circuit-breaker/" data-link-title="熔斷器" data-link-desc="依賴服務失敗時怎麼快速失敗而非拖慢自己 — 三狀態模型（closed → open → half-open）和熔斷判斷條件">熔斷器&lt;/a>&lt;/li>
&lt;li>不同工作負載的資源隔離 → &lt;a href="https://tarrragon.github.io/blog/devops/03-traffic-management/bulkhead/" data-link-title="Bulkhead 隔離" data-link-desc="不同工作負載的資源池隔離 — 一個功能過載不拖垮其他功能的隔艙設計">Bulkhead 隔離&lt;/a>&lt;/li>
&lt;li>Backend 的 rate limit 實作（middleware / Redis / 配額設計）→ &lt;a href="https://tarrragon.github.io/blog/backend/09-performance-capacity/rate-limit-implementation/" data-link-title="Rate Limit 實作" data-link-desc="單機 middleware / Redis 分散式限速 / 配額設計 — 概念見 DevOps 流量管控，本章聚焦後端實作">Rate Limit 實作&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>Rate limiting 是主動的流量控制 — 在系統還沒過載之前，就限制每個來源的請求速率。和背壓不同，rate limit 的觸發依據是預設的速率上限，而非實際的系統負載。</p>
<h2 id="兩個粒度">兩個粒度</h2>
<h3 id="per-client每來源限速">Per-client（每來源限速）</h3>
<p>限制每個 client（by API key / IP / SDK instance）的請求速率。防止單一來源打爆系統。</p>
<p>自用場景下 per-client 限速的價值不高（只有自己的 SDK），但開源工具被多人部署後，per-client 限速防止某個失控的 SDK 影響其他來源。</p>
<h3 id="global全局限速">Global（全局限速）</h3>
<p>限制系統的總吞吐量。不管多少個 client，collector 每秒最多處理 N 個事件。</p>
<p>Global 限速是系統保護的最後一道線 — 即使每個 client 都在限速內，所有 client 加起來可能超過系統承載。Global 限速確保總量不超過系統能力。</p>
<h2 id="演算法">演算法</h2>
<h3 id="token-bucket">Token Bucket</h3>
<p>桶裡有固定數量的 token，每個請求消耗一個 token，token 按固定速率補充。桶空了就拒絕。</p>
<p>特點：允許短暫 burst（桶滿時一次消耗多個 token），但長期平均不超過補充速率。適合「允許偶爾的高峰但長期平均要在限制內」的場景。</p>
<h3 id="sliding-window">Sliding Window</h3>
<p>在固定的時間窗口（如 1 分鐘）內計數請求。超過上限就拒絕。窗口結束時計數重設。</p>
<p>特點：嚴格的速率限制（窗口內不會超過 N 個），但窗口邊界有突增風險（上一個窗口末尾 + 下一個窗口開頭各 N 個 = 瞬間 2N）。滑動窗口（sliding window log / counter）解決邊界問題但記憶體較高。</p>
<h3 id="選擇">選擇</h3>
<p>自架監控系統推薦 token bucket — 允許 SDK 的 flush burst（一次送 100 個事件是正常行為），但限制長期平均速率。</p>
<h2 id="http-429--retry-after">HTTP 429 + Retry-After</h2>
<p>限速觸發時回 HTTP 429 Too Many Requests，帶 <code>Retry-After</code> header 和 rate limit 相關 header：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">HTTP/1.1 429 Too Many Requests
</span></span><span class="line"><span class="ln">2</span><span class="cl">Retry-After: 5
</span></span><span class="line"><span class="ln">3</span><span class="cl">X-RateLimit-Limit: 1000
</span></span><span class="line"><span class="ln">4</span><span class="cl">X-RateLimit-Remaining: 0
</span></span><span class="line"><span class="ln">5</span><span class="cl">X-RateLimit-Reset: 1719302400</span></span></code></pre></div><p>SDK 收到 429 後觸發離線 buffer 暫存事件，<code>Retry-After</code> 秒後重試。</p>
<h2 id="優先級豁免">優先級豁免</h2>
<p>某些請求不應被限速：</p>
<table>
  <thead>
      <tr>
          <th>請求類型</th>
          <th>限速？</th>
          <th>理由</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Health check</td>
          <td>不限</td>
          <td>探活請求被限速等於 LB 誤判服務掛了</td>
      </tr>
      <tr>
          <td>Error 事件</td>
          <td>不限或較寬</td>
          <td>Debug 價值最高、丟了就查不到</td>
      </tr>
      <tr>
          <td>Event 事件</td>
          <td>限速</td>
          <td>量大、行為分析可以接受取樣</td>
      </tr>
      <tr>
          <td>Metric 事件</td>
          <td>限速</td>
          <td>高頻取樣可以降頻</td>
      </tr>
  </tbody>
</table>
<p>優先級的判斷依據是「這個事件丟了的代價」。Error 事件丟了影響 debug 能力，event 事件丟了影響行為分析精度 — 前者的代價更高。</p>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>被動的流量控制 → <a href="/blog/devops/03-traffic-management/backpressure/" data-link-title="背壓機制" data-link-desc="下游處理慢時上游怎麼減速 — 有限 buffer &#43; 回壓訊號的設計、和 rate limit 的區別">背壓機制</a></li>
<li>依賴失敗時的快速失敗 → <a href="/blog/devops/03-traffic-management/circuit-breaker/" data-link-title="熔斷器" data-link-desc="依賴服務失敗時怎麼快速失敗而非拖慢自己 — 三狀態模型（closed → open → half-open）和熔斷判斷條件">熔斷器</a></li>
<li>不同工作負載的資源隔離 → <a href="/blog/devops/03-traffic-management/bulkhead/" data-link-title="Bulkhead 隔離" data-link-desc="不同工作負載的資源池隔離 — 一個功能過載不拖垮其他功能的隔艙設計">Bulkhead 隔離</a></li>
<li>Backend 的 rate limit 實作（middleware / Redis / 配額設計）→ <a href="/blog/backend/09-performance-capacity/rate-limit-implementation/" data-link-title="Rate Limit 實作" data-link-desc="單機 middleware / Redis 分散式限速 / 配額設計 — 概念見 DevOps 流量管控，本章聚焦後端實作">Rate Limit 實作</a></li>
</ul>
]]></content:encoded></item></channel></rss>