4、流量路由基础:基于权重的路由、基于标签的路由、基于用户ID/策略ID的哈希路由

在量化策略灰度发布体系中,流量路由是连接“策略版本”与“真实用户/订单”的核心桥梁。灰度发布并非简单地将流量切分,而是需要根据不同的业务场景,将流量精确地导向不同的策略版本。本章将深入剖析三种最基础、最常用的路由模式:基于权重、基于标签和基于哈希的路由。

4.1 路由模式总览

三种路由模式分别解决不同维度的流量调度问题。下表从核心原理、适用场景和典型配置三个维度进行对比:

路由模式 核心原理 适用场景 典型配置
基于权重 按比例随机分配流量 A/B测试、新策略小流量验证、性能压测 v1: 90%, v2: 10%
基于标签 根据请求携带的元数据(标签)匹配 内部员工灰度、VIP用户优先体验、地域灰度 env=internal → v2
基于哈希 对用户ID/策略ID进行一致性哈希 会话保持、用户粒度灰度、策略版本绑定 hash(user_id) % 100
架构选型建议:在实际生产环境中,这三种模式通常不是互斥的,而是组合使用。例如:先通过标签路由将内部员工流量导入新版本,再对普通用户使用权重路由进行小流量验证,最后通过哈希路由实现用户粒度的稳定灰度。

4.2 基于权重的路由

基于权重的路由是最直观的流量分配方式。灰度发布平台为每个策略版本分配一个权重值(通常为百分比),路由引擎根据权重比例随机地将请求转发到对应版本。

4.2.1 权重路由模型

下图展示了权重路由的核心逻辑:

基于权重的流量路由模型 流量入口 权重路由引擎 随机数生成器 策略版本 A 权重: 70% 策略版本 B 权重: 30% 说明:每次请求到达时,路由引擎生成一个 [0, 100) 的随机数。若随机数 < 70,则路由到版本 A;否则路由到版本 B。

4.2.2 代码实现示例

以下是一个简化的权重路由实现,使用 Go 语言编写:

// WeightedRouter 基于权重的路由选择器
type WeightedRouter struct {
    versions []VersionWeight
    totalWeight int
}

type VersionWeight struct {
    Version string
    Weight  int // 权重值,如 70
}

func (r *WeightedRouter) SelectVersion() string {
    randNum := rand.Intn(r.totalWeight) // 生成 [0, totalWeight) 随机数
    cumulative := 0
    for _, vw := range r.versions {
        cumulative += vw.Weight
        if randNum < cumulative {
            return vw.Version
        }
    }
    return r.versions[len(r.versions)-1].Version
}

// 初始化:v1=70%, v2=30%
router := &WeightedRouter{
    versions: []VersionWeight{
        {"v1", 70},
        {"v2", 30},
    },
    totalWeight: 100,
}
注意:权重路由的随机性可能导致流量分布不均匀,尤其是在短时间窗口内。对于需要严格按比例分配的场景,建议使用令牌桶或滑动窗口算法进行平滑处理。

4.3 基于标签的路由

基于标签的路由允许灰度发布平台根据请求中携带的元数据(如用户等级、设备类型、地域、渠道等)进行精细化路由。这种模式常用于内部灰度验证和定向放量。

4.3.1 标签路由流程

下图展示了标签路由的完整执行流程:

基于标签的流量路由流程 用户请求 标签: role=admin 标签提取器 规则匹配引擎 role=admin → v2 策略版本 v2 规则配置示例: 标签: role=admin → 版本 v2 | 标签: region=cn-east → 版本 v1 | 默认 → 版本 v1

4.3.2 标签路由配置表

在实际系统中,标签路由规则通常以配置表的形式存储,支持动态更新:

优先级 标签键 标签值 目标版本 说明
1 role admin v2 (灰度版) 内部管理员优先体验
2 user_level vip v2 (灰度版) VIP用户定向灰度
3 region cn-east v1 (稳定版) 特定地域保持稳定
4 * * v1 (稳定版) 默认路由到稳定版
最佳实践:标签路由规则应支持优先级和默认兜底。当请求携带多个标签时,按优先级最高的规则执行;若所有规则均不匹配,则路由到默认版本,确保系统不会出现“无版本可用”的情况。

4.4 基于用户ID/策略ID的哈希路由

哈希路由通过计算用户ID或策略ID的哈希值,将流量稳定地映射到特定版本。其核心优势在于会话保持:同一个用户或同一个策略ID的请求始终路由到同一个版本,避免因版本切换导致的状态不一致问题。

4.4.1 一致性哈希路由模型

下图展示了基于用户ID的哈希路由模型:

基于用户ID的哈希路由模型 用户ID user_12345 哈希函数 hash(user_id) 取模运算 % 100 版本 v1 哈希值: 0-69 版本 v2 哈希值: 70-99 关键特性:同一个用户ID的哈希值始终相同,因此每次请求都会路由到同一个版本,实现用户粒度的会话保持。

4.4.2 哈希路由代码实现

// HashRouter 基于用户ID的哈希路由
type HashRouter struct {
    versionRanges map[string][2]int // 版本 -> [起始, 结束) 哈希区间
}

func (r *HashRouter) SelectVersion(userID string) string {
    hashVal := int(crc32.ChecksumIEEE([]byte(userID))) % 100
    for version, rng := range r.versionRanges {
        if hashVal >= rng[0] && hashVal < rng[1] {
            return version
        }
    }
    return "v1" // 默认版本
}

// 初始化:v1 占 70%,v2 占 30%
router := &HashRouter{
    versionRanges: map[string][2]int{
        "v1": {0, 70},
        "v2": {70, 100},
    },
}

// 使用示例
version := router.SelectVersion("user_12345") // 始终返回 v1 或 v2

4.4.3 哈希路由 vs 权重路由

对比维度 哈希路由 权重路由
确定性 确定:同一ID始终路由到同一版本 随机:同一请求可能路由到不同版本
会话保持 天然支持,无需额外状态 不支持,需要外部存储记录
流量分布 取决于ID分布,可能不均匀 严格按比例分布
版本扩缩容 需要重新哈希,可能影响大量用户 直接调整权重,影响平滑
适用场景 用户粒度灰度、策略版本绑定 A/B测试、小流量验证
工程实践:在量化策略灰度中,推荐使用策略ID作为哈希键。因为策略ID通常与交易标的或策略逻辑绑定,同一个策略ID的订单路由到同一个版本,可以避免因版本切换导致的策略信号不一致问题。

4.5 三种路由模式的组合策略

在实际的量化策略灰度发布系统中,单一路由模式往往无法满足所有需求。以下是一个典型的组合路由策略示例:

  1. 第一层:标签路由 — 检查请求是否携带内部灰度标签(如 env=staging),若是则直接路由到最新灰度版本。
  2. 第二层:哈希路由 — 对普通用户,根据用户ID进行哈希路由,确保用户粒度的会话保持。
  3. 第三层:权重路由 — 在哈希路由的基础上,通过调整哈希区间(即权重)来控制灰度比例。

这种组合策略既保证了灰度验证的灵活性,又实现了用户粒度的稳定性,是生产环境中最推荐的架构模式。