產業脈動 2026 年 7 月 29 日

2026-07-29 — LINE 為百萬級 Kafka 訊息上了 E2EE、Spotify 用外部索引讓資料湖秒回單筆查詢、MCP 規格轉向 stateless-first 並由 C# SDK v2.0 同步實作

primary=https://techblog.lycorp.co.jp/en/applying-e2ee-to-apache-kafka-in-line-app primary=https://engineering.atspotify.com/2026/7/indexing-the-data-lake-for-online-point-queries primary=https://blog.modelcontextprotocol.io/posts/2026-07-28/ primary=https://devblogs.microsoft.com/dotnet/announcing-v20-of-the-official-mcp-csharp-sdk/

LINE 如何在每秒百萬則訊息的 Kafka 上加裝端到端加密

LY Corp Tech Blog · 2026-07-27

LY Corporation 工程團隊 Changgu Han、Hyeon Woo Jeong、Haruki Okada 在 LY Corp Tech Blog 發表文章,說明他們如何為 LINE App 內部的 Apache Kafka 訊息流加上端到端加密(E2EE),在單一叢集吞吐量達 1,000,000 訊息/秒的規模下完成部署。這套機制已在生產環境上線,並在兩週的灰度發布期間錄得零起加解密相關事故。

原本的問題

LINE 的訊息、通知與內部事件透過 Kafka broker 在服務之間傳遞,broker 層級雖然已有 TLS 傳輸加密與存取控制,但這些機制管的是「誰能連上 broker」,管不到「broker 上儲存的資料本身是不是明文」。換句話說,任何取得 broker 存取權限的維運或攻擊者,理論上都能讀到磁碟上的原始訊息內容。

團隊要解的問題是:在不改動 Kafka client 內部邏輯、不新增額外伺服器、且不能讓百萬級 QPS 的吞吐量明顯下降的前提下,把加密範圍從「傳輸中」延伸到「儲存中」,做到訊息從 producer 寫入的那一刻起就是密文,直到被授權的 consumer 端才解密還原。

採用的方法

他們選擇的是 record 級別(而非整批 batch)的加密,透過 Kafka 既有的 interceptor 擴充點掛載,不需要碰觸 client library 核心程式碼。每一則訊息的 payload 用 AES-GCM 對稱加密(DEK,Data Encryption Key);而 DEK 本身再用 ECIES(橢圓曲線 secp521r1)非對稱加密成 KEK(Key Encryption Key)保護。

訊息結構因此拆成三段:header 攜帶 KEK 的 ID 與被 KEK 加密後的 DEK;body 則是被 DEK 加密後的 payload;而決定 partition 的 key 欄位維持不變,確保分區邏輯不受影響。金鑰管理採 共享 KEK 模型——多個授權 consumer 共用同一把 KEK,避免訊息大小隨 consumer 數量線性膨脹。

  • Producer 只被授予 公鑰加密權限,無法解密任何訊息
  • 只有通過授權的 consumer 才拿得到對應 私鑰解密權限
  • KEK 定期輪替,新舊金鑰在過渡期並存,做到零停機換鑰
  • Consumer 端偵測 header 是否存在加密中繼資料,缺 header 就直接視為明文放行,兼容舊訊息

部署策略上,團隊沒有一次全量切換,而是把加密訊息比例從 1% 逐步拉到 10%50%,最終在兩週內推到 100%,過程中明文與密文訊息在同一 topic 內共存,靠 header 判斷分流。

實際效果

最終量測結果顯示,加密機制在單一 instance 上帶來的 CPU 使用率增幅低於 1%,配合 DEK 快取策略,訊息體積增長也被壓到最低,整套方案完全沒有新增額外伺服器。系統在 1,000,000 訊息/秒的量級下運作,兩週漸進式發布期間錄得零起加解密相關事故。

這套設計的核心價值在於「縱深防禦」:即便 broker 存取控制或 TLS 傳輸層某一環被突破,資料本體仍然是密文,且整個方案不需要修改 Kafka client 的內部實作,屬於外掛式(interceptor-based)加密層。

原始來源:LY Corp Tech Blog


