4. 特征服务接口设计:RESTful与gRPC接口规范、请求/响应结构、超时与重试策略

在实时风控网关中,特征服务是核心数据供给层。接口设计的优劣直接决定了特征获取的延迟、吞吐量与稳定性。本章围绕 RESTful 与 gRPC 两种主流协议,从接口规范、请求/响应结构、超时与重试策略三个维度展开对比与实战设计。

4.1 接口协议选型对比

对比维度 RESTful (HTTP/JSON) gRPC (HTTP/2 + Protobuf)
传输协议 HTTP/1.1 或 HTTP/2 HTTP/2 (强制)
数据编码 JSON (文本,可读性强) Protobuf (二进制,体积小)
接口定义 OpenAPI / Swagger 文档 .proto 文件 (强类型契约)
流式支持 SSE / WebSocket (需额外实现) 原生 Server/Client/Bidirectional Streaming
延迟 较高 (JSON 序列化 + 文本传输) 较低 (二进制 + 头部压缩)
生态成熟度 极高,浏览器友好 高,微服务场景首选
风控场景适用性 外部 API、管理面、调试 内部高吞吐、低延迟特征计算
选型建议: 风控网关内部特征服务调用建议优先采用 gRPC;对外暴露的管理接口或第三方对接可使用 RESTful。混合架构中,网关层可同时暴露两种协议,内部路由统一走 gRPC。

4.2 RESTful 接口规范与请求/响应结构

4.2.1 接口设计原则

  • 资源路径: /v1/features/{feature_name} 或批量 /v1/features:batch
  • HTTP 方法: 仅使用 POST(避免 GET 请求体长度限制与缓存污染)
  • 版本控制: 路径前缀 /v1/、/v2/
  • 幂等性: 批量查询天然幂等,写入类特征需携带 idempotency-key

4.2.2 请求/响应结构示例

// 请求体 (POST /v1/features:batch)
{
  "request_id": "req-20250321-abc123",
  "entity_type": "user",
  "entity_ids": ["u1001", "u1002", "u1003"],
  "feature_keys": ["login_count_1d", "payment_amt_7d", "device_risk_score"],
  "context": {
    "source": "online_gateway",
    "timestamp": 1711000000000
  }
}

// 响应体
{
  "request_id": "req-20250321-abc123",
  "code": 0,
  "message": "success",
  "data": {
    "u1001": {
      "login_count_1d": 5,
      "payment_amt_7d": 12800.50,
      "device_risk_score": 0.23
    },
    "u1002": {
      "login_count_1d": 1,
      "payment_amt_7d": 0,
      "device_risk_score": 0.87
    },
    "u1003": {
      "error": "entity_not_found"
    }
  },
  "meta": {
    "total_entities": 3,
    "success_count": 2,
    "fail_count": 1,
    "elapsed_ms": 12
  }
}
关键字段说明:
request_id:全链路追踪 ID,必须透传;
entity_type + entity_ids:支持批量查询,减少网络开销;
meta.elapsed_ms:服务端处理耗时,用于客户端感知延迟。

4.3 gRPC 接口规范与请求/响应结构

4.3.1 Proto 文件定义

syntax = "proto3";

package risk.feature.v1;

service FeatureService {
  // 单实体特征查询
  rpc GetFeature(FeatureRequest) returns (FeatureResponse);

  // 批量实体特征查询(核心接口)
  rpc BatchGetFeatures(BatchFeatureRequest) returns (BatchFeatureResponse);

  // 服务端流式推送特征变更(可选)
  rpc WatchFeature(WatchRequest) returns (stream FeatureChangeEvent);
}

message FeatureRequest {
  string request_id = 1;
  string entity_type = 2;
  string entity_id = 3;
  repeated string feature_keys = 4;
  map<string, string> context = 5;
}

message BatchFeatureRequest {
  string request_id = 1;
  string entity_type = 2;
  repeated string entity_ids = 3;
  repeated string feature_keys = 4;
  map<string, string> context = 5;
}

message FeatureValue {
  oneof value {
    int64 int_val = 1;
    double double_val = 2;
    string string_val = 3;
    bool bool_val = 4;
  }
}

message EntityFeature {
  string entity_id = 1;
  map<string, FeatureValue> features = 2;
  string error = 3; // 空表示成功
}

message BatchFeatureResponse {
  string request_id = 1;
  int32 code = 2;
  string message = 3;
  repeated EntityFeature entities = 4;
  ResponseMeta meta = 5;
}

