你的服務用 Go 寫,呼叫一個 Python 的 ML 服務,兩個服務之間要怎麼定義 API?
你在看一個 Go 的 codebase,想讓 IDE 給你 go-to-definition 和型別資訊,需要什麼工具?
你的 CI 要跑 Go、Python、Node.js 三個語言的測試,怎麼管理這些環境?
這篇是這些場景的工具地圖。
跨語言服務通訊
Protocol Buffers(protobuf)+ gRPC
當兩個不同語言的服務要溝通,你需要一個雙方都能理解的 API 定義。
protobuf 是一個語言無關的介面描述語言(IDL):
// user.proto
syntax = "proto3";
service UserService {
rpc GetUser (GetUserRequest) returns (User);
rpc ListUsers (ListUsersRequest) returns (stream User);
}
message GetUserRequest {
int64 user_id = 1;
}
message User {
int64 id = 1;
string name = 2;
string email = 3;
}protoc(protobuf compiler)從這個 .proto 檔生成各語言的 code:
protoc --go_out=. --go-grpc_out=. user.proto # 生成 Go code
protoc --python_out=. --grpc_python_out=. user.proto # 生成 Python codeGo 的 server 和 Python 的 client 各自用生成的 code,型別定義一致,不用手動維護兩邊。
gRPC 是建在 protobuf 上的 RPC 框架,支援:
- Unary(一個請求一個回應)
- Server streaming(一個請求,多個回應)
- Client streaming
- Bidirectional streaming
相較於 REST + JSON,gRPC 的優點:binary 格式比 JSON 小、速度快、型別強制。代價是 debug 不如 JSON 直觀(需要工具解碼)。
OpenAPI / Swagger(REST 的 IDL)
如果你用 REST,OpenAPI 規格讓你也能從定義生成多語言 client:
# openapi.yaml
paths:
/users/{id}:
get:
parameters:
- name: id
in: path
schema:
type: integer
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/User'openapi-generator 能從這個生成 Go、Python、TypeScript 等語言的 client SDK。
語言環境管理
asdf / mise:多語言版本管理
如果你的 repo 有多個語言,每個語言有自己的版本需求:
# .tool-versions(asdf 格式)
python 3.11.5
golang 1.21.0
nodejs 20.9.0asdf install 一次安裝所有版本,asdf local 設定目錄的語言版本,切換目錄自動切版本。
mise(前身是 rtx)是更快的替代品,相容 .tool-versions 格式。
Docker:最極端的環境隔離
當「確保環境一致」比「快速安裝」更重要,用 Docker:
# Dockerfile(多語言服務)
FROM golang:1.21 AS go-builder
COPY go/ /app/
RUN cd /app && go build -o /server .
FROM python:3.11-slim
COPY --from=go-builder /server /server
COPY requirements.txt /app/
RUN pip install -r /app/requirements.txtMulti-stage build 讓你在同一個 Dockerfile 裡混合不同語言的建構步驟。
IDE 工具:Language Server Protocol(LSP)
你用 VS Code 或其他 editor,為什麼 go-to-definition 在 Go 能跳到正確位置、在 TypeScript 也能?
因為大多數現代語言都實作了 LSP(Language Server Protocol)——一個語言和 editor 之間的標準協議,定義了 textDocument/definition、textDocument/hover、textDocument/completion 等操作的格式。
Editor 發送請求(「游標在 line 23,col 10,這是什麼?」),Language Server 回應(「這是 User.Name,定義在 models.go:45」)。
主要的 Language Server:
- Go:
gopls(Go 官方) - Python:
pylsp、pyright(Microsoft) - TypeScript/JavaScript:
tsserver(內建於 TypeScript compiler) - Rust:
rust-analyzer
如果你在一個新語言的 codebase 裡 navigate,第一件事是確認對應的 Language Server 有安裝——這讓讀不熟悉語言的程式碼的效率提升幾倍。
跨語言測試工具
合約測試(Contract Testing)
當你有 A(Go)和 B(Python)兩個服務,A 呼叫 B 的 API。你怎麼確保 A 和 B 的 API 定義沒有衝突?
Pact(pact.io)是主流的合約測試框架,支援 Go、Python、Java、Node.js:
- B(Provider)定義它能提供什麼
- A(Consumer)定義它預期的 response 格式
- Pact 驗證 Provider 的實際回應滿足 Consumer 的期望
這讓 API 相容性問題在 CI 就被抓到,不是在 production。
k6:多語言的負載測試
k6 用 JavaScript 寫測試腳本,但可以對任何 HTTP/gRPC 服務做壓測——不管後端是 Go 還是 Python:
// k6 腳本
import http from 'k6/http';
export let options = {
vus: 100, // 100 個虛擬使用者
duration: '30s',
};
export default function() {
http.get('http://api.example.com/users/1');
}序列化格式的跨語言相容
JSON:最通用,但有細節
JSON 在所有語言都支援,但有幾個跨語言的細節:
- 整數精度:JavaScript 的
number型別是 64-bit float,大整數(> 2^53)會失去精度——Go 的int64序列化成 JSON 傳給 JS,建議用 string - 時間格式:用 ISO 8601(
2024-01-15T10:30:00Z),不要用 Unix timestamp(語意不清),也不要用 locale-specific 格式 - null vs missing field:Python 的
None、Go 的nil、JavaScript 的null/undefined在 JSON 序列化行為不完全一樣,需要明確處理
MessagePack / CBOR:binary JSON
如果 JSON 的 overhead(text 格式、parsing 成本)是問題,MessagePack 和 CBOR 是 binary 格式的替代品,大多數語言都有 library,和 JSON 的資料模型相容但更緊湊快速。
這些工具的選擇,取決於你的跨語言場景:
- 微服務通訊 → protobuf + gRPC 或 OpenAPI
- 多語言本地開發環境 → asdf / mise 或 Docker
- IDE navigate 多語言 codebase → 確認各語言的 LSP 有安裝
- 驗證跨服務 API 相容 → Pact 合約測試