Spotify 用 Random Access Parquet 讓資料湖秒回單筆查詢

Spotify Engineering · 2026-07-27

Spotify Staff Data Engineer Will Edwards 在 Spotify Engineering Blog 發表文章,介紹他們如何在既有的 GCS 資料湖上,直接對未經修改的 Parquet 檔案建立外部索引,把原本要靠 Trino、BigQuery 等分散式 SQL 引擎才能查的單筆(point query)資料,做到接近 Bigtable 等線上系統的存取效率,同時不需要額外複製資料或重跑 ETL。

原本的問題

Spotify 的資料湖存有 exabyte 等級的歷史資料,但 Trino、BigQuery 這類分析型引擎即便只查一筆資料,查詢規劃的固定開銷就要花上數秒。相對地,Bigtable 這類專用系統雖然能快速回應線上查詢,卻只裝得下 petabyte 級的子集資料。真正的瓶頸不是儲存介質本身——文章指出「GCS 單次請求延遲落在 30-100ms」——而是分析引擎的查詢規劃機制天生是為吞吐量設計,不是為互動式存取設計。

團隊要解的問題是:能不能不建第二套儲存系統、不搬資料,就讓資料湖本身也能做到毫秒級的單筆查詢。

採用的方法

他們的做法叫 Random Access Parquet(RAP):在既有 ML pipeline、批次分析共用的原始 Parquet 檔案之上,額外建立一個 外部索引,把 key 直接映射到「檔案序號+行號(row numbers)」,用精確的 ranged read 只抓需要的位元組,取代原本一連串互相依賴的讀取鏈。這個索引本質上是 O(1) 查找的 multimap,和 Parquet 內建的 PageIndex 或 Bloom filter 不同——後兩者是機率性過濾,RAP 索引則是確定性的。

在建索引之前,系統會先用 候選集縮減 做粗篩:依檔名做逐檔 partition pruning,再用 user ID 欄位上的 Bloom filter(快取在 metadata store)把候選範圍從 90 天窄化到使用者實際活躍的十幾天。之後才是針對 Parquet 檔案本身的三類優化:

  • 集中 key 資料:依 key 排序讓相關列聚在同一個 page;用 ARRAY_AGG(STRUCT(...)) 做 co-grouping,確保每個 key 每檔只出現一列;改用較粗的分區粒度(例如日轉週),一年檔案數從 365 降到 52
  • 減少讀取位元組:page 在 key 邊界處 flush,讓每個解壓縮後的 page 只屬於一個 key;搭配 ZSTD frame reset 讓每個 key 的資料可在壓縮 frame 內獨立定址,並用 skippable frame 對齊到 4KB/16KB 區塊邊界,避免讀取放大
  • 減少讀取次數:把多欄位收斂成單一 JSON/Protobuf 欄位(Blob/Variant),將 N 欄讀取變成一次讀取;同一 key 的多欄資料相鄰排列並用 ZSTD skip frame 串接,一次連續 ranged read 就能取代多次讀取;甚至把小型數值直接內嵌進索引項目(covering index),連儲存讀取都省掉

實際效果

以一個橫跨 90 天、涉及數十億使用者、每天產生上千個檔案的查詢情境為例,未優化前得掃過 90,000+ 個 Parquet 檔案,索引縮減後只需要碰觸約 12 個檔案。文章總結:把上述優化疊加起來,一次單筆查詢可以收斂成一次幾 KB 的 ranged read,甚至完全省下儲存讀取

RAP 之上還能疊加多種次級索引結構——雜湊表做 O(1) 精確查找、排序索引做範圍查詢——都建立在同一批索引項目上,且完全不需要更動既有的資料寫入 pipeline 或重寫資料本身。

原始來源:Spotify Engineering Blog


MCP 規格轉向 Stateless-First,C# SDK v2.0 同步跟進實作

devblogs.microsoft.com · 2026-07-28