message ResponseMeta {
  int32 total_entities = 1;
  int32 success_count = 2;
  int32 fail_count = 3;
  int64 elapsed_ms = 4;
}

4.3.2 gRPC 优势在风控场景的体现

  • 强类型契约: 客户端与服务端共用 .proto,避免字段名拼写错误与类型不匹配
  • 二进制传输: 批量 1000 个实体特征时,Payload 体积约为 JSON 的 30%~40%
  • 流式推送: 特征变更实时推送,减少轮询开销
  • 内置超时与取消: gRPC 客户端原生支持 deadline 与 cancel
注意: gRPC 的负载均衡需客户端侧配合(如 pick_first 或 round_robin),且 HTTP/2 连接复用可能导致单个慢请求阻塞连接池,建议配合 连接池隔离 与 应用层超时 使用。

4.4 超时与重试策略设计

特征服务是风控决策链路的 关键依赖,超时与重试策略直接影响网关的 P99 延迟与可用性。

4.4.1 超时策略

层级 推荐值 说明
连接超时 500ms TCP 握手 + TLS 协商,超过则快速失败
请求超时 (RESTful) 2s 包含序列化、网络传输、服务端处理
请求超时 (gRPC) 1.5s 二进制传输更快,适当收紧
批量查询超时 3s 批量 1000 实体以内,超过则拆分或降级
总网关超时 5s 包含特征服务 + 规则引擎 + 外部依赖

4.4.2 重试策略

  • 重试条件: 仅对 5xx(服务端错误)与 UNAVAILABLE(gRPC 状态码)重试;4xx 或 INVALID_ARGUMENT 不重试
  • 最大重试次数: 2 次(含首次共 3 次请求)
  • 退避算法: 指数退避 + 随机抖动,初始间隔 200ms,最大间隔 1s
  • 重试预算: 基于时间窗口(如 10s 内最多重试 5 次),防止重试风暴
  • 幂等性保障: 重试请求携带相同 request_id,服务端去重
// 重试策略伪代码 (Go 风格)
retryPolicy := &RetryPolicy{
    MaxAttempts:      3,
    InitialBackoff:   200 * time.Millisecond,
    MaxBackoff:       1 * time.Second,
    BackoffFactor:    2.0,
    JitterRate:       0.2,
    RetryableCodes:   []int{500, 502, 503, 504}, // RESTful
    // gRPC: codes.Unavailable, codes.DeadlineExceeded
    PerTryTimeout:    2 * time.Second,
    Budget:           &Budget{Window: 10 * time.Second, MaxRetries: 5},
}
实战要点:
1. 重试必须配合 快速失败(Fail Fast),避免长时间阻塞网关线程;
2. 特征服务应提供 降级值(如默认 0 或空值),网关在超时/重试耗尽后使用降级值继续决策,而非直接拒绝;
3. 监控重试率与降级率,超过阈值触发告警。

4.5 接口调用流程总览

特征服务接口调用流程(RESTful / gRPC 双协议) 风控网关 协议适配层 (REST / gRPC) 超时控制 (1.5s~3s) 重试策略 (指数退避 + 预算) 特征服务集群 (gRPC 优先) 降级处理 (默认值 / 缓存) 网关决策引擎 告警 & 熔断 说明: 1. 网关发起请求,协议适配层 选择 REST 或 gRPC 调用 2. 超时控制 & 重试在客户端完成

4.6 接口设计检查清单

  • ✅ 统一 request_id: 全链路透传,用于日志关联与去重
  • ✅ 批量接口优先: 减少网络往返,单次请求携带多个实体
  • ✅ 明确的错误码: 区分业务错误(实体不存在)与系统错误(超时、熔断)
  • ✅ 超时分层: 连接超时 < 请求超时 < 网关总超时
  • ✅ 重试预算: 防止级联重试导致特征服务雪崩
  • ✅ 降级默认值: 每个特征都有安全默认值,确保决策不断流
  • ✅ 协议可观测: 记录 REST 与 gRPC 各自的延迟、成功率、重试率
核心结论: 特征服务接口设计不是简单的“选 REST 还是 gRPC”,而是要在 协议规范、数据结构、超时控制、重试降级 四个维度形成统一治理体系。gRPC 适合内部高吞吐场景,RESTful 适合外部生态对接,两者通过网关协议适配层共存,是实时风控网关的最佳实践。