第十三章:API网关与服务治理
说到API网关和服务治理,我脑子里第一个蹦出来的画面,是几年前一次线上事故。
那天下午,我们的做市系统突然响应变慢,交易员在群里疯狂@我。查了半天才发现,是某个底层服务挂了,结果所有请求都堵在网关层,最后把整个系统拖垮了。嗯,从那以后,我对API网关和服务治理的重视程度,直接拉满了。
13.1 RESTful API设计:别小看接口规范
RESTful API,说白了就是一套约定。你按规矩来,大家都省心。我在项目中见过太多"自由发挥"的接口设计,结果维护成本高得吓人。
核心原则:
- 资源路径用名词,别用动词。比如
/orders而不是/getOrders - HTTP动词对应操作:GET查、POST增、PUT全量改、PATCH部分改、DELETE删
- 状态码要准确:200成功、201创建、400参数错误、404不存在、500服务器挂了
举个例子,我们做市系统的订单接口:
# 好的设计
GET /api/v1/orders # 查询订单列表
POST /api/v1/orders # 创建新订单
GET /api/v1/orders/{id} # 查询单个订单
PUT /api/v1/orders/{id} # 全量更新订单
PATCH /api/v1/orders/{id} # 部分更新订单
DELETE /api/v1/orders/{id} # 删除订单
# 坏的设计(我见过有人这么写)
GET /api/v1/getOrderList
POST /api/v1/createOrder
POST /api/v1/deleteOrder?id=123
避坑指南:我曾经接手过一个项目,接口路径里混着中英文和下划线,比如 /get_订单List。嗯,你想想看,这种接口谁敢用?所以从一开始就定好规范,比事后重构省心一百倍。
13.2 gRPC通信:高性能的另一种选择
RESTful API虽然好,但有些场景它确实不够快。比如做市系统里,行情数据推送、订单状态同步,这些对延迟极度敏感。这时候,gRPC就派上用场了。
gRPC基于HTTP/2,用Protobuf做序列化。我个人的习惯是:对外暴露的接口用RESTful,内部服务间通信用gRPC。这样既保证了外部兼容性,又兼顾了内部性能。
一个典型的gRPC服务定义:
syntax = "proto3";
package marketmaker;
service OrderService {
rpc CreateOrder (CreateOrderRequest) returns (OrderResponse);
rpc GetOrder (GetOrderRequest) returns (OrderResponse);
rpc SubscribeOrderStatus (SubscribeRequest) returns (stream OrderStatus);
}
message CreateOrderRequest {
string symbol = 1;
double price = 2;
double quantity = 3;
string side = 4; // buy or sell
}
message OrderResponse {
string order_id = 1;
string status = 2;
double filled_quantity = 3;
}
为什么选gRPC?
- 序列化快:Protobuf比JSON小3-10倍
- 支持双向流:适合行情推送这种场景
- 强类型:接口定义即文档,不会出现"这个字段是啥意思"的尴尬
我在项目中遇到过一个问题:gRPC的负载均衡不像HTTP那么简单。因为gRPC长连接的特性,普通的轮询负载均衡反而会导致连接分布不均。后来我们用了客户端负载均衡,配合服务发现,才解决了这个问题。
13.3 服务注册发现:让服务找到彼此
微服务架构里,服务实例的IP和端口是动态变化的。你不能把地址写死在配置文件里,对吧?服务注册发现就是解决这个问题的。
常见的方案有Consul、Etcd、Zookeeper。我个人偏爱Consul,因为它自带健康检查和DNS接口,用起来比较顺手。
服务注册发现的工作流程:
- 服务启动时,向注册中心注册自己的地址和端口
- 注册中心定期检查服务健康状态
- 客户端从注册中心获取可用服务列表
- 客户端根据策略选择一个实例发起调用
注意:我曾经踩过一个坑——服务注册成功了,但健康检查配置得太宽松。结果一个服务已经半死不活了,注册中心还认为它活着,导致大量请求超时。健康检查的间隔和超时时间,一定要根据业务场景仔细调。
下面这张图展示了服务注册发现的核心逻辑:
13.4 限流熔断:保护系统不被冲垮
做市系统最怕什么?怕流量洪峰。行情剧烈波动时,订单量可能是平时的几十倍。如果没有限流熔断,系统分分钟被冲垮。
限流和熔断是两个概念,但经常一起用:
- 限流:控制请求速率,防止系统过载。比如每秒最多处理1000个订单请求。
- 熔断:当某个服务故障率过高时,暂时切断对该服务的调用,避免故障扩散。
常用的限流算法:
| 算法 | 原理 | 适用场景 |
|---|---|---|
| 令牌桶 | 以固定速率往桶里放令牌,请求来了拿令牌 | 允许突发流量,适合做市系统 |
| 漏桶 | 请求先进桶,以固定速率流出 | 平滑流量,适合消息队列 |
| 滑动窗口 | 统计时间窗口内的请求数 | 实现简单,精度可控 |
我个人习惯用令牌桶算法。为什么呢?因为做市系统有时候需要处理突发订单,比如大单拆分后同时涌入。令牌桶允许一定程度的突发,正好满足这个需求。
熔断器的状态机:
状态:CLOSED → OPEN → HALF_OPEN → CLOSED
CLOSED(关闭):正常状态,请求正常通过
OPEN(打开):故障率超阈值,直接拒绝请求
HALF_OPEN(半开):过一段时间后,放少量请求试探
避坑指南:我曾经把熔断阈值设得太低,结果一次小波动就触发了熔断,导致大量正常请求被拒绝。后来我学乖了——阈值要根据历史数据动态调整,不能拍脑袋定死。
13.5 网关层的统一处理
API网关不只是做路由转发。它还可以统一处理很多事情:
- 认证鉴权:在网关层统一校验Token,不用每个服务都写一遍
- 日志记录:记录所有请求的入参、出参、耗时
- 协议转换:对外暴露RESTful,内部转成gRPC
- 灰度发布:根据Header或Cookie,把请求路由到不同版本的服务
我们用的网关是自研的,基于Netty做了一层封装。为什么不用现成的?因为做市系统对延迟要求太高,现成网关的某些功能我们用不上,反而增加了延迟。
我的建议:网关层尽量做"薄"一点。只做路由、认证、限流这些通用功能。业务逻辑不要放在网关里,否则网关会变得越来越重,最后变成"上帝服务",谁都依赖它,谁都改不动它。
好了,API网关和服务治理这块,核心就是这些。从接口设计到通信协议,从服务发现到限流熔断,每一环都关系到系统的稳定性和性能。做市系统对稳定性的要求极高,这些治理手段不是锦上添花,而是保命用的。
交易系统化学习资料 微信Strategy888888