Model Context Protocol 專案的兩位 Lead Maintainer David Soria Parra 與 Den Delimarsky,在 MCP 官方部落格發布 2026-07-28 版規格,這是自 MCP 推出以來最大幅度的一次協定改版,核心是把協定從雙向、有狀態的模型轉為請求/回應式的 stateless-first 架構。同一天,Microsoft 工程經理 Jeff Handley 在 .NET 官方部落格宣布官方 MCP C# SDK v2.0 同步實作這份新規格。

背景

舊版 MCP 依賴 initializeinitialized 交握建立連線狀態,再靠 Mcp-Session-Id header 維繫整個 session,這代表部署時必須有 sticky routing 或 session 同步機制,多實例部署與水平擴展因此變得麻煩。官方文章形容「拿掉 session 狀態」是開發者社群最常提出的需求之一。

核心改動

新規格依 SEP-2575SEP-2567 廢除了 initializeinitialized 交握與 Mcp-Session-Id header;協定版本、client 身份與能力改為放進每一次請求的 _meta 欄位內,讓每個請求自帶完整上下文,不再依賴長連線或 session 親和性。伺服器端仍可選擇性提供 server/discover 讓 client 事先查詢能力,但這是可選項,不是必要交握。

另一項變動是 SEP-2243 標準化的 HTTP header 路由:新增 Mcp-MethodMcp-Name header 暴露方法資訊,Mcp-Param-* 系列 header 則可選擇性提升部分工具參數,讓 gateway、WAF 不必解析 JSON body 就能做地理分佈式路由,JSON-RPC body 本身仍是最終權威來源:

Mcp-Method: tools/call
Mcp-Name: search

過去需要伺服器主動發起請求(server-initiated)才能做的互動——像是 elicitation、sampling、roots——在無狀態傳輸下被 Multi Round-Trip Requests(MRTR,SEP-2322)取代:工具回傳 resultType: "input_required" 的結果,client 蒐集完所需輸入後,把答案放進 inputResponses 重新發送同一次呼叫,可以橫跨多輪互動。此外 SEP-2549tools/listprompts/listresources/listresources/read 的回應可以帶 ttlMscacheScope,減少重複拉取。授權面也同步收緊,包括 RFC 9207 的 issuer 驗證、client 憑證綁定發證的授權伺服器(SEP-2352),並正式棄用 Dynamic Client Registration,改推 Client ID Metadata Documents(CIMD)。

影響範圍

C# SDK v2.0ModelContextProtocol.AspNetCore 套件把 stateless 設成 HTTP transport 的預設行為,啟動一個無狀態 MCP 伺服器只需要:

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddMcpServer()
    .WithHttpTransport()   // stateless by default
    .WithToolsFromAssembly();

var app = builder.Build();
app.MapMcp();
app.Run("http://localhost:3001");

相容性上維持雙向:v2 client 連到舊版伺服器時會自動退回舊版 initialize 交握,v2 伺服器也接受舊版 client 的交握請求;穩定的 v1 API 全部繼續可用,只是被標成 deprecation 警告(診斷碼 MCP9004MCP9006),沒有被直接移除。唯一的例外是實驗性的 Tasks 擴充:依 SEP-2663,Tasks 從核心規格移到獨立擴充 io.modelcontextprotocol/tasks,改用輪詢式的 tasks/get 與新增的 tasks/update,與舊版設計不相容。

  • ModelContextProtocol.Core:client 與低階伺服器,依賴最少
  • ModelContextProtocol:含 hosting/DI 的 stdio 伺服器,官方建議的起點
  • ModelContextProtocol.AspNetCore:Streamable HTTP 伺服器
  • ModelContextProtocol.Extensions.Tasks:輪詢式長任務工具

MCP 官方规格文章也提到,四個 Tier 1 SDK(TypeScript、Python、Go、C#)都已完成規格同步,Rust SDK 則以 beta 形式支援;舊有的 HTTP+SSE transport 與 Roots、Sampling、Logging 被列為棄用,依 SEP-2577 給予至少 12 個月的下架緩衝期。C# SDK 支援的目標框架涵蓋 net8.0net9.0net10.0netstandard2.0

原始來源:MCP 官方部落格.NET DevBlogs


End of article
0
Would love your thoughts, please comment.x
()
x