11. API网关与认证:RESTful API设计,WebSocket API,API Key与JWT认证,速率限制

做衍生品做市商系统,说白了就是跟市场抢时间。你的API设计得好不好,直接决定了交易员能不能在微秒级别内完成下单、撤单、查持仓。我见过不少团队,策略写得漂亮,结果API层拖了后腿,整个系统就像跑车装了自行车链条——白搭。

今天咱们聊聊API网关和认证这块。嗯,这部分内容其实挺杂的,但我会尽量讲得有条理。你想想看,一个做市商系统每天要处理几百万甚至上千万的请求,如果没有一个好的API网关,那简直就是灾难。

核心要点:API网关是做市商系统的交通枢纽,认证是安全的第一道防线。两者缺一不可。

11.1 RESTful API设计:别把接口搞得太花哨

我个人习惯,RESTful API的设计原则就是「简单、一致、可预测」。做市商系统里,我们主要处理订单、持仓、行情、账户这几类资源。每个资源对应一套标准的CRUD操作。

举个例子,订单管理的API我一般这么设计:

# 订单相关API
POST   /api/v1/orders          # 创建订单
GET    /api/v1/orders          # 查询订单列表
GET    /api/v1/orders/{id}     # 查询单个订单
DELETE /api/v1/orders/{id}     # 撤销订单
PUT    /api/v1/orders/{id}     # 修改订单(部分交易所支持)

# 持仓相关API
GET    /api/v1/positions       # 查询持仓
GET    /api/v1/positions/{symbol}  # 查询某个品种的持仓

# 行情相关API
GET    /api/v1/market/ticker   # 获取最新行情
GET    /api/v1/market/orderbook/{symbol}  # 获取深度数据

这里要注意几个坑。我曾经在项目里遇到过,有人把订单创建设计成GET请求,理由是「方便测试」。嗯,这绝对是个坏习惯。创建资源必须用POST,这是RESTful的基本约定。还有,版本号一定要放在路径里,比如/api/v1/,这样以后升级接口不会影响老用户。

小技巧:响应格式统一用JSON,错误码也要标准化。我一般用这样的结构:{ "code": 0, "msg": "success", "data": {...} }。code为0表示成功,非0表示具体错误类型。

11.2 WebSocket API:实时性才是做市商的命根子

做市商系统里,RESTful API只适合做查询和操作类的请求。真正要命的是行情推送、成交回报、持仓变动这些实时数据。这时候就得靠WebSocket了。

我建议把WebSocket设计成订阅-推送模式。客户端先订阅感兴趣的主题,服务端有数据更新就主动推过来。这样比轮询REST接口高效得多。

# WebSocket消息格式示例
# 订阅请求
{
  "type": "subscribe",
  "channel": "orderbook.btc-usdt",
  "depth": 10
}

# 推送数据
{
  "type": "push",
  "channel": "orderbook.btc-usdt",
  "data": {
    "bids": [[50000, 1.5], [49990, 2.0]],
    "asks": [[50010, 1.2], [50020, 3.0]],
    "timestamp": 1700000000000
  }
}

# 取消订阅
{
  "type": "unsubscribe",
  "channel": "orderbook.btc-usdt"
}

我记得有一次,团队里有人把WebSocket当REST用,每次请求都重新建立连接。结果服务器压力巨大,连接数暴涨。其实WebSocket是长连接,建立一次就够了,后续所有通信都复用这个连接。这个坑踩过之后,我就在代码里加了连接池管理。

注意:WebSocket连接一定要有心跳机制。我一般设置30秒一次心跳,如果连续3次没收到响应,就判定连接断开,触发重连逻辑。不然客户端那边还以为连接好好的,实际上数据早就断了。

11.3 API Key与JWT认证:两种方案,各有千秋

认证这块,做市商系统里最常见的就是API Key和JWT。我两种都用过,说说我的感受。

API Key 适合机器对机器的场景。比如你的策略服务器要连接交易所,用API Key + Secret Key的方式签名请求。优点是简单直接,缺点是Key一旦泄露,别人就能冒充你操作。

# API Key签名示例(Python)
import hmac
import hashlib
import time

def sign_request(api_secret, method, path, body=""):
    timestamp = str(int(time.time() * 1000))
    message = timestamp + method + path + body
    signature = hmac.new(
        api_secret.encode(),
        message.encode(),
        hashlib.sha256
    ).hexdigest()
    return signature

