Skip to content

Commit

Permalink
feat: 🎸 支持多版本能力:根据不同路由映射不同的Version版本。 (#1429)
Browse files Browse the repository at this point in the history
  • Loading branch information
heimanba authored Nov 7, 2024
1 parent 00cac81 commit 9b99532
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 39 deletions.
8 changes: 5 additions & 3 deletions plugins/wasm-go/extensions/frontend-gray/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 地址作为唯一标识。
Expand Down
27 changes: 15 additions & 12 deletions plugins/wasm-go/extensions/frontend-gray/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -129,20 +130,22 @@ 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() {
continue
}
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
}
Expand Down
57 changes: 33 additions & 24 deletions plugins/wasm-go/extensions/frontend-gray/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
if util.IsSupportMultiVersion(grayConfig) {
deployment = util.FilterMultiVersionGrayRule(&grayConfig, grayKeyValue, requestPath)
log.Infof("multi version %v", deployment)
} else {
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 {
deployment = util.FilterGrayRule(&grayConfig, grayKeyValue)
grayDeployment := util.FilterGrayRule(&grayConfig, grayKeyValue)
deployment = util.GetVersion(grayConfig, grayDeployment, preVersion, isPageRequest)
}
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)

Expand Down Expand Up @@ -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
}
Expand All @@ -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 // 默认值
Expand Down
37 changes: 37 additions & 0 deletions plugins/wasm-go/extensions/frontend-gray/util/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down

0 comments on commit 9b99532

Please sign in to comment.