一. 服务流量牵引的背景 #
1.1 流量类型属性 #
南北流量:从网关到集群应用的流量既客户端浏览器和服务器之间的流量被称为南北流量。
东西流量:应用到服务之间,服务到服务之间的流量,即不同服务器之间互相调用的流量。
1.2 核心概念说明 #

二. 东西方向流量负载均衡模式 #
2.1 默认负载均衡 #
Mesh架构采用客户端负载均衡策略,根据sidecar启动时上报到动态注册中心的weight计算流量比例,weight值从环境变量里读取 IDG_WEIGHT,当环境变量里缺少此配置或此配置小于等于0时,默认weight=10

2.2 流量牵引 - 两级负载均衡 #
配置流量牵引规则后,兼容Mesh默认负载均衡策略,流量首先经过流量牵引规则计算当匹配到多个目标实例时再根据系统默认策略计算具体实例

服务发布使用金丝雀、蓝绿、AB等策略时,防止部署的新版本一上线立马接收流量,需要在部署前先配置流量牵引策略,首先把所有流量牵引到正常版本,新版本部署完成后逐步调整流量比例,牵引流程:
配置流量牵引规则 => 部署新版本 => 调整流量比例 => 下线旧版本 => 删除流量牵引规则
三. 流量牵引模式支撑范围 #
3.1 金丝雀发布 #
3.1.1 简介 #
A => B,B发布V2版本后,牵引10%流量到V2版本,经过测试V2版本正常后,不断增大V2版本副本数及流量比例直到将100%的流量都切换到新版本上,最后关闭剩下的老版本服务,完成金丝雀发布。

3.1.2 发布流程 #
- 未部署v2.0版本之前,先配置流量牵引规则,调用控制面
POST/govern/router接口,新增或修改规则,规则配置完成后此时尚未部署v2.0版本,流量依然全部走v1.0版本
{
"to_unique_id": "testBBB",
"last_update_time": 1618458126111,
"routers": [
{
"match": {
"prefix": "*" # 匹配所有流量
},
"routes": [
{
"metadata": {
"version": "v1.0"
},
"weight": 90 # v1.0版本承载90%
},
{
"metadata": {
"version": "v2.0"
},
"weight": 10 # v2.0版本承载10%
}
]
}
]
}
prefix: "*"代表匹配所有流量route.metadata.version匹配条件,route.weight负载均衡权重- 满足
route.metadata.version:v2.0匹配条件,负载10%流量 - 控制面动态调整配置参数,v2.0版本 weight ==» 100 v1版本 weight ==» 0 发布完成
- 部署v2.0版本,部署成功上线后会自动负载10%流量,逐步调用控制面
POST/govern/router接口,调整v2.0负载权重
{
"to_unique_id": "testBBB",
"last_update_time": 1618458126111,
"routers": [
{
"match": {
"prefix": "*" # 匹配所有流量
},
"routes": [
{
"metadata": {
"version": "v1.0"
},
"weight": 60 # v1.0版本承载60% ===> 0
},
{
"metadata": {
"version": "v2.0"
},
"weight": 40 # v2.0版本承载10% ===> 100
}
]
}
]
}
- 流量全部平滑牵引到v2.0版本后,下线所有v1.0版本pod,下线完成后调用控制面
DELETE/govern/router/{to_unique_id}删除流量牵引规则,走默认负载策略 - 金丝雀发布完成
3.1.3 平台配置示例 #

3.2 蓝绿发布 #
3.2.1 简介 #
所谓蓝绿部署,是指同时运行两个版本的应用,如上图所示,蓝绿部署的时候,并不停止掉老版本,而是直接部署一套新版本,等新版本运行起来后,再将流量切换到新版本上。其间发现流量异常时再把流量切换回原先版本

3.2.2 发布流程 #
- 未部署v2.0版本之前,先配置流量牵引规则,调用控制面
POST/govern/router接口,新增或修改规则,配置规则全部走v1.0版本
{
"to_unique_id": "testBBB",
"last_update_time": 1618458126222,
"routers": [
{
"match": {
"prefix": "*" # 匹配所有流量
},
"routes": [
{
"metadata": {
"version": "v1.0" # 切换版本动态调整流量
},
"weight": 100 # v1.0版本承载100%
}
]
}
]
}
prefix: "*"代表匹配所有流量route.metadata.version匹配条件,route.weight负载均衡权重- 满足
route.metadata.version:v1.0匹配条件,负载100%流量 - 控制面动态调整配置参数,在v1.0和v2.0版本之间切换
- 部署v2.0版本,部署完成后调用控制面
POST/govern/router接口,修改规则,配置规则全部走v2.0版本
{
"to_unique_id": "testBBB",
"last_update_time": 1618458126333,
"routers": [
{
"match": {
"prefix": "*" # 匹配所有流量
},
"routes": [
{
"metadata": {
"version": "v2.0" # 切换版本动态调整流量
},
"weight": 100 # v1.0版本承载100%
}
]
}
]
}
- 当发现流量异常时,调用控制面
POST/govern/router接口,修改规则,配置规则还原回v1.0版本 - 无异常则下线v1.0版本pod,下线完成后调用控制面
DELETE/govern/router/{to_unique_id}删除流量牵引规则,走默认负载策略 - 蓝绿发布完成
3.2.3 平台配置示例 #

3.3 AB测试 #
3.3.1 简介 #
A => B,B发布V2.0版本后,配置部分特征流量使用V2;例:请求中 appkey=test, channel=99 的流量请求到V2.0, 其余请求到V1.0

3.3.2 发布流程 #
- 未部署v2.0版本之前,先配置流量牵引规则,调用控制面
POST/govern/router接口,新增或修改规则,配置规则全部走v1.0版本
{
"to_unique_id":"testBBB",
"last_update_time": 1600000123123,
"routers": [
{
"match":{
"prefix": "*"
},
"route":[
{
"metadata":{
"version":"v1.0" # 全部流量走v1.0版本
}
}
]
}
]
}
- 部署v1.0版本,部署成功后调用控制面
POST/govern/router接口,修改流量牵引规则,特定头部走v2.0版本- match匹配规则支持多种匹配,可自行调控规则匹配特征流量
{
"to_unique_id":"testBBB",
"last_update_time": 1600000123123,
"routers": [
{
"match":{
"headers":[
{
"name": "x-appkey",
"value": "test",
"regex": false // value是否是个正则表达式
},
{
"name": "x-channel",
"value": "99",
"regex": false // value是否是个正则表达式
},
]
},
"route":[
{
"metadata":{
"version":"v2.0" # 匹配到的特征流量走v2.0版本
}
}
]
}
]
}
- 测试无误后全部流量切换到v2.0 版本
- 下线v1.0版本后删除流量牵引规则,AB测试发布完成
3.3.3 平台配置示例 #

四. 流量牵引配置规则调控 #
4.1 业务方通过控制面接口调控流量 #
控制面接口文档:http://192.168.2.80:8111/swagger/index.html

4.2 通过平台调控流量 #
服务运行时可以通过平台动态配置调控规则,满足运营阶段需求

4.2.1 金丝雀配置示例 #

4.2.2 蓝绿发布 #

4.2.3 AB发布 #

五. 内部相关实现 #
5.1 流量牵引配置说明 #
5.1.1 Router 配置 #
{
"to_unique_id":"aaa",
"last_update_time": 1600000123123,
"routers": [
{
"match":{
"prefix":"*",
"path":"",
"regex":"",
"headers": [
{
"name":"",
"value":"",
"regex":""
}
]
},
"routes":[
{
"metadata":{
"version":"v1.0.1",
"runtime": "production"
},
"weight":10
},
{
"metadata":{
"version":"v1.0.1",
"runtime": "production"
},
"weight":20
}
]
}
]
}
last_update_time,配置最后变更时间,毫秒时间戳to_unique_id,请求目标服务unique_idrouters,描述具体的路由规则细节。
5.1.2 Routers #
{
"match":{},
"route":{}
}
match,访问目标服务时,客户端解析请求url,当符合匹配条件时,根据对应route规则转发流量route,描述目标服务具体信息
5.1.3 Match #
{
"prefix":"",
"path":"",
"regex":"",
"headers": []
}
- 路径(path)匹配
prefix,表示路由会匹配 path 的前缀,该配置的优先级高于 path 和 regex。 如果 prefix 被配置,那么请求首先要满足 path 的前缀与 prefix 配置相符合。path,表示路由会匹配精确的 path,该配置的优先级高于 regex。如果 path被配置,那么请求首先要满足 path 与 path 配置相符合。regex,表示路由会按照正则匹配的方式匹配 path。如果 regex 被配置,那么请求首先要满足 path 与 regex 配置相符合。- 路径匹配配置同时存在时,只有高优先级的配置会生效。
- Heaer 匹配
- headers,表示一组请求需要匹配的 header。请求需要满足配置中所有的 Header 配置条件才算匹配成功。
5.1.4 Header #
{
"name": "",
"value": "",
"regex": false
}
name,表示 header 的 key。value,表示 header 对应 key 的 value。regex,bool 类型,如果为 true,表示 value 支持按照正则表达式的方式进行匹配。
5.1.5 Routes #
{
"metadata":{},
"weight": 10
}
metadata,路由会基于该 metadata 去匹配满足配置中所有属性信息的目标服务。weight,当前属性目标服务负载均衡权重
5.1.6 Metadata #
{
"version":"",
"runtime": ""
}
version,服务版本号runtime,服务运行环境
5.2 控制面配置下发说明 #
5.2.1 Golang结构体 #
// 控制面配置参数
type RouterConfig struct {
ToUniqueID string `json:"to_unique_id"` // 不能为空
LastUpdateTime int64 `json:"last_update_time"` // 最后更新时间,13位毫秒时间戳
Routers []Router `json:"routers"` // 内部至少一个router配置
}
// 一个特定的url规则匹配到特定目标
type Router struct {
Match RouterMatch `json:"match"` // 不能为空
Routes []Route `json:"routes"` // 至少一个route
}
// 具体的目标实例配置
type Route struct {
Metadata map[string]string `json:"metadata"` // 目标实例所包含的属性元数据,匹配不到目标则请求时报错;配置不能为空
Weight int `json:"weight,omitempty"` // 负载到具体实例的权重,默认10,可1-10设置
}
// 请求url匹配规则
// 优先级 prefix > path > regex 路径匹配配置同时存在时,只有高优先级的配置会生效。
type RouterMatch struct {
Prefix string `json:"prefix,omitempty"` // 前缀匹配
Path string `json:"path,omitempty"` // 精准path匹配
Regex string `json:"regex,omitempty"` // 正则path匹配,下发数据需要保证正则表达式有效
Headers []HeaderMatcher `json:"headers,omitempty"` // header头部匹配
}
// header
type HeaderMatcher struct {
Name string `json:"name"` // 不能为空
Value string `json:"value"` //
Regex bool `json:"regex,omitempty"` // 是否可以正则匹配header value, 默认精准匹配
}
5.3 redis存储 #
5.3.1 存储采用hash存储 #
RouterConfigKey = "msp:service_router:%s" // hash key
RouterConfigField = "config" // hash field value为具体配置json
RouterConfigUpdateField = "last_update_time" // hash field 最后更新时间 13位毫秒时间戳
5.3.2 存储示例 #
需要保证两个last_update_time一致
hmset msp:service_router:to_unique_id config '{"to_unique_id":"testBBB","last_update_time":1618457063123,"routers":[{"match":{"prefix":"*"},"routes":[{"metadata":{"version":"v1.0"},"weight":6},{"metadata":{"version":"v2.0"},"weight":4}]}]}' last_update_time 1618457063123