<?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>Signal on Tarragon</title><link>https://tarrragon.github.io/blog/tags/signal/</link><description>Recent content in Signal on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Fri, 19 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/signal/index.xml" rel="self" type="application/rss+xml"/><item><title>Go 平台適配</title><link>https://tarrragon.github.io/blog/monitoring/05-platform-adaptation/go-platform/</link><pubDate>Fri, 19 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/monitoring/05-platform-adaptation/go-platform/</guid><description>&lt;p>Go 的 monitoring SDK 和其他平台 SDK 的定位不同。JS / Flutter / Python SDK 是 client-side 的事件上報工具，Go SDK 更常用在 server-side — 包括 collector 本身的自身監控。Go 的 goroutine 並行模型、signal handling 機制和 HTTP server 的 graceful shutdown 是 Go 環境中的三個核心適配問題。&lt;/p>
&lt;h2 id="graceful-shutdown">Graceful shutdown&lt;/h2>
&lt;p>Go 程式收到 SIGTERM 或 SIGINT 時需要在退出前完成清理：flush 剩餘的 buffer、關閉網路連線、寫入最後的 lifecycle 事件。&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-go" data-lang="go">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="nx">ctx&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">stop&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">signal&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">NotifyContext&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">context&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Background&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="nx">syscall&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">SIGTERM&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">syscall&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">SIGINT&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="k">defer&lt;/span> &lt;span class="nf">stop&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="o">&amp;lt;-&lt;/span>&lt;span class="nx">ctx&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Done&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="c1">// signal received, start graceful shutdown&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">&lt;span class="nx">monitor&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Close&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">context&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">WithTimeout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">context&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Background&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="mi">5&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="nx">time&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Second&lt;/span>&lt;span class="p">))&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>graceful shutdown 的時間窗口由部署環境決定。Kubernetes 的預設 terminationGracePeriodSeconds 是 30 秒，Docker 的 stop timeout 是 10 秒。SDK 的 Close 方法接受 context 讓呼叫端控制超時。&lt;/p>
&lt;h3 id="http-server-的-shutdown-順序">HTTP server 的 shutdown 順序&lt;/h3>
&lt;p>如果 Go 程式同時是 HTTP server 和 monitoring SDK 的使用者，shutdown 順序需要正確：&lt;/p>
&lt;ol>
&lt;li>停止接受新連線（&lt;code>server.Shutdown(ctx)&lt;/code>）&lt;/li>
&lt;li>等待進行中的請求完成&lt;/li>
&lt;li>flush 監控 buffer（&lt;code>monitor.Close(ctx)&lt;/code>）&lt;/li>
&lt;li>關閉 log 和其他資源&lt;/li>
&lt;/ol>
&lt;p>如果先 close monitor 再 shutdown server，進行中的請求產生的事件會在 monitor 已關閉後嘗試送出，被靜默丟棄。&lt;/p>
&lt;h2 id="signal-handling">Signal handling&lt;/h2>
&lt;p>Go 的 &lt;code>signal.Notify&lt;/code> 和 &lt;code>signal.NotifyContext&lt;/code> 是接收 OS signal 的標準方式。SDK 在 init 時不應該自己註冊 signal handler — 這會和應用程式的 signal handling 衝突（Go 的 signal handler 是先到先得，後註冊的覆蓋先註冊的）。&lt;/p>
&lt;p>SDK 端的適配方式是提供 &lt;code>Close&lt;/code> 方法讓應用程式在自己的 signal handler 中呼叫，而非 SDK 內部攔截 signal。應用程式控制 shutdown 流程，SDK 只負責在被告知關閉時 flush 和清理。&lt;/p>
&lt;h3 id="panic-recovery">panic recovery&lt;/h3>
&lt;p>Go 的 panic 會終止當前 goroutine。如果 panic 發生在 main goroutine 且沒有 recover，程式直接退出，SDK 的 buffer 中的事件遺失。&lt;/p>
&lt;p>SDK 可以提供 &lt;code>monitor.RecoverAndReport()&lt;/code> 讓開發者在 goroutine 的入口用 &lt;code>defer monitor.RecoverAndReport()&lt;/code> 攔截 panic，記錄 error 事件後再 re-panic（保持原有的 crash 行為）。&lt;/p>
&lt;p>HTTP handler 的 panic 可以用 middleware 攔截：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-go" data-lang="go">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="kd">func&lt;/span> &lt;span class="nf">monitorMiddleware&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">next&lt;/span> &lt;span class="nx">http&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Handler&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="nx">http&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Handler&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">http&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">HandlerFunc&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kd">func&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">w&lt;/span> &lt;span class="nx">http&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">ResponseWriter&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">r&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="nx">http&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Request&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl"> &lt;span class="k">defer&lt;/span> &lt;span class="nx">monitor&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">RecoverAndReport&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl"> &lt;span class="nx">next&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">ServeHTTP&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">w&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">r&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl"> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">&lt;span class="p">}&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="http-server-自身監控">HTTP server 自身監控&lt;/h2>
&lt;p>Go 常用來寫 collector 本身。Collector 需要監控自己的健康狀態 — 請求處理速率、錯誤率、goroutine 數量、記憶體使用量。&lt;/p></description><content:encoded><![CDATA[<p>Go 的 monitoring SDK 和其他平台 SDK 的定位不同。JS / Flutter / Python SDK 是 client-side 的事件上報工具，Go SDK 更常用在 server-side — 包括 collector 本身的自身監控。Go 的 goroutine 並行模型、signal handling 機制和 HTTP server 的 graceful shutdown 是 Go 環境中的三個核心適配問題。</p>
<h2 id="graceful-shutdown">Graceful shutdown</h2>
<p>Go 程式收到 SIGTERM 或 SIGINT 時需要在退出前完成清理：flush 剩餘的 buffer、關閉網路連線、寫入最後的 lifecycle 事件。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="ln">1</span><span class="cl"><span class="nx">ctx</span><span class="p">,</span> <span class="nx">stop</span> <span class="o">:=</span> <span class="nx">signal</span><span class="p">.</span><span class="nf">NotifyContext</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nf">Background</span><span class="p">(),</span> <span class="nx">syscall</span><span class="p">.</span><span class="nx">SIGTERM</span><span class="p">,</span> <span class="nx">syscall</span><span class="p">.</span><span class="nx">SIGINT</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="k">defer</span> <span class="nf">stop</span><span class="p">()</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="o">&lt;-</span><span class="nx">ctx</span><span class="p">.</span><span class="nf">Done</span><span class="p">()</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c1">// signal received, start graceful shutdown</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="nx">monitor</span><span class="p">.</span><span class="nf">Close</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nf">WithTimeout</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nf">Background</span><span class="p">(),</span> <span class="mi">5</span><span class="o">*</span><span class="nx">time</span><span class="p">.</span><span class="nx">Second</span><span class="p">))</span></span></span></code></pre></div><p>graceful shutdown 的時間窗口由部署環境決定。Kubernetes 的預設 terminationGracePeriodSeconds 是 30 秒，Docker 的 stop timeout 是 10 秒。SDK 的 Close 方法接受 context 讓呼叫端控制超時。</p>
<h3 id="http-server-的-shutdown-順序">HTTP server 的 shutdown 順序</h3>
<p>如果 Go 程式同時是 HTTP server 和 monitoring SDK 的使用者，shutdown 順序需要正確：</p>
<ol>
<li>停止接受新連線（<code>server.Shutdown(ctx)</code>）</li>
<li>等待進行中的請求完成</li>
<li>flush 監控 buffer（<code>monitor.Close(ctx)</code>）</li>
<li>關閉 log 和其他資源</li>
</ol>
<p>如果先 close monitor 再 shutdown server，進行中的請求產生的事件會在 monitor 已關閉後嘗試送出，被靜默丟棄。</p>
<h2 id="signal-handling">Signal handling</h2>
<p>Go 的 <code>signal.Notify</code> 和 <code>signal.NotifyContext</code> 是接收 OS signal 的標準方式。SDK 在 init 時不應該自己註冊 signal handler — 這會和應用程式的 signal handling 衝突（Go 的 signal handler 是先到先得，後註冊的覆蓋先註冊的）。</p>
<p>SDK 端的適配方式是提供 <code>Close</code> 方法讓應用程式在自己的 signal handler 中呼叫，而非 SDK 內部攔截 signal。應用程式控制 shutdown 流程，SDK 只負責在被告知關閉時 flush 和清理。</p>
<h3 id="panic-recovery">panic recovery</h3>
<p>Go 的 panic 會終止當前 goroutine。如果 panic 發生在 main goroutine 且沒有 recover，程式直接退出，SDK 的 buffer 中的事件遺失。</p>
<p>SDK 可以提供 <code>monitor.RecoverAndReport()</code> 讓開發者在 goroutine 的入口用 <code>defer monitor.RecoverAndReport()</code> 攔截 panic，記錄 error 事件後再 re-panic（保持原有的 crash 行為）。</p>
<p>HTTP handler 的 panic 可以用 middleware 攔截：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="ln">1</span><span class="cl"><span class="kd">func</span> <span class="nf">monitorMiddleware</span><span class="p">(</span><span class="nx">next</span> <span class="nx">http</span><span class="p">.</span><span class="nx">Handler</span><span class="p">)</span> <span class="nx">http</span><span class="p">.</span><span class="nx">Handler</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">    <span class="k">return</span> <span class="nx">http</span><span class="p">.</span><span class="nf">HandlerFunc</span><span class="p">(</span><span class="kd">func</span><span class="p">(</span><span class="nx">w</span> <span class="nx">http</span><span class="p">.</span><span class="nx">ResponseWriter</span><span class="p">,</span> <span class="nx">r</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">        <span class="k">defer</span> <span class="nx">monitor</span><span class="p">.</span><span class="nf">RecoverAndReport</span><span class="p">()</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">        <span class="nx">next</span><span class="p">.</span><span class="nf">ServeHTTP</span><span class="p">(</span><span class="nx">w</span><span class="p">,</span> <span class="nx">r</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">    <span class="p">})</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><h2 id="http-server-自身監控">HTTP server 自身監控</h2>
<p>Go 常用來寫 collector 本身。Collector 需要監控自己的健康狀態 — 請求處理速率、錯誤率、goroutine 數量、記憶體使用量。</p>
<p>Collector 的自身監控和接收外部事件是兩個獨立的管線。自身監控的 metric 可以寫入獨立的 JSONL 檔案（和外部事件分開），或透過 Go 的 <code>expvar</code> / <code>runtime.ReadMemStats</code> 暴露為 HTTP endpoint。</p>
<p>自身監控的關鍵指標：</p>
<ul>
<li><code>collector.events.received</code>：每秒收到的事件數</li>
<li><code>collector.events.invalid</code>：schema 驗證失敗的事件數</li>
<li><code>collector.storage.write_duration_ms</code>：寫入 JSONL 的耗時</li>
<li><code>collector.goroutines</code>：goroutine 數量（洩漏偵測）</li>
<li><code>collector.memory.alloc_mb</code>：記憶體使用量</li>
</ul>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>跨平台 timestamp 一致性 → <a href="/blog/monitoring/05-platform-adaptation/cross-platform-timestamp/" data-link-title="跨平台 timestamp 一致性" data-link-desc="時區、精度、clock drift — 不同平台產生的 timestamp 在 collector 端需要能正確比對和排序">跨平台 timestamp 一致性</a></li>
<li>Collector 的架構設計 → <a href="/blog/monitoring/04-collector/" data-link-title="模組四：Collector 設計" data-link-desc="收 → 驗 → 存 → 查 → 觸發的完整鏈路 — Go 單一 binary、可插拔 Storage Backend、rule engine">模組四 Collector 設計</a></li>
<li>SDK 公開 API 的 Close 方法 → <a href="/blog/monitoring/03-sdk-design/public-api/" data-link-title="SDK 公開 API 設計" data-link-desc="init / event / error / metric / flush / close 六個方法構成 SDK 的完整生命週期 — 跨平台共用相同 API 介面">模組三 SDK 公開 API</a></li>
</ul>
]]></content:encoded></item><item><title>公開案例量是 vendor 社群活躍度 signal</title><link>https://tarrragon.github.io/blog/report/public-case-availability-vendor-signal/</link><pubDate>Mon, 18 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/report/public-case-availability-vendor-signal/</guid><description>&lt;h2 id="結論">結論&lt;/h2>
&lt;p>公開 customer engineering case 的累積量、是 vendor 社群活躍度跟長期可維護性的信號。case 多寡跟 vendor 工程能力沒有線性關係、跟以下因素相關：&lt;/p>
&lt;ul>
&lt;li>社群活躍度（用戶數 + 用戶寫 blog 文化）&lt;/li>
&lt;li>Vendor 自身的 customer success / DevRel 投入&lt;/li>
&lt;li>Feature 成熟度（新 feature 公開 case 通常稀薄）&lt;/li>
&lt;li>議題公開度（內部運維議題公司不常寫、incident / migration 容易寫）&lt;/li>
&lt;/ul>
&lt;p>選型時、公開 case 量值得作為信號之一、但要跟「該 vendor 是否仍積極開發」「文檔品質」「社群 issue 回應速度」等其他信號合併判讀。&lt;/p>
&lt;h2 id="為什麼">為什麼&lt;/h2>
&lt;p>backend/03-message-queue 模組 6 vendor 案例採集發現 case 累積量差異極大：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>Vendor&lt;/th>
 &lt;th>採集前案例&lt;/th>
 &lt;th>公開可採集案例（5-10 目標）&lt;/th>
 &lt;th>累積差異&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Kafka&lt;/td>
 &lt;td>已有 8 個&lt;/td>
 &lt;td>12 個新案例（容易找）&lt;/td>
 &lt;td>案例豐富&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>RabbitMQ&lt;/td>
 &lt;td>0（待補）&lt;/td>
 &lt;td>11 個新案例&lt;/td>
 &lt;td>中等豐富&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>AWS SQS&lt;/td>
 &lt;td>0（待補）&lt;/td>
 &lt;td>12 個新案例&lt;/td>
 &lt;td>豐富（managed service 客戶多）&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Google Pub/Sub&lt;/td>
 &lt;td>0（待補）&lt;/td>
 &lt;td>10 個新案例（Mercari/Spotify 集中）&lt;/td>
 &lt;td>中等&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>NATS&lt;/td>
 &lt;td>0（待補）&lt;/td>
 &lt;td>8 個新案例（部分依 Synadia partner blog）&lt;/td>
 &lt;td>中等偏少&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Redis Streams&lt;/td>
 &lt;td>0（待補）&lt;/td>
 &lt;td>6 個新案例（不少公司用 Redis 但少寫 Streams）&lt;/td>
 &lt;td>偏少&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>差異不只是「採集力度」、是公開資料密度本身差異。&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;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>「Kafka case 比 NATS 多、所以選 Kafka」&lt;/td>
 &lt;td>把 case 量當技術品質訊號、忽略需求形狀對齊（NATS 對 microservices messaging 可能更合適）&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>「Redis Streams case 少、所以不該用」&lt;/td>
 &lt;td>把案例稀薄當不成熟訊號、但 Redis Streams 在 Redis 生態內已是常見 pattern、只是公司不常單獨寫 blog&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>「Pub/Sub case 集中在 Spotify + Mercari、所以代表性不足」&lt;/td>
 &lt;td>大公司多篇深度 case 比中等公司零散 case 教學價值更高、累積量不等於覆蓋廣度&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="修法">修法&lt;/h2>
