diff --git a/plugins/wasm-go/extensions/frontend-gray/README.md b/plugins/wasm-go/extensions/frontend-gray/README.md index 3cf64cdab8..6c36b3a2e9 100644 --- a/plugins/wasm-go/extensions/frontend-gray/README.md +++ b/plugins/wasm-go/extensions/frontend-gray/README.md @@ -12,7 +12,6 @@ | `baseDeployment` | object | 非必填 | - | 配置Base基线规则的配置 | | `grayDeployments` | array of object | 非必填 | - | 配置Gray灰度的生效规则,以及生效版本 | | `backendGrayTag` | string | 非必填 | `x-mse-tag` | 后端灰度版本Tag,如果配置了,cookie中将携带值为`${backendGrayTag}:${grayDeployments[].backendVersion}` | -| `debugGrayWeight` | boolean | 非必填 | - | 开启按照比例灰度的Debug模式,用于观测按比例灰度效果, | | `injection` | object | 非必填 | - | 往首页HTML中注入全局信息,比如`` | @@ -52,7 +51,9 @@ | `backendVersion` | string | 必填 | - | 后端灰度版本,配合`key`为`${backendGrayTag}`,写入cookie中 | | `name` | string | 必填 | - | 规则名称和`rules[].name`关联, | | `enabled` | boolean | 必填 | - | 是否启动当前灰度规则 | -| `weight` | int | 非必填 | - | 按照比例灰度,比如`50`。注意:灰度规则权重总和不能超过100,如果配置了`weight`,则优先生效 | +| `weight` | int | 非必填 | - | 按照比例灰度,比如`50`。注意:灰度规则权重总和不能超过100,如果同时配置了`grayKey`以及`grayDeployments[0].weight`按照比例灰度优先生效 | +> 为了实现按比例(weight) 进行灰度发布,并确保用户粘滞,我们需要确认客户端的唯一性。如果配置了 grayKey,则将其用作唯一标识;如果未配置 grayKey,则使用客户端的访问 IP 地址作为唯一标识。 + `injection`字段配置说明: @@ -119,7 +120,7 @@ grayDeployments: enabled: true weight: 80 ``` -总的灰度规则为100%,其中灰度版本的权重为`80%`,基线版本为`20%`。一旦用户命中了灰度规则,会根据IP固定这个用户的灰度版本(否则会在下次请求时随机选择一个灰度版本)。如果需要观测按比例灰度是否生效,使用`debugGrayWeight`开启Debug模式。 +总的灰度规则为100%,其中灰度版本的权重为`80%`,基线版本为`20%`。一旦用户命中了灰度规则,会根据IP固定这个用户的灰度版本(否则会在下次请求时随机选择一个灰度版本)。 ### 用户信息存在JSON中 diff --git a/plugins/wasm-go/extensions/frontend-gray/config/config.go b/plugins/wasm-go/extensions/frontend-gray/config/config.go index 3c0735544e..2be58209e4 100644 --- a/plugins/wasm-go/extensions/frontend-gray/config/config.go +++ b/plugins/wasm-go/extensions/frontend-gray/config/config.go @@ -8,7 +8,7 @@ import ( const ( XHigressTag = "x-higress-tag" - XForwardedFor = "x-forwarded-for" + XUniqueClient = "x-unique-client" XPreHigressTag = "x-pre-higress-tag" IsIndex = "is-index" IsNotFound = "is-not-found" @@ -58,7 +58,6 @@ type GrayConfig struct { Rewrite *Rewrite BaseDeployment *Deployment GrayDeployments []*Deployment - DebugGrayWeight bool BackendGrayTag string Injection *Injection } @@ -84,7 +83,6 @@ func JsonToGrayConfig(json gjson.Result, grayConfig *GrayConfig) { // 解析 GrayKey grayConfig.GrayKey = json.Get("grayKey").String() grayConfig.GraySubKey = json.Get("graySubKey").String() - grayConfig.DebugGrayWeight = json.Get("debugGrayWeight").Bool() grayConfig.BackendGrayTag = json.Get("backendGrayTag").String() if grayConfig.BackendGrayTag == "" { diff --git a/plugins/wasm-go/extensions/frontend-gray/envoy.yaml b/plugins/wasm-go/extensions/frontend-gray/envoy.yaml index 8dfccd46af..3d57170380 100644 --- a/plugins/wasm-go/extensions/frontend-gray/envoy.yaml +++ b/plugins/wasm-go/extensions/frontend-gray/envoy.yaml @@ -89,10 +89,10 @@ static_resources: { "name": "beta-user", "version": "0.0.1", - "enabled": true + "enabled": true, + "weight": 50 } ], - "debugGrayWeight": true, "injection": { "head": [ "" diff --git a/plugins/wasm-go/extensions/frontend-gray/main.go b/plugins/wasm-go/extensions/frontend-gray/main.go index cae4306329..59d2f8d4d7 100644 --- a/plugins/wasm-go/extensions/frontend-gray/main.go +++ b/plugins/wasm-go/extensions/frontend-gray/main.go @@ -62,12 +62,18 @@ func onHttpRequestHeaders(ctx wrapper.HttpContext, grayConfig config.GrayConfig, xPreHigressVersion := util.ExtractCookieValueByKey(cookies, config.XPreHigressTag) preVersions := strings.Split(xPreHigressVersion, ",") - xForwardedFor, _ := proxywasm.GetHttpRequestHeader("X-Forwarded-For") + // 客户端唯一ID,用于在按照比率灰度时候 客户访问黏贴 + uniqueClientId := grayKeyValue + if uniqueClientId == "" { + xForwardedFor, _ := proxywasm.GetHttpRequestHeader("X-Forwarded-For") + uniqueClientId = util.GetRealIpFromXff(xForwardedFor) + } // 如果没有配置比例,则进行灰度规则匹配 if isIndex { + log.Infof("grayConfig.TotalGrayWeight==== %v", grayConfig.TotalGrayWeight) if grayConfig.TotalGrayWeight > 0 { - deployment = util.FilterGrayWeight(&grayConfig, preVersions, xForwardedFor) + deployment = util.FilterGrayWeight(&grayConfig, preVersions, uniqueClientId) } else { deployment = util.FilterGrayRule(&grayConfig, grayKeyValue) } @@ -80,7 +86,7 @@ func onHttpRequestHeaders(ctx wrapper.HttpContext, grayConfig config.GrayConfig, ctx.SetContext(config.XPreHigressTag, deployment.Version) ctx.SetContext(grayConfig.BackendGrayTag, deployment.BackendVersion) ctx.SetContext(config.IsIndex, isIndex) - ctx.SetContext(config.XForwardedFor, xForwardedFor) + ctx.SetContext(config.XUniqueClient, uniqueClientId) rewrite := grayConfig.Rewrite if rewrite.Host != "" { @@ -154,10 +160,10 @@ func onHttpResponseHeader(ctx wrapper.HttpContext, grayConfig config.GrayConfig, proxywasm.ReplaceHttpResponseHeader("Cache-Control", "no-cache, no-store") frontendVersion := ctx.GetContext(config.XPreHigressTag).(string) - xForwardedFor := ctx.GetContext(config.XForwardedFor).(string) + xUniqueClient := ctx.GetContext(config.XUniqueClient).(string) // 设置前端的版本 - proxywasm.AddHttpResponseHeader("Set-Cookie", fmt.Sprintf("%s=%s,%s; Max-Age=%s; Path=/;", config.XPreHigressTag, frontendVersion, util.GetRealIpFromXff(xForwardedFor), config.MaxAgeCookie)) + proxywasm.AddHttpResponseHeader("Set-Cookie", fmt.Sprintf("%s=%s,%s; Max-Age=%s; Path=/;", config.XPreHigressTag, frontendVersion, xUniqueClient, config.MaxAgeCookie)) // 设置后端的版本 if util.IsBackendGrayEnabled(grayConfig) { backendVersion := ctx.GetContext(grayConfig.BackendGrayTag).(string) diff --git a/plugins/wasm-go/extensions/frontend-gray/util/utils.go b/plugins/wasm-go/extensions/frontend-gray/util/utils.go index 852d8a97d7..5e3b1aaec6 100644 --- a/plugins/wasm-go/extensions/frontend-gray/util/utils.go +++ b/plugins/wasm-go/extensions/frontend-gray/util/utils.go @@ -4,7 +4,6 @@ import ( "fmt" "math/rand" "net/url" - "os" "path" "path/filepath" "sort" @@ -20,9 +19,7 @@ import ( func LogInfof(format string, args ...interface{}) { format = fmt.Sprintf("[%s] %s", "frontend-gray", format) - if os.Getenv("TEST_MODE") != "" { - proxywasm.LogInfof(format, args...) - } + proxywasm.LogInfof(format, args...) } // 从xff中获取真实的IP @@ -234,31 +231,21 @@ func FilterGrayRule(grayConfig *config.GrayConfig, grayKeyValue string) *config. return grayConfig.BaseDeployment } -func FilterGrayWeight(grayConfig *config.GrayConfig, preVersions []string, xForwardedFor string) *config.Deployment { - deployments := append(grayConfig.GrayDeployments, grayConfig.BaseDeployment) - realIp := GetRealIpFromXff(xForwardedFor) - - LogInfof("DebugGrayWeight enabled: %s, realIp: %s, preVersions: %v", grayConfig.DebugGrayWeight, realIp, preVersions) - // 开启Debug模式,否则无法观测到效果 - if !grayConfig.DebugGrayWeight { - // 如果没有获取到真实IP,则返回不走灰度规则 - if realIp == "" { - return grayConfig.BaseDeployment - } - - // 确保每个用户每次访问的都是走同一版本 - if len(preVersions) > 1 && preVersions[1] != "" && realIp == preVersions[1] { - for _, deployment := range deployments { - if deployment.Version == strings.Trim(preVersions[0], " ") { - return deployment - } - } - } +func FilterGrayWeight(grayConfig *config.GrayConfig, preVersions []string, uniqueClientId string) *config.Deployment { + // 如果没有灰度权重,直接返回基础版本 + if grayConfig.TotalGrayWeight == 0 { return grayConfig.BaseDeployment } - if grayConfig.TotalGrayWeight == 0 { - return grayConfig.BaseDeployment + deployments := append(grayConfig.GrayDeployments, grayConfig.BaseDeployment) + LogInfof("uniqueClientId: %s, preVersions: %v", uniqueClientId, preVersions) + // 用户粘滞,确保每个用户每次访问的都是走同一版本 + if len(preVersions) > 1 && preVersions[1] != "" && uniqueClientId == preVersions[1] { + for _, deployment := range deployments { + if deployment.Version == strings.Trim(preVersions[0], " ") { + return deployment + } + } } totalWeight := 100