MCP(Model Context Protocol)協議是一個用於 AI 模型和工具之間通信的標準協議。隨着 AI 應用變得越來越複雜並被廣泛部署,原有的通信機制面臨着一系列挑戰。近期 MCP 倉庫的 PR #206【1】 引入了一個全新的 Streamable HTTP 傳輸層替代原有的 HTTP+SSE 傳輸層。本文將詳細分析該協議的技術細節和實際優勢。
要點速覽
- Streamable HTTP 相比 HTTP + SSE 具有更好的穩定性,在高併發場景下表現更優。
- Streamable HTTP 在性能方面相比 HTTP + SSE 具有明顯優勢,響應時間更短且更穩定。
- Streamable HTTP 客户端實現相比 HTTP + SSE 更簡單,代碼量更少,維護成本更低。
為什麼選擇 Streamable HTTP?
HTTP + SSE 存在的問題
HTTP+SSE 的傳輸過程實現中,客户端和服務器通過兩個主要渠道進行通信:(1)HTTP 請求 / 響應:客户端通過標準的 HTTP 請求向服務器發送消息。(2)服務器發送事件(SSE):服務器通過專門的 /sse 端點向客户端推送消息,這就導致存在下面三個問題:
- 服務器必須維護長連接,在高併發情況下會導致顯著的資源消耗。
- 服務器消息只能通過 SSE 傳遞,造成了不必要的複雜性和開銷。
- 基礎架構兼容性,許多現有的網絡基礎架構可能無法正確處理長期的 SSE 連接。企業防火牆可能會強制終止超時連接,導致服務不可靠。
Streamable HTTP 的改進
Streamable HTTP 是 MCP 協議的一次重要升級,通過下面的改進解決了原有 HTTP + SSE 傳輸方式的多個關鍵問題:
- 統一端點:移除了專門建立連接的 /sse 端點,將所有通信整合到統一的端點。
- 按需流式傳輸:服務器可以靈活選擇返回標準 HTTP 響應或通過 SSE 流式返回。
- 狀態管理:引入 session 機制以支持狀態管理和恢復。
HTTP + SSE vs Streamable HTTP
下面通過實際應用場景中穩定性,性能和客户端複雜度三個角度對比説明 Streamable HTTP 相比 HTTP + SSE 的優勢,AI 網關 Higress 目前已經支持了 Streamable HTTP 協議,通過 MCP 官方 Python SDK 的樣例 Server 部署了一個 HTTP + SSE 協議的 MCP Server,通過 Higress 部署了一個 Streamable HTTP 協議的 MCP Server。
穩定性對比
TCP 連接數對比
利用 Python 程序模擬 1000 個用户同時併發訪問遠程的 MCP Server 並調用獲取工具列表,圖中可以看出 SSE Server 的 SSE 連接無法複用且需要長期維護,高併發的需求也會帶來 TCP 連接數的突增,而 Streamable HTTP 協議則可以直接返回響應,多個請求可以複用同一個 TCP 連接,TCP 連接數最高只到幾十條,並且整體執行時間也只有 SSE Server 的四分之一。
在 1000 個併發用户的測試場景下,Higress 部署的 Streamable HTTP 方案的 TCP 連接數明顯低於 HTTP + SSE 方案:
- HTTP + SSE:需要維持大量長連接,TCP 連接數隨時間持續增長
- Streamable HTTP:按需建立連接,TCP 連接數維持在較低水平
請求成功率對比
實際應用場景中進程級別通常會限制最大連接數,linux 默認通常是 1024。利用 Python 程序模擬不同數量的用户訪問遠程的 MCP Server 並調用獲取工具列表,SSE Server 在併發請求數到達最大連接數限制後,成功率會極速下降,大量的併發請求無法建立新的 SSE 連接而訪問失敗。
在不同併發用户數下的請求成功率測試中,Higress 部署的 Streamable HTTP 的成功率顯著高於 HTTP + SSE 方案:
- HTTP + SSE:隨着併發用户數增加,成功率顯著下降
- Streamable HTTP:即使在高併發場景下仍能保持較高的請求成功率
性能對比
這裏對比的是社區 Python 版本的 GitHub MCP Server【2】 和 Higress MCP 市場的 GitHub MCP Server
利用 Python 程序模擬不同數量的用户同時併發訪問遠程的 MCP Server 並調用獲取工具列表,並統計調用返回響應的時間,圖中給出的響應時間對比為對數刻度,SSE Server 在併發用户數量較多時平均響應時間會從 0.0018s 顯著增加到 1.5112s,而 Higress 部署的 Streamable HTTP Server 則依然維持在 0.0075s 的響應時間,也得益於 Higress 生產級的性能相比於 Python Starlette 框架。
性能測試結果顯示,Higress 部署的 Streamable HTTP 在響應時間方面具有明顯優勢:
- Streamable HTTP 的平均響應時間更短,響應時間波動較小,隨併發用户數增加,響應時間增長更平
- HTTP + SSE 的平均響應時間更長,在高併發場景下響應時間波動較大
客户端複雜度對比
Streamable HTTP 支持無狀態的服務和有狀態的服務,目前的大部分場景無狀態的 Streamable HTTP 的可以解決,通過對比兩種傳輸方案的客户端實現代碼,可以直觀地看到無狀態的 Streamable HTTP 的客户端實現簡潔性。
HTTP + SSE 客户端樣例代碼
class SSEClient:
def __init__(self, url: str, headers: dict = None):
self.url = url
self.headers = headers or {}
self.event_source = None
self.endpoint = None
async def connect(self):
# 1. 建立 SSE 連接
async with aiohttp.ClientSession(headers=self.headers) as session:
self.event_source = await session.get(self.url)
# 2. 處理連接事件
print('SSE connection established')
# 3. 處理消息事件
async for line in self.event_source.content:
if line:
message = json.loads(line)
await self.handle_message(message)
# 4. 處理錯誤和重連
if self.event_source.status != 200:
print(f'SSE error: {self.event_source.status}')
await self.reconnect()
async def send(self, message: dict):
# 需要額外的 POST 請求發送消息
async with aiohttp.ClientSession(headers=self.headers) as session:
async with session.post(self.endpoint, json=message) as response:
return await response.json()
async def handle_message(self, message: dict):
# 處理接收到的消息
print(f'Received message: {message}')
async def reconnect(self):
# 實現重連邏輯
print('Attempting to reconnect...')
await self.connect()
Streamable HTTP 客户端樣例代碼
class StreamableHTTPClient:
def __init__(self, url: str, headers: dict = None):
self.url = url
self.headers = headers or {}
async def send(self, message: dict):
# 1. 發送 POST 請求
async with aiohttp.ClientSession(headers=self.headers) as session:
async with session.post( self.url, json=message,
headers={'Content-Type': 'application/json'}
) as response:
# 2. 處理響應
if response.status == 200:
return await response.json()
else:
raise Exception(f'HTTP error: {response.status}')
從代碼對比可以看出:
- 複雜度:Streamable HTTP 無需處理連接維護、重連等複雜邏輯
- 可維護性:Streamable HTTP 代碼結構更清晰,更易於維護和調試
- 錯誤處理:Streamable HTTP 的錯誤處理更直接,無需考慮連接狀態