&lt;p>選型時把案例量當合併信號之一、跟以下信號交叉判讀：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>議題對齊度&lt;/strong>：該 vendor 的 case 是否覆蓋你的需求形狀（吞吐 / 延遲 / 持久化 / 多租戶 / 跨區）？&lt;/li>
&lt;li>&lt;strong>Vendor 活躍度&lt;/strong>：GitHub release 節奏、issue 回應速度、CVE 修復時間&lt;/li>
&lt;li>&lt;strong>生態整合&lt;/strong>：是否有你需要的 client library / framework / observability 工具&lt;/li>
&lt;li>&lt;strong>社群健康&lt;/strong>：Stack Overflow 問題回答率、Discord / Slack 活躍度&lt;/li>
&lt;li>&lt;strong>長期承諾&lt;/strong>：vendor 公司 / 基金會背景、license 模式、商業化路徑&lt;/li>
&lt;/ol>
&lt;p>單看案例量會誤導、但&lt;strong>完全忽略也會錯失重要信號&lt;/strong>：某些 vendor 案例量低反映社群活躍度低、選型後遇到問題找不到參考、自己要從零摸索。&lt;/p>
&lt;h2 id="關係">關係&lt;/h2>
&lt;ul>
&lt;li>&lt;strong>跟採集流程的關係&lt;/strong>：採集到「該 vendor 公開 case 偏少」是真實信號、不是採集失敗、不該強求 10 個案例&lt;/li>
&lt;li>&lt;strong>跟 case-driven 寫作的關係&lt;/strong>：公開 case 稀薄的章節改走 standard-driven 或通用工程知識補強、明示覆蓋缺口&lt;/li>
&lt;li>&lt;strong>跟 vendor 選型的關係&lt;/strong>：案例量是合併信號之一、不是主要判讀依據&lt;/li>
&lt;/ul>
&lt;h2 id="case">case&lt;/h2>
&lt;p>backend/03-message-queue 模組採集後盤點：&lt;/p></description><content:encoded><![CDATA[<h2 id="結論">結論</h2>
<p>公開 customer engineering case 的累積量、是 vendor 社群活躍度跟長期可維護性的信號。case 多寡跟 vendor 工程能力沒有線性關係、跟以下因素相關：</p>
<ul>
<li>社群活躍度（用戶數 + 用戶寫 blog 文化）</li>
<li>Vendor 自身的 customer success / DevRel 投入</li>
<li>Feature 成熟度（新 feature 公開 case 通常稀薄）</li>
<li>議題公開度（內部運維議題公司不常寫、incident / migration 容易寫）</li>
</ul>
<p>選型時、公開 case 量值得作為信號之一、但要跟「該 vendor 是否仍積極開發」「文檔品質」「社群 issue 回應速度」等其他信號合併判讀。</p>
<h2 id="為什麼">為什麼</h2>
<p>backend/03-message-queue 模組 6 vendor 案例採集發現 case 累積量差異極大：</p>
<table>
  <thead>
      <tr>
          <th>Vendor</th>
          <th>採集前案例</th>
          <th>公開可採集案例（5-10 目標）</th>
          <th>累積差異</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Kafka</td>
          <td>已有 8 個</td>
          <td>12 個新案例（容易找）</td>
          <td>案例豐富</td>
      </tr>
      <tr>
          <td>RabbitMQ</td>
          <td>0（待補）</td>
          <td>11 個新案例</td>
          <td>中等豐富</td>
      </tr>
      <tr>
          <td>AWS SQS</td>
          <td>0（待補）</td>
          <td>12 個新案例</td>
          <td>豐富（managed service 客戶多）</td>
      </tr>
      <tr>
          <td>Google Pub/Sub</td>
          <td>0（待補）</td>
          <td>10 個新案例（Mercari/Spotify 集中）</td>
          <td>中等</td>
      </tr>
      <tr>
          <td>NATS</td>
          <td>0（待補）</td>
          <td>8 個新案例（部分依 Synadia partner blog）</td>
          <td>中等偏少</td>
      </tr>
      <tr>
          <td>Redis Streams</td>
          <td>0（待補）</td>
          <td>6 個新案例（不少公司用 Redis 但少寫 Streams）</td>
          <td>偏少</td>
      </tr>
  </tbody>
</table>
<p>差異不只是「採集力度」、是公開資料密度本身差異。</p>
<h2 id="反模式">反模式</h2>
<p>選型時誤用案例量的方式：</p>
<table>
  <thead>
      <tr>
          <th>反模式</th>
          <th>問題</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>「Kafka case 比 NATS 多、所以選 Kafka」</td>
          <td>把 case 量當技術品質訊號、忽略需求形狀對齊（NATS 對 microservices messaging 可能更合適）</td>
      </tr>
      <tr>
          <td>「Redis Streams case 少、所以不該用」</td>
          <td>把案例稀薄當不成熟訊號、但 Redis Streams 在 Redis 生態內已是常見 pattern、只是公司不常單獨寫 blog</td>
      </tr>
      <tr>
          <td>「Pub/Sub case 集中在 Spotify + Mercari、所以代表性不足」</td>
          <td>大公司多篇深度 case 比中等公司零散 case 教學價值更高、累積量不等於覆蓋廣度</td>
      </tr>
  </tbody>
</table>
<h2 id="修法">修法</h2>
<p>選型時把案例量當合併信號之一、跟以下信號交叉判讀：</p>
<ol>
<li><strong>議題對齊度</strong>：該 vendor 的 case 是否覆蓋你的需求形狀（吞吐 / 延遲 / 持久化 / 多租戶 / 跨區）？</li>
<li><strong>Vendor 活躍度</strong>：GitHub release 節奏、issue 回應速度、CVE 修復時間</li>
<li><strong>生態整合</strong>：是否有你需要的 client library / framework / observability 工具</li>
<li><strong>社群健康</strong>：Stack Overflow 問題回答率、Discord / Slack 活躍度</li>
<li><strong>長期承諾</strong>：vendor 公司 / 基金會背景、license 模式、商業化路徑</li>
</ol>
<p>單看案例量會誤導、但<strong>完全忽略也會錯失重要信號</strong>：某些 vendor 案例量低反映社群活躍度低、選型後遇到問題找不到參考、自己要從零摸索。</p>
<h2 id="關係">關係</h2>
<ul>
<li><strong>跟採集流程的關係</strong>：採集到「該 vendor 公開 case 偏少」是真實信號、不是採集失敗、不該強求 10 個案例</li>
<li><strong>跟 case-driven 寫作的關係</strong>：公開 case 稀薄的章節改走 standard-driven 或通用工程知識補強、明示覆蓋缺口</li>
<li><strong>跟 vendor 選型的關係</strong>：案例量是合併信號之一、不是主要判讀依據</li>
</ul>
<h2 id="case">case</h2>
<p>backend/03-message-queue 模組採集後盤點：</p>
<ul>
<li>Kafka 17+ 案例、議題覆蓋廣度高、但 KRaft / 部分新 feature 仍稀薄</li>
<li>NATS 8 案例、議題集中在 IoT / edge / multi-cloud、其他場景偏少</li>
<li>Redis Streams 6 案例、Stream + Functions / Cluster on Streams 缺、是 feature 成熟度信號</li>
<li>Pub/Sub Mercari 4 篇深度 case 是 anchor cluster、品質高過案例量</li>
</ul>
<p>選型時把這些差異當輔助信號、不當主判讀。</p>
<h2 id="判讀徵兆">判讀徵兆</h2>
<p>何時案例量該升為主要選型信號：</p>
<ul>
<li>該領域有很多 vendor 都做類似功能（如 message broker 有 7+ 個 vendor）、案例量可以區分活躍度</li>
<li>該 vendor 是新興 / 商業化不確定（vendor lock-in 風險）、需要評估社群獨立性</li>
<li>該 vendor 過去有 license 改變或商業化轉向（Redis / Elasticsearch / MongoDB）、社群 fork 的活躍度該追蹤</li>
</ul>
<p>何時案例量不該當主要信號：</p>
<ul>
<li>需求形狀已有明確 vendor 對齊（如 GCP 生態下 Pub/Sub 是預設）</li>
<li>Vendor 公司本身極穩定（AWS / Google managed service）</li>
<li>主要 case 集中在反例 / 退場案例（這時案例多反而是負面信號）</li>
</ul>
]]></content:encoded></item></channel></rss>