gRPC 高效通訊指南
用途:高效能、強型別的跨服務 RPC 通訊框架(HTTP/2 + Protocol Buffers)
NuGet:
Grpc.Net.Client(客戶端)/Grpc.AspNetCore(伺服端)授權:Apache-2.0
支援:.NET 6+
GitHub:grpc/grpc-dotnet
什麼是 gRPC
gRPC(gRPC Remote Procedure Call)是 Google 開發的高效能 RPC 框架。它用 Protocol Buffers(protobuf) 做二進制序列化,透過 HTTP/2 傳輸,效能遠優於傳統的 JSON over HTTP。
在工業自動化場景中,設備資料量大(多台 PLC 同時上報感測器數據)、延遲要求低(控制命令即時送達)、型別安全很重要(暫存器值的型別不能搞錯)。gRPC 在這些方面都有明顯優勢。
核心特性
| 特性 | 說明 |
|---|---|
| Protocol Buffers | 二進制序列化,比 JSON 小 3-10 倍,解析快 10 倍 |
| HTTP/2 | 多路複用、Header 壓縮、Server Push |
| 強型別 | 從 .proto 檔案自動生成型別安全的 Client/Server 程式碼 |
| 串流支援 | 原生支援 Server/Client/雙向串流 |
| 多語言 | 同一份 .proto 可生成 C#、Python、Go、Java 等程式碼 |
vs REST API vs SignalR vs ZeroMQ
| 比較項目 | gRPC | REST (JSON/HTTP) | SignalR | ZeroMQ |
|---|---|---|---|---|
| 序列化 | Protobuf(二進制) | JSON(文字) | JSON / MessagePack | 自訂 |
| 傳輸 | HTTP/2 | HTTP/1.1 or 2 | WebSocket | TCP/IPC |
| 型別安全 | ✅ 編譯期 | ❌ 執行期 | ❌ 執行期 | ❌ 無 |
| 串流 | ✅ 原生四種模式 | ❌ 需要 SSE/WebSocket | ✅ 即時雙向 | ✅ Pub/Sub |
| 效能 | 極高 | 中等 | 高 | 極高 |
| 瀏覽器支援 | ❌ 需 gRPC-Web | ✅ 原生 | ✅ 原生 | ❌ 無 |
| 學習曲線 | 中等(需學 proto) | 低 | 低 | 高 |
| 適合場景 | 服務間通訊、設備數據串流 | 公開 API、Web 前端 | 即時 UI 更新 | 超低延遲 |
公司場景選擇
- 設備資料即時串流(PLC → 監控端):gRPC Server Streaming
- 服務間通訊(MES 整合層 ↔ 設備控制層):gRPC Unary
- WPF UI 即時更新:SignalR 或直接用 Rx.NET
- 公開 REST API(給外部系統呼叫):仍用 REST
四種通訊模式
gRPC 支援四種模式,涵蓋幾乎所有通訊場景:
1. Unary(一問一答)
最常見的模式,等同於傳統的 Request/Response:
場景:發送控制命令(啟動/停止設備)、查詢設備狀態
2. Server Streaming(伺服器推流)
客戶端發一個請求,伺服器持續推送多筆回應,直到客戶端取消或伺服器主動結束:
場景:訂閱 PLC 感測器數據、即時監控資料推送
3. Client Streaming(客戶端推流)
客戶端持續送出多筆資料,伺服器最後回一個彙總回應:
場景:批次上傳感測器歷史資料、檔案上傳
4. Bidirectional Streaming(雙向推流)
雙方在同一條連線上同時持續推送,互不依賴對方節奏:
場景:多設備即時命令與回報、雙向互動控制
.proto 定義
gRPC 的介面用 .proto 檔案定義。這份檔案同時描述服務介面和資料結構,然後自動生成 C# 程式碼。
設備服務範例
syntax = "proto3";
option csharp_namespace = "GatherTech.Equipment.Grpc";
package equipment;
// 設備控制服務
service DeviceService {
// 一問一答:查詢設備狀態
rpc GetStatus (DeviceRequest) returns (DeviceStatus);
// 一問一答:發送控制命令
rpc SendCommand (DeviceCommand) returns (CommandResult);
// Server Streaming:訂閱感測器數據
rpc StreamSensorData (SensorSubscription) returns (stream SensorData);
// Bidirectional:雙向控制通道
rpc ControlChannel (stream DeviceCommand) returns (stream DeviceEvent);
}
message DeviceRequest {
string device_id = 1;
}
message DeviceStatus {
string device_id = 1;
bool is_connected = 2;
string mode = 3; // "Auto", "Manual", "Maintenance"
double temperature = 4;
double pressure = 5;
int64 uptime_seconds = 6;
}
message DeviceCommand {
string device_id = 1;
string command = 2; // "Start", "Stop", "Reset"
map<string, string> parameters = 3;
}
message CommandResult {
bool success = 1;
string message = 2;
}
message SensorSubscription {
string device_id = 1;
int32 interval_ms = 2; // 推送間隔(毫秒)
}
message SensorData {
string device_id = 1;
int64 timestamp = 2; // Unix timestamp (ms)
double temperature = 3;
double pressure = 4;
double flow_rate = 5;
int32 status_code = 6;
}
message DeviceEvent {
string device_id = 1;
string event_type = 2; // "AlarmRaised", "StateChanged", "CommandAck"
string payload = 3;
int64 timestamp = 4;
}
安裝
伺服端(ASP.NET Core)
dotnet add package Grpc.AspNetCore
客戶端
dotnet add package Grpc.Net.Client
dotnet add package Google.Protobuf
dotnet add package Grpc.Tools # .proto → C# 自動生成
專案檔設定(.csproj)
<ItemGroup>
<Protobuf Include="Protos\device.proto" GrpcServices="Both" />
</ItemGroup>
GrpcServices 可以是 Server、Client 或 Both,控制只生成伺服端、客戶端或兩者的程式碼。
基本範例:設備狀態查詢
伺服端實作
public class DeviceServiceImpl : DeviceService.DeviceServiceBase
{
private readonly IDeviceManager _deviceManager;
private readonly ILogger<DeviceServiceImpl> _logger;
public DeviceServiceImpl(
IDeviceManager deviceManager,
ILogger<DeviceServiceImpl> logger)
{
_deviceManager = deviceManager;
_logger = logger;
}
public override async Task<DeviceStatus> GetStatus(
DeviceRequest request, ServerCallContext context)
{
_logger.LogDebug("GetStatus request for {DeviceId}", request.DeviceId);
var device = await _deviceManager.GetDeviceAsync(request.DeviceId);
return new DeviceStatus
{
DeviceId = device.Id,
IsConnected = device.IsConnected,
Mode = device.Mode.ToString(),
Temperature = device.Temperature,
Pressure = device.Pressure,
UptimeSeconds = (long)device.Uptime.TotalSeconds
};
}
}
// 註冊服務(Program.cs)
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddGrpc();
var app = builder.Build();
app.MapGrpcService<DeviceServiceImpl>();
app.Run();
客戶端呼叫
// 建立連線
using var channel = GrpcChannel.ForAddress("https://localhost:5001");
var client = new DeviceService.DeviceServiceClient(channel);
// Unary 呼叫
var status = await client.GetStatusAsync(new DeviceRequest
{
DeviceId = "CMP-01"
});
Console.WriteLine($"Device {status.DeviceId}: " +
$"Connected={status.IsConnected}, " +
$"Temp={status.Temperature}°C");
延伸閱讀
- 公司場景 Pattern — 設備資料串流、控制命令、微服務通訊、效能比較
- Modbus 通訊整合 — 現有的 PLC 通訊方式
- SECS/GEM 通訊整合 — 半導體設備通訊協定