为什么我们选 gRPC 而不是 REST?内部通信设计决策
TL;DR
为什么我们选 gRPC 而不是 REST?内部通信设计决策
“系列:S2 架构全景 · 第 2 篇 | 难度:中级 | 阅读时间:18 分钟
TL;DR
- 在内部通信基准测试中,gRPC 相比 REST/JSON 实现了 3-10 倍的吞吐量提升和 60-70% 的延迟下降,Protobuf 序列化体积仅为 JSON 的 30-50%。
- 平台 64 个 proto 文件按 Layer 命名空间组织,通过统一的代码生成管线为 Java(gRPC-Java)和 Python(grpcio)生成类型安全的客户端存根,形成契约驱动开发工作流。
- REST 仅作为外部门面存在(59 个端点),通过 Spring Cloud Gateway 实现 REST→gRPC 的协议转换,严格禁止内部服务使用 REST/JSON。
#1. 决策背景:一个技术红线的诞生
在 coomia-dip 的技术红线列表中,有一条很醒目:
“禁止内部服务之间使用 REST/JSON(必须使用 gRPC)
这不是一个随意的偏好,而是经过严格基准测试和架构分析后的理性决策。本文将完整展开这个决策的推理过程。
#2. gRPC vs REST 基准测试
#2.1 测试环境
硬件: 4C8G Linux VM (Intel Xeon E5-2680)
网络: 同一数据中心,延迟 < 0.1ms
场景: Ontology 实例 CRUD 操作
数据: 典型 Ontology 实例(15 个属性,3 个关系)
框架对比:
REST: Spring Boot 3.x + Jackson + HTTP/1.1
gRPC: gRPC-Java 1.60 + Protobuf 3 + HTTP/2
#2.2 延迟对比
操作类型 REST (P50/P99) gRPC (P50/P99) 改善
----------- ---------------- ---------------- ------
GetInstance 2.1ms / 8.3ms 0.6ms / 2.1ms 71% / 75%
CreateInstance 3.8ms / 15.2ms 1.2ms / 4.8ms 68% / 68%
BatchGet(100) 18ms / 52ms 5ms / 14ms 72% / 73%
QueryFederation 45ms / 180ms 12ms / 48ms 73% / 73%
StreamSubscribe N/A 0.3ms/msg N/A
#2.3 吞吐量对比
并发连接数 REST (req/s) gRPC (req/s) 倍数
----------- ------------ ------------ -----
10 4,200 14,500 3.5x
50 8,100 38,000 4.7x
100 10,200 62,000 6.1x
500 11,500 105,000 9.1x
1000 10,800 118,000 10.9x
#2.4 载荷体积对比
数据对象 JSON (bytes) Protobuf (bytes) 压缩比
--------------------- ------------ ---------------- ------
OntologyInstance (小) 512 185 36%
OntologyInstance (中) 2,048 680 33%
OntologyInstance (大) 8,192 2,870 35%
BatchResponse (100条) 204,800 68,000 33%
WorldContext 256 48 19%
#2.5 为什么差距这么大?
HTTP/1.1 + JSON (REST):
+------+------------------+------+------------------+------+
| Conn | Headers (500B) | Body | Headers (500B) | Body |
| Est | Content-Type | JSON | Content-Type | JSON |
| | Accept | | Accept | |
| | Authorization | | Authorization | |
| | X-Request-Id | | X-Request-Id | |
+------+------------------+------+------------------+------+
每个请求独立的 TCP 连接或 Keep-Alive 复用(串行)
文本序列化,人类可读但冗余
HTTP/2 + Protobuf (gRPC):
+------+--------+--------+--------+--------+--------+
| Conn | Frame1 | Frame2 | Frame3 | Frame4 | Frame5 |
| Est | (Req) | (Req) | (Resp) | (Req) | (Resp) |
| | 48B | 185B | 680B | 48B | 185B |
+------+--------+--------+--------+--------+--------+
单连接多路复用(并行)
二进制序列化,紧凑高效
Header 压缩 (HPACK)
关键差异因素:
| 因素 | REST/JSON | gRPC/Protobuf |
|---|---|---|
| 传输协议 | HTTP/1.1 | HTTP/2 多路复用 |
| 序列化 | JSON(文本) | Protobuf(二进制) |
| Header | 每请求完整 Header | HPACK 压缩 |
| 连接管理 | Keep-Alive 串行 | 单连接并行流 |
| 流式 | 不原生支持 | 原生双向流 |
| 类型安全 | 运行时校验 | 编译时检查 |
#3. Protobuf 契约驱动开发工作流
#3.1 Proto-First 开发流程
在 coomia-dip 中,proto 文件是一切的起点:
Step 1: 定义 Proto
architect writes .proto file
|
v
Step 2: 团队评审
proto PR review (breaking change check)
|
v
Step 3: 代码生成
buf generate (Java stubs + Python stubs)
|
+---> Java: control-Layer/src/gen/
| com.onto.{Layer}.api.v1.*
|
+---> Python: intelligence-Layer/gen/
| onto_{Layer}_v1_pb2.py
| onto_{Layer}_v1_pb2_grpc.py
|
+---> SDK: python-sdk/ontology_sdk/gen/
(gRPC client wrappers)
|
v
Step 4: 实现服务端
Java: implements XxxServiceGrpc.XxxServiceImplBase
Python: class XxxServicer(xxx_pb2_grpc.XxxServicer)
|
v
Step 5: SDK 封装
Python SDK wraps gRPC stubs into Pythonic API
|
v
Step 6: REST Facade 映射
Spring Cloud Gateway maps REST endpoints to gRPC calls
#3.2 实际 Proto 示例
以 OntologyRuntimeService 为例,这是平台最核心的服务:
// proto/plane_c/ontology_runtime.proto
syntax = "proto3";
package com.onto.data.v1;
option java_package = "com.onto.data.api.v1";
option java_outer_classname = "OntologyRuntimeProto";
option java_multiple_files = true;
import "common/common.proto";
import "common/errors.proto";
import "google/protobuf/struct.proto";
import "google/protobuf/timestamp.proto";
service OntologyRuntimeService {
// Instance CRUD
rpc CreateInstance(CreateInstanceRequest)
returns (CreateInstanceResponse);
rpc GetInstance(GetInstanceRequest)
returns (GetInstanceResponse);
rpc UpdateInstance(UpdateInstanceRequest)
returns (UpdateInstanceResponse);
rpc PatchInstance(PatchInstanceRequest)
returns (PatchInstanceResponse);
rpc DeleteInstance(DeleteInstanceRequest)
returns (DeleteInstanceResponse);
// Batch Operations
rpc BatchCreateInstances(BatchCreateInstancesRequest)
returns (BatchCreateInstancesResponse);
rpc BatchGetInstances(BatchGetInstancesRequest)
returns (BatchGetInstancesResponse);
// Relation Management
rpc CreateRelation(CreateRelationRequest)
returns (CreateRelationResponse);
rpc GetRelations(GetRelationsRequest)
returns (GetRelationsResponse);
rpc BatchCreateRelations(BatchCreateRelationsRequest)
returns (BatchCreateRelationsResponse);
// Time Travel
rpc GetInstanceAtVersion(GetInstanceAtVersionRequest)
returns (GetInstanceResponse);
}
message CreateInstanceRequest {
com.onto.common.v1.RequestContext context = 1;
string object_type_id = 2;
google.protobuf.Struct attributes = 3;
repeated RelationInput relations = 4;
}
message GetInstanceRequest {
com.onto.common.v1.RequestContext context = 1;
string instance_id = 2;
repeated string select_properties = 3; // 字段投影
bool include_relations = 4;
bool include_derived = 5; // 是否包含派生属性
}
#3.3 共享类型:WorldContext 和 RequestContext
// proto/common/common.proto
message RequestContext {
string request_id = 1;
string user_id = 2;
repeated string roles = 3;
com.onto.common.v1.WorldContext world_context = 4;
string trace_id = 5;
string span_id = 6;
map<string, string> headers = 7;
}
所有 gRPC 请求的第一个参数都是 RequestContext,它内嵌 WorldContext。这保证了:
- 租户隔离:
tenant_id+org_id确保数据不泄漏 - 分支隔离:
world_id+branch_name确保操作在正确的世界分支 - 可追踪性:
trace_id+span_id支持 OpenTelemetry 分布式追踪 - 权限校验:
user_id+roles供 Policy Engine 鉴权
#4. 64 个 Proto 文件的命名空间组织
#4.1 按 Layer 划分的 Proto 分布
proto/
├── common/ [3 files]
│ ├── common.proto # 基础共享类型
│ ├── common_b.proto # Control Layer 扩展类型
│ └── errors.proto # 统一错误定义
│
├── plane_b/ [16 files]
│ ├── authentication_service.proto # 认证服务
│ ├── classification_service.proto # 数据分类
│ ├── connection_registry.proto # 连接注册
│ ├── dashboard_service.proto # 仪表盘
│ ├── interface_registry.proto # 接口类型注册
│ ├── ontology_proposal.proto # 本体变更提案
│ ├── policy_engine.proto # 策略引擎
│ ├── property_access.proto # 属性访问控制
│ ├── scenario_service.proto # 场景管理
│ ├── schema_registry.proto # Schema 注册
│ ├── struct_type.proto # 结构体类型
│ ├── user_management.proto # 用户管理
│ └── world_manager.proto # 世界管理
│
├── plane_c/ [15 files]
│ ├── data_ingestion.proto # 数据接入
│ ├── materialized_view.proto # 物化视图
│ ├── metric_registry.proto # 指标注册
│ ├── object_set.proto # 对象集
│ ├── ontology_change_event.proto # 本体变更事件
│ ├── ontology_edit.proto # 本体编辑
│ ├── ontology_onboarding.proto # 本体上线
│ ├── ontology_runtime.proto # 本体运行时(核心)
│ ├── query_federation.proto # 联邦查询
│ ├── resource_provisioning.proto # 资源供给
│ ├── storage_sync.proto # 存储同步
│ ├── temporal_query.proto # 时间查询
│ ├── time_series_store.proto # 时序存储
│ ├── timeseries_query.proto # 时序查询
│ └── vector_service.proto # 向量服务
│
├── plane_d/ [14 files]
│ ├── approval_service.proto # 审批服务
│ ├── decision_engine.proto # 决策引擎
│ ├── derived_property.proto # 派生属性
│ ├── function_runtime.proto # 函数运行时
│ ├── impact_assessment.proto # 影响评估
│ ├── lowcode_rule.proto # 低代码规则
│ ├── ml_model_service.proto # ML 模型服务
│ ├── oag.proto # OAG 图
│ ├── problem_type.proto # 问题类型
│ ├── rag_service.proto # RAG 服务
│ ├── reasoning_engine.proto # 推理引擎
│ ├── reasoning_subscription.proto # 推理订阅
│ ├── rule_script.proto # 规则脚本
│ ├── solver.proto # 求解器
│ └── temporal_query.proto # 时间查询
│
├── plane_e/ [5 files]
│ ├── action_engine.proto # 动作引擎
│ ├── aip_logic_workflow.proto # AIP 逻辑工作流
│ ├── approval_workflow.proto # 审批工作流
│ ├── mutation_rules.proto # 变更规则
│ └── notification.proto # 通知服务
│
├── plane_f/ [8 files]
│ ├── automate.proto # 自动化
│ ├── connection_management.proto # 连接管理
│ ├── dolphinscheduler_project.proto # DS 项目管理
│ ├── lineage.proto # 血缘追踪
│ ├── pipeline_engine.proto # 管道引擎
│ ├── scheduler.proto # 调度器
│ ├── transform_executor.proto # 转换执行器
│ └── trigger_rule.proto # 触发规则
│
└── plane_g/ [5 files]
├── audit_service.proto # 审计服务
├── lineage_service.proto # 血缘服务
├── metadata_service.proto # 元数据服务
├── replay_service.proto # 回放服务
└── workflow_lineage.proto # 工作流血缘
合计: 3 + 16 + 15 + 14 + 5 + 8 + 5 = 66 files
(含 common 的 3 个共享文件)
#4.2 命名约定
包命名: com.onto.{Layer}.v1
Java 包: com.onto.{Layer}.api.v1
文件命名: {service_name}.proto (小写下划线)
服务命名: {ServiceName}Service (大驼峰)
方法命名: {VerbNoun} (大驼峰,如 CreateInstance)
消息命名: {MethodName}Request / {MethodName}Response
#5. 代码生成管线
#5.1 Buf 构建配置
# buf.yaml
version: v1
name: buf.build/onto/platform
breaking:
use:
- FILE
lint:
use:
- DEFAULT
except:
- PACKAGE_VERSION_SUFFIX
# buf.gen.yaml
version: v1
plugins:
# Java gRPC stubs
- plugin: buf.build/protocolbuffers/java
out: control-Layer/src/main/java
- plugin: buf.build/grpc/java
out: control-Layer/src/main/java
# Python gRPC stubs
- plugin: buf.build/protocolbuffers/python
out: intelligence-Layer/gen
- plugin: buf.build/grpc/python
out: intelligence-Layer/gen
# Python type stubs (mypy)
- plugin: buf.build/community/nipunn1313-mypy
out: intelligence-Layer/gen
#5.2 生成产物示例
Java 侧(control-Layer/data-Layer):
// 自动生成的 gRPC Service 基类
public abstract class OntologyRuntimeServiceGrpc
.OntologyRuntimeServiceImplBase {
public void createInstance(
CreateInstanceRequest request,
StreamObserver<CreateInstanceResponse> responseObserver) {
// 待实现
}
public void getInstance(
GetInstanceRequest request,
StreamObserver<GetInstanceResponse> responseObserver) {
// 待实现
}
}
// 实现类
@GrpcService
public class OntologyRuntimeServiceImpl
extends OntologyRuntimeServiceGrpc
.OntologyRuntimeServiceImplBase {
@Override
public void createInstance(
CreateInstanceRequest request,
StreamObserver<CreateInstanceResponse> response) {
WorldContext world = request.getContext()
.getWorldContext();
// 1. 验证 WorldContext
worldValidator.validate(world);
// 2. Schema 校验
schemaRegistry.validateAttributes(
world, request.getObjectTypeId(),
request.getAttributes());
// 3. 写入 Doris
OntologyInstance instance = ontologyStore
.create(world, request);
// 4. 发布变更事件
eventPublisher.publish(
OntologyChangeEvent.of(instance));
response.onNext(CreateInstanceResponse.newBuilder()
.setInstance(instance.toProto())
.build());
response.onCompleted();
}
}
Python 侧(intelligence-Layer / python-sdk):
# 自动生成的 gRPC Client Stub
class OntologyRuntimeServiceStub:
def __init__(self, channel: grpc.Channel):
self.CreateInstance = channel.unary_unary(
'/com.onto.data.v1.OntologyRuntimeService/'
'CreateInstance',
request_serializer=CreateInstanceRequest
.SerializeToString,
response_deserializer=CreateInstanceResponse
.FromString,
)
# SDK 封装层
class GrpcOntologyClient:
"""Python SDK 中的 Ontology 客户端"""
def __init__(self, channel: grpc.Channel):
self._stub = OntologyRuntimeServiceStub(channel)
def get_instance(
self,
instance_id: str,
*,
select: list[str] | None = None,
include_relations: bool = False,
include_derived: bool = False,
) -> OntologyInstance:
request = GetInstanceRequest(
context=self._build_context(),
instance_id=instance_id,
select_properties=select or [],
include_relations=include_relations,
include_derived=include_derived,
)
response = self._stub.GetInstance(request)
return OntologyInstance.from_proto(
response.instance)
#6. 版本管理与向后兼容
#6.1 Proto 版本策略
v1 = 稳定版本(当前所有服务使用)
v2 = 下一代版本(规划中)
兼容性规则:
OK: 添加新字段(field number 不冲突即可)
OK: 添加新 RPC 方法
OK: 添加新枚举值
BAD: 删除或重命名字段
BAD: 更改字段类型
BAD: 更改字段编号
BAD: 删除 RPC 方法
#6.2 Buf Breaking Change 检测
# CI 流程中自动检测破坏性变更
$ buf breaking --against proto-baseline.binpb
# 如果通过,更新基线
$ buf build -o proto-baseline.binpb
#6.3 字段演进示例
// v1.0 - 初始版本
message WorldContext {
string world_id = 1;
string branch_name = 2;
WorldType type = 5;
}
// v1.1 - 添加多租户支持(兼容)
message WorldContext {
string world_id = 1;
string branch_name = 2;
optional string commit_hash = 3; // 新增
optional Timestamp as_of_timestamp = 4; // 新增
WorldType type = 5;
map<string, string> metadata = 6; // 新增
string tenant_id = 7; // 新增
string org_id = 8; // 新增
string project_id = 9; // 新增
WorldBranchType world_branch_type = 10; // 新增
string manifest_id = 11; // 新增
optional string compute_version = 12; // 新增
}
#7. gRPC 错误处理
#7.1 统一错误码体系
// proto/common/errors.proto
enum ErrorCode {
ERROR_CODE_UNSPECIFIED = 0;
// 通用错误 (1xxx)
ERROR_CODE_INTERNAL = 1000;
ERROR_CODE_INVALID_ARGUMENT = 1001;
ERROR_CODE_NOT_FOUND = 1002;
ERROR_CODE_ALREADY_EXISTS = 1003;
ERROR_CODE_PERMISSION_DENIED = 1004;
ERROR_CODE_UNAUTHENTICATED = 1005;
// World 错误 (2xxx)
ERROR_CODE_WORLD_NOT_FOUND = 2001;
ERROR_CODE_BRANCH_NOT_FOUND = 2002;
ERROR_CODE_BRANCH_CONFLICT = 2003;
ERROR_CODE_WORLD_LOCKED = 2004;
// Ontology 错误 (3xxx)
ERROR_CODE_SCHEMA_VIOLATION = 3001;
ERROR_CODE_INSTANCE_NOT_FOUND = 3002;
ERROR_CODE_RELATION_INVALID = 3003;
ERROR_CODE_DERIVED_COMPUTATION_FAILED = 3004;
// Pipeline 错误 (4xxx)
ERROR_CODE_PIPELINE_FAILED = 4001;
ERROR_CODE_TRANSFORM_ERROR = 4002;
ERROR_CODE_SCHEDULER_CONFLICT = 4003;
}
#7.2 错误传播机制
Java (Server) -> gRPC Status -> Python (Client)
Java:
throw new StatusRuntimeException(
Status.NOT_FOUND
.withDescription("Instance not found")
.augmentDescription("instance_id=" + id));
Python:
try:
response = stub.GetInstance(request)
except grpc.RpcError as e:
if e.code() == grpc.StatusCode.NOT_FOUND:
raise InstanceNotFoundError(
e.details()) from e
elif e.code() == grpc.StatusCode.PERMISSION_DENIED:
raise PermissionDeniedError(
e.details()) from e
#8. 流式 RPC:订阅与实时推送
#8.1 服务端流式(Server Streaming)
用于订阅本体变更事件:
// proto/plane_d/reasoning_subscription.proto
service ReasoningSubscriptionService {
// 订阅推理结果变更
rpc SubscribeReasoningResults(SubscribeRequest)
returns (stream ReasoningEvent);
// 订阅派生属性变更
rpc SubscribeDerivedPropertyChanges(
DerivedPropertySubscribeRequest)
returns (stream DerivedPropertyChangeEvent);
}
message SubscribeRequest {
RequestContext context = 1;
repeated string object_type_ids = 2; // 关注的类型
repeated string instance_ids = 3; // 关注的实例
EventFilter filter = 4; // 事件过滤
}
#8.2 双向流式(Bidirectional Streaming)
用于交互式推理对话:
service RagService {
// 交互式 RAG 对话
rpc Chat(stream ChatRequest)
returns (stream ChatResponse);
}
#8.3 与 WebSocket 的关系
外部客户端 (浏览器/SDK)
|
| WebSocket
v
+------------------+
| REST Facade |
| (Spring Gateway) |
+------------------+
|
| gRPC Server Streaming
v
+------------------+
| Reasoning & Decision Layer + Agent Runtime Layer |
| (Intelligence) |
+------------------+
WebSocket 是面向外部客户端的推送协议
gRPC Streaming 是内部 Layer 间的推送协议
REST Facade 负责协议转换
#9. REST Gateway:外部门面设计
#9.1 为什么需要 REST?
gRPC 对浏览器不友好(需要 gRPC-Web 代理),REST 仍然是外部 API 的事实标准。因此我们保留 REST 作为纯粹的外部门面。
#9.2 REST 端点映射
REST Facade 端点分布 (59 endpoints):
Ontology 操作 : 15 endpoints
POST /api/v1/ontology/instances
GET /api/v1/ontology/instances/{id}
PUT /api/v1/ontology/instances/{id}
PATCH /api/v1/ontology/instances/{id}
DELETE /api/v1/ontology/instances/{id}
POST /api/v1/ontology/instances/batch
GET /api/v1/ontology/instances/{id}/relations
...
Schema 管理 : 8 endpoints
GET /api/v1/schemas
POST /api/v1/schemas
GET /api/v1/schemas/{id}
PUT /api/v1/schemas/{id}/publish
...
World 管理 : 10 endpoints
GET /api/v1/worlds
POST /api/v1/worlds
POST /api/v1/worlds/{id}/branch
POST /api/v1/worlds/{id}/merge
...
推理与决策 : 8 endpoints
查询与分析 : 6 endpoints
Pipeline : 5 endpoints
用户与权限 : 4 endpoints
审计与治理 : 3 endpoints
#9.3 REST→gRPC 转换实现
// REST Controller (Spring Boot)
@RestController
@RequestMapping("/api/v1/ontology")
public class OntologyRestController {
private final OntologyRuntimeServiceGrpc
.OntologyRuntimeServiceBlockingStub stub;
@GetMapping("/instances/{id}")
public ResponseEntity<JsonNode> getInstance(
@PathVariable String id,
@RequestHeader("X-World-Id") String worldId,
@RequestHeader("X-Branch") String branch) {
// 1. 构建 gRPC Request
GetInstanceRequest request = GetInstanceRequest
.newBuilder()
.setContext(RequestContext.newBuilder()
.setWorldContext(WorldContext.newBuilder()
.setWorldId(worldId)
.setBranchName(branch)
.build())
.build())
.setInstanceId(id)
.build();
// 2. 调用 gRPC
GetInstanceResponse response =
stub.getInstance(request);
// 3. Proto -> JSON
return ResponseEntity.ok(
ProtoJsonConverter.toJson(
response.getInstance()));
}
}
#10. 跨语言调用的实际路径
#10.1 Python SDK -> Java 服务的完整调用链
Python SDK (用户代码)
|
| ontology.get_instance("obj_123")
v
GrpcOntologyClient (Python SDK 封装层)
|
| 构建 GetInstanceRequest Protobuf
v
grpcio Channel (Python gRPC 客户端)
|
| HTTP/2 + Protobuf 二进制
| 目标: onto-data:6668
v
Quarkus gRPC Server (onto-data.jar)
|
| 反序列化 Protobuf
v
OntologyRuntimeServiceImpl (Java 实现)
|
| 业务逻辑 + Doris 查询
v
GetInstanceResponse (Protobuf 响应)
|
| HTTP/2 + Protobuf 二进制
v
Python SDK (反序列化为 Pydantic 模型)
|
| OntologyInstance 返回给用户
v
用户代码
#10.2 Java -> Python 的调用(推理触发)
onto-data.jar (Ontology 变更触发)
|
| DerivedPropertyService.compute()
v
grpc-java Channel
|
| HTTP/2 + Protobuf
| 目标: onto-intelligence:6670
v
FastAPI gRPC Server (onto-intelligence)
|
| Python 推理引擎执行计算
v
DerivedPropertyResponse
|
| HTTP/2 + Protobuf
v
onto-data.jar (回写结果到 Doris)
#11. 性能优化实践
#11.1 连接池管理
// Java 侧 gRPC 连接池
@Configuration
public class GrpcChannelConfig {
@Bean
public ManagedChannel intelligenceChannel() {
return ManagedChannelBuilder
.forAddress("onto-intelligence", 6670)
.usePlaintext() // 内网通信
.maxInboundMessageSize(16 * 1024 * 1024)
.keepAliveTime(30, TimeUnit.SECONDS)
.keepAliveTimeout(10, TimeUnit.SECONDS)
.build();
}
}
# Python 侧 gRPC 连接池
import grpc
channel = grpc.insecure_channel(
'onto-data:6668',
options=[
('grpc.max_receive_message_length',
16 * 1024 * 1024),
('grpc.keepalive_time_ms', 30000),
('grpc.keepalive_timeout_ms', 10000),
('grpc.http2.max_pings_without_data', 0),
]
)
#11.2 批量操作优化
单条操作 vs 批量操作性能对比 (100 条记录):
模式 耗时 网络往返
--- ---- --------
100 x GetInstance 180ms 100 次
1 x BatchGetInstances 5ms 1 次
结论: 批量 RPC 减少 97% 的延迟
#11.3 Deadline 传播
// 设置 3 秒超时
stub.withDeadlineAfter(3, TimeUnit.SECONDS)
.getInstance(request);
// 如果超时,gRPC 自动取消下游所有调用
// 避免雪崩效应
#12. 与其他内部通信方案的对比
| 方案 | 延迟 | 吞吐量 | 类型安全 | 流式 | 跨语言 | 生态 |
|---|---|---|---|---|---|---|
| REST/JSON | 高 | 低 | 弱 | 无 | 优秀 | 最广 |
| gRPC/Protobuf | 低 | 高 | 强 | 原生 | 良好 | 广泛 |
| Thrift | 低 | 高 | 强 | 有限 | 良好 | 收窄 |
| GraphQL | 中 | 中 | 强 | 订阅 | 良好 | Web向 |
| NATS/消息队列 | 中 | 高 | 弱 | 原生 | 良好 | 异步 |
为什么选 gRPC 而非 Thrift?
- Google 背书,社区活跃度远超 Thrift
- 原生 HTTP/2,与云原生生态完美集成
- buf 工具链成熟(lint、breaking change 检测、代码生成)
- Kubernetes 服务网格原生支持 gRPC 负载均衡
#Key Takeaways
-
gRPC 不仅是性能选择,更是架构选择。Protobuf 契约文件就是 Layer 之间的"接口法典"——它们定义了数据结构、服务方法、错误码、版本演进规则。64 个 proto 文件覆盖了平台 100% 的内部交互。
-
Proto-First 开发工作流消除了跨语言的隐式约定。先定义 proto,再生成代码,最后实现——这个流程保证了 Java 服务端和 Python 客户端对同一接口有完全一致的理解,编译时就能发现不兼容。
-
REST 是门面,gRPC 是骨架。59 个 REST 端点不承载任何业务逻辑,它们只是 gRPC 调用的 HTTP 包装。这个分层让内部通信获得最大性能,同时对外保持 REST 的易用性。
“下一篇预告: [S2-03] 存储架构演进:从 9 个组件到 5 个的统一之路——深入 Apache Doris 如何同时承担 OLAP、向量搜索和全文检索,以及 Nessie+Iceberg 如何实现全量版本化。
Tags: #grpc #protobuf #rest #api-design #contract-driven #code-generation #coomia-dip #智策平台