# 使用
api_key = "your_api_key"
api_secret = "your_api_secret"
signature = sign_request(api_secret, "POST", "/api/v1/orders", '{"symbol":"BTC-USDT","side":"buy","price":50000}')
headers = {
    "X-API-Key": api_key,
    "X-Signature": signature,
    "X-Timestamp": str(int(time.time() * 1000))
}

JWT 更适合用户登录场景。比如你的交易员要登录Web端查看持仓,用用户名密码换取JWT Token,后续请求带上这个Token就行。JWT的好处是自包含,服务端不需要存session,减轻了数据库压力。

# JWT生成示例(Python)
import jwt
import datetime

def generate_jwt(user_id, role, secret_key):
    payload = {
        "user_id": user_id,
        "role": role,
        "exp": datetime.datetime.utcnow() + datetime.timedelta(hours=2),
        "iat": datetime.datetime.utcnow()
    }
    token = jwt.encode(payload, secret_key, algorithm="HS256")
    return token

# 验证JWT
def verify_jwt(token, secret_key):
    try:
        payload = jwt.decode(token, secret_key, algorithms=["HS256"])
        return payload
    except jwt.ExpiredSignatureError:
        return None  # Token过期
    except jwt.InvalidTokenError:
        return None  # Token无效

我的建议:做市商系统里,API Key用于策略服务器与交易所之间的通信,JWT用于用户前端与后端之间的通信。两者可以共存,互不冲突。

11.4 速率限制:别让一个客户拖垮整个系统

速率限制(Rate Limiting)是做市商系统里必不可少的一环。你想想看,如果某个客户的策略出了bug,疯狂发请求,那其他客户的交易就会受影响。我见过最夸张的一次,一个客户每秒发了10万次请求,直接把我们的网关打挂了。

常用的速率限制算法有几种:

算法 原理 适用场景
令牌桶 以固定速率往桶里放令牌,请求消耗令牌 允许突发流量,适合做市商系统
漏桶 请求先进入桶里,以固定速率流出 严格限制请求速率,适合对稳定性要求高的场景
滑动窗口 统计时间窗口内的请求数,超过则拒绝 实现简单,适合大多数场景

我个人习惯用令牌桶算法。为什么呢?因为做市商系统有时候需要突发流量。比如行情剧烈波动时,策略需要快速下多笔订单。令牌桶允许你积累一些令牌,关键时刻能爆发一下。

# 令牌桶算法实现(Python)
import time
import threading

class TokenBucket:
    def __init__(self, rate, capacity):
        self.rate = rate          # 令牌生成速率(个/秒)
        self.capacity = capacity  # 桶容量
        self.tokens = capacity    # 当前令牌数
        self.last_refill = time.time()
        self.lock = threading.Lock()
    
    def consume(self, tokens=1):
        with self.lock:
            self._refill()
            if self.tokens >= tokens:
                self.tokens -= tokens
                return True
            return False
    
    def _refill(self):
        now = time.time()
        elapsed = now - self.last_refill
        self.tokens = min(self.capacity, self.tokens + elapsed * self.rate)
        self.last_refill = now

# 使用示例:每个API Key限制每秒100次请求
bucket = TokenBucket(rate=100, capacity=100)
if bucket.consume():
    # 处理请求
    pass
else:
    # 返回429 Too Many Requests
    pass

注意:速率限制一定要按API Key来区分,不能全局限制。不然一个客户出问题,所有客户都受影响。另外,返回429状态码时,最好在响应头里告诉客户端什么时候可以重试,比如Retry-After: 1

11.5 知识体系总览

说了这么多,我画了一张图帮你梳理一下本章的知识结构。你看,API网关就像个守门员,负责接收请求、做认证、限流,然后把请求转发到后端服务。RESTful和WebSocket是两种不同的通信方式,API Key和JWT是两种认证手段,速率限制是保护系统的最后一道防线。

API网关与认证知识体系 API网关 RESTful API WebSocket API 认证(API Key / JWT) 速率限制 订单/持仓/行情接口 行情推送/成交回报 令牌桶/漏桶/滑动窗口 429状态码/Retry-After API网关统一入口,四大模块协同工作,保障系统安全稳定

好了,这一章的内容就到这里。API网关和认证这块,说白了就是「入口要统一,安全要到位,速度要够快」。做市商系统里,每一毫秒都很珍贵,所以设计API时一定要考虑性能。我见过太多系统,功能做得很全,但接口响应慢得像蜗牛,最后没人愿意用。

记住一句话:好的API设计,是让调用者感觉不到它的存在。它就在那里,稳定、快速、可靠。


交易系统化学习资料 微信Strategy888888