mirror of
https://github.com/alibaba/higress.git
synced 2026-03-02 07:30:49 +08:00
feat: 🎸 支持多版本能力:根据不同路由映射不同的Version版本。 (#1429)
This commit is contained in:
@@ -57,15 +57,17 @@ description: 前端灰度插件配置参考
|
||||
|
||||
| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
|
||||
|----------------|--------------|------|-----|-----------------------------------------------------------------------------------|
|
||||
| `version` | string | 必填 | - | Base版本的版本号,作为兜底的版本 |
|
||||
| `version` | string | 必填 | - | Base版本的版本号,作为兜底的版本 |
|
||||
| `versionPredicates` | string | 必填 | - | 和`version`含义相同,但是满足多版本的需求:根据不同路由映射不同的`Version`版本。一般用于微前端的场景:一个主应用需要管理多个微应用 |
|
||||
|
||||
`grayDeployments`字段配置说明:
|
||||
|
||||
| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
|
||||
|--------|--------|------|-----|-------------------------------------------------|
|
||||
| `version` | string | 必填 | - | Gray版本的版本号,如果命中灰度规则,则使用此版本。如果是非CDN部署,在header添加`x-higress-tag` |
|
||||
| `version` | string | 必填 | - | Gray版本的版本号,如果命中灰度规则,则使用此版本。如果是非CDN部署,在header添加`x-higress-tag` |
|
||||
| `versionPredicates` | string | 必填 | - | 和`version`含义相同,但是满足多版本的需求:根据不同路由映射不同的`Version`版本。一般用于微前端的场景:一个主应用需要管理多个微应用 |
|
||||
| `backendVersion` | string | 必填 | - | 后端灰度版本,配合`key`为`${backendGrayTag}`,写入cookie中 |
|
||||
| `name` | string | 必填 | - | 规则名称和`rules[].name`关联, |
|
||||
| `name` | string | 必填 | - | 规则名称和`rules[].name`关联 |
|
||||
| `enabled` | boolean | 必填 | - | 是否启动当前灰度规则 |
|
||||
| `weight` | int | 非必填 | - | 按照比例灰度,比如`50`。注意:灰度规则权重总和不能超过100,如果同时配置了`grayKey`以及`grayDeployments[0].weight`按照比例灰度优先生效 |
|
||||
> 为了实现按比例(weight) 进行灰度发布,并确保用户粘滞,我们需要确认客户端的唯一性。如果配置了 grayKey,则将其用作唯一标识;如果未配置 grayKey,则使用客户端的访问 IP 地址作为唯一标识。
|
||||
|
||||
@@ -25,11 +25,12 @@ type GrayRule struct {
|
||||
}
|
||||
|
||||
type Deployment struct {
|
||||
Name string
|
||||
Enabled bool
|
||||
Version string
|
||||
BackendVersion string
|
||||
Weight int
|
||||
Name string
|
||||
Enabled bool
|
||||
Version string
|
||||
BackendVersion string
|
||||
Weight int
|
||||
VersionPredicates map[string]string
|
||||
}
|
||||
|
||||
type Rewrite struct {
|
||||
@@ -129,8 +130,9 @@ func JsonToGrayConfig(json gjson.Result, grayConfig *GrayConfig) {
|
||||
grayDeployments := json.Get("grayDeployments").Array()
|
||||
|
||||
grayConfig.BaseDeployment = &Deployment{
|
||||
Name: baseDeployment.Get("name").String(),
|
||||
Version: strings.Trim(baseDeployment.Get("version").String(), " "),
|
||||
Name: baseDeployment.Get("name").String(),
|
||||
Version: strings.Trim(baseDeployment.Get("version").String(), " "),
|
||||
VersionPredicates: convertToStringMap(baseDeployment.Get("versionPredicates")),
|
||||
}
|
||||
for _, item := range grayDeployments {
|
||||
if !item.Get("enabled").Bool() {
|
||||
@@ -138,11 +140,12 @@ func JsonToGrayConfig(json gjson.Result, grayConfig *GrayConfig) {
|
||||
}
|
||||
grayWeight := int(item.Get("weight").Int())
|
||||
grayConfig.GrayDeployments = append(grayConfig.GrayDeployments, &Deployment{
|
||||
Name: item.Get("name").String(),
|
||||
Enabled: item.Get("enabled").Bool(),
|
||||
Version: strings.Trim(item.Get("version").String(), " "),
|
||||
BackendVersion: item.Get("backendVersion").String(),
|
||||
Weight: grayWeight,
|
||||
Name: item.Get("name").String(),
|
||||
Enabled: item.Get("enabled").Bool(),
|
||||
Version: strings.Trim(item.Get("version").String(), " "),
|
||||
BackendVersion: item.Get("backendVersion").String(),
|
||||
Weight: grayWeight,
|
||||
VersionPredicates: convertToStringMap(item.Get("versionPredicates")),
|
||||
})
|
||||
grayConfig.TotalGrayWeight += grayWeight
|
||||
}
|
||||
|
||||
@@ -70,22 +70,28 @@ func onHttpRequestHeaders(ctx wrapper.HttpContext, grayConfig config.GrayConfig,
|
||||
}
|
||||
|
||||
// 如果没有配置比例,则进行灰度规则匹配
|
||||
if isPageRequest {
|
||||
if grayConfig.TotalGrayWeight > 0 {
|
||||
log.Infof("grayConfig.TotalGrayWeight: %v", grayConfig.TotalGrayWeight)
|
||||
deployment = util.FilterGrayWeight(&grayConfig, preVersion, preUniqueClientId, uniqueClientId)
|
||||
} else {
|
||||
deployment = util.FilterGrayRule(&grayConfig, grayKeyValue)
|
||||
}
|
||||
log.Infof("index deployment: %v, path: %v, backend: %v, xPreHigressVersion: %s,%s", deployment, requestPath, deployment.BackendVersion, preVersion, preUniqueClientId)
|
||||
if util.IsSupportMultiVersion(grayConfig) {
|
||||
deployment = util.FilterMultiVersionGrayRule(&grayConfig, grayKeyValue, requestPath)
|
||||
log.Infof("multi version %v", deployment)
|
||||
} else {
|
||||
grayDeployment := util.FilterGrayRule(&grayConfig, grayKeyValue)
|
||||
deployment = util.GetVersion(grayConfig, grayDeployment, preVersion, isPageRequest)
|
||||
if isPageRequest {
|
||||
if grayConfig.TotalGrayWeight > 0 {
|
||||
log.Infof("grayConfig.TotalGrayWeight: %v", grayConfig.TotalGrayWeight)
|
||||
deployment = util.FilterGrayWeight(&grayConfig, preVersion, preUniqueClientId, uniqueClientId)
|
||||
} else {
|
||||
deployment = util.FilterGrayRule(&grayConfig, grayKeyValue)
|
||||
}
|
||||
log.Infof("index deployment: %v, path: %v, backend: %v, xPreHigressVersion: %s,%s", deployment, requestPath, deployment.BackendVersion, preVersion, preUniqueClientId)
|
||||
} else {
|
||||
grayDeployment := util.FilterGrayRule(&grayConfig, grayKeyValue)
|
||||
deployment = util.GetVersion(grayConfig, grayDeployment, preVersion, isPageRequest)
|
||||
}
|
||||
ctx.SetContext(config.XPreHigressTag, deployment.Version)
|
||||
ctx.SetContext(grayConfig.BackendGrayTag, deployment.BackendVersion)
|
||||
}
|
||||
|
||||
proxywasm.AddHttpRequestHeader(config.XHigressTag, deployment.Version)
|
||||
|
||||
ctx.SetContext(config.XPreHigressTag, deployment.Version)
|
||||
ctx.SetContext(grayConfig.BackendGrayTag, deployment.BackendVersion)
|
||||
ctx.SetContext(config.IsPageRequest, isPageRequest)
|
||||
ctx.SetContext(config.XUniqueClientId, uniqueClientId)
|
||||
|
||||
@@ -167,18 +173,24 @@ func onHttpResponseHeader(ctx wrapper.HttpContext, grayConfig config.GrayConfig,
|
||||
log.Errorf("error status: %s, error message: %v", status, err)
|
||||
return types.ActionContinue
|
||||
}
|
||||
cacheControl, _ := proxywasm.GetHttpResponseHeader("cache-control")
|
||||
if !strings.Contains(cacheControl, "no-cache") {
|
||||
proxywasm.ReplaceHttpResponseHeader("cache-control", "no-cache, no-store, max-age=0, must-revalidate")
|
||||
}
|
||||
|
||||
proxywasm.ReplaceHttpResponseHeader("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate")
|
||||
|
||||
frontendVersion := ctx.GetContext(config.XPreHigressTag).(string)
|
||||
xUniqueClient := ctx.GetContext(config.XUniqueClientId).(string)
|
||||
frontendVersion, isFeVersionOk := ctx.GetContext(config.XPreHigressTag).(string)
|
||||
xUniqueClient, isUniqClientOk := ctx.GetContext(config.XUniqueClientId).(string)
|
||||
|
||||
// 设置前端的版本
|
||||
proxywasm.AddHttpResponseHeader("Set-Cookie", fmt.Sprintf("%s=%s,%s; Max-Age=%s; Path=/;", config.XPreHigressTag, frontendVersion, xUniqueClient, grayConfig.UserStickyMaxAge))
|
||||
if isFeVersionOk && isUniqClientOk && frontendVersion != "" {
|
||||
proxywasm.AddHttpResponseHeader("Set-Cookie", fmt.Sprintf("%s=%s,%s; Max-Age=%s; Path=/;", config.XPreHigressTag, frontendVersion, xUniqueClient, grayConfig.UserStickyMaxAge))
|
||||
}
|
||||
// 设置后端的版本
|
||||
if util.IsBackendGrayEnabled(grayConfig) {
|
||||
backendVersion := ctx.GetContext(grayConfig.BackendGrayTag).(string)
|
||||
proxywasm.AddHttpResponseHeader("Set-Cookie", fmt.Sprintf("%s=%s; Max-Age=%s; Path=/;", grayConfig.BackendGrayTag, backendVersion, grayConfig.UserStickyMaxAge))
|
||||
backendVersion, isBackVersionOk := ctx.GetContext(grayConfig.BackendGrayTag).(string)
|
||||
if isBackVersionOk && backendVersion != "" {
|
||||
proxywasm.AddHttpResponseHeader("Set-Cookie", fmt.Sprintf("%s=%s; Max-Age=%s; Path=/;", grayConfig.BackendGrayTag, backendVersion, grayConfig.UserStickyMaxAge))
|
||||
}
|
||||
}
|
||||
return types.ActionContinue
|
||||
}
|
||||
@@ -188,16 +200,13 @@ func onHttpResponseBody(ctx wrapper.HttpContext, grayConfig config.GrayConfig, b
|
||||
if !enabledGray {
|
||||
return types.ActionContinue
|
||||
}
|
||||
isPageRequest, ok := ctx.GetContext(config.IsPageRequest).(bool)
|
||||
if !ok {
|
||||
isPageRequest = false // 默认值
|
||||
}
|
||||
isPageRequest, isPageRequestOk := ctx.GetContext(config.IsPageRequest).(bool)
|
||||
frontendVersion, isFeVersionOk := ctx.GetContext(config.XPreHigressTag).(string)
|
||||
// 只处理首页相关请求
|
||||
if !isPageRequest {
|
||||
if !isFeVersionOk || !isPageRequestOk || !isPageRequest {
|
||||
return types.ActionContinue
|
||||
}
|
||||
|
||||
frontendVersion := ctx.GetContext(config.XPreHigressTag).(string)
|
||||
isNotFound, ok := ctx.GetContext(config.IsNotFound).(bool)
|
||||
if !ok {
|
||||
isNotFound = false // 默认值
|
||||
|
||||
@@ -276,6 +276,43 @@ func GetGrayKey(grayKeyValueByCookie string, grayKeyValueByHeader string, graySu
|
||||
return grayKeyValue
|
||||
}
|
||||
|
||||
// 如果基础部署或任何灰度部署中包含VersionPredicates,则认为是多版本配置
|
||||
func IsSupportMultiVersion(grayConfig config.GrayConfig) bool {
|
||||
if len(grayConfig.BaseDeployment.VersionPredicates) > 0 {
|
||||
return true
|
||||
}
|
||||
for _, deployment := range grayConfig.GrayDeployments {
|
||||
if len(deployment.VersionPredicates) > 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// FilterMultiVersionGrayRule 过滤多版本灰度规则
|
||||
func FilterMultiVersionGrayRule(grayConfig *config.GrayConfig, grayKeyValue string, requestPath string) *config.Deployment {
|
||||
// 首先根据灰度键值获取当前部署
|
||||
currentDeployment := FilterGrayRule(grayConfig, grayKeyValue)
|
||||
|
||||
// 创建一个新的部署对象,初始化版本为当前部署的版本
|
||||
deployment := &config.Deployment{
|
||||
Version: currentDeployment.Version,
|
||||
}
|
||||
|
||||
// 对版本谓词的键进行排序
|
||||
keys := SortKeysByLengthAndLexicographically(currentDeployment.VersionPredicates)
|
||||
|
||||
// 遍历排序后的键
|
||||
for _, prefix := range keys {
|
||||
// 如果请求路径以当前前缀开头
|
||||
if strings.HasPrefix(requestPath, prefix) {
|
||||
deployment.Version = currentDeployment.VersionPredicates[prefix]
|
||||
return deployment
|
||||
}
|
||||
}
|
||||
return deployment
|
||||
}
|
||||
|
||||
// FilterGrayRule 过滤灰度规则
|
||||
func FilterGrayRule(grayConfig *config.GrayConfig, grayKeyValue string) *config.Deployment {
|
||||
for _, deployment := range grayConfig.GrayDeployments {
|
||||
|
||||
Reference in New Issue
Block a user