diff --git a/examples/README.md b/examples/README.md index 88d088dc..fbd1b19f 100644 --- a/examples/README.md +++ b/examples/README.md @@ -19,15 +19,15 @@ ## 🛠️ 管理脚本 -本目录还提供了三个便捷的管理脚本,用于批量管理所有示例模块: +本目录还提供了四个便捷的管理脚本,用于批量管理所有示例模块: ### 脚本功能对比 | 脚本名称 | 主要功能 | 使用场景 | 执行效果 | -|---------|---------|---------|---------| -| **check_versions.sh** | 版本一致性检查 | 检查所有模块的依赖版本是否一致 | 显示版本分布统计,识别版本不一致问题 | +|---------|---------|---------|---------| | **check_versions.sh** | 版本一致性检查 | 检查所有模块的依赖版本是否一致 | 显示版本分布统计,识别版本不一致问题 | | **update_polaris_versions.sh** | 批量版本更新 | 统一更新所有模块的polaris-go和Go版本 | 更新require依赖版本,忽略replace指令 | | **batch_mod_tidy.sh** | 批量依赖整理 | 清理和整理所有模块的依赖关系 | 执行go mod tidy,统计成功/失败数量 | +| **batch_make_clean.sh** | 批量清理构建 | 清理所有子目录的构建产物 | 递归执行make clean,统计成功/失败数量 | ### 详细使用说明 @@ -109,6 +109,31 @@ chmod +x batch_mod_tidy.sh - 成功/失败统计 - 详细的使用说明 +#### 4. batch_make_clean.sh - 批量清理构建脚本 + +**功能**:批量执行 `make clean` 清理所有子目录的构建产物 + +**使用方法**: +```bash +# 给脚本执行权限 +chmod +x batch_make_clean.sh + +# 执行批量清理 +./batch_make_clean.sh +``` + +**执行特点**: +- 🚀 自动发现所有包含Makefile的子目录 +- 📁 跳过根目录的Makefile文件 +- 🧹 递归执行make clean清理构建产物 +- ✅/❌ 实时显示每个模块的执行状态 +- 📊 提供执行结果统计 + +**输出信息**: +- 处理进度和状态 +- 成功/失败统计 +- 详细的使用说明 + ## 🚀 快速开始 1. **选择示例**:根据需要的功能选择对应的示例目录 @@ -122,6 +147,7 @@ chmod +x batch_mod_tidy.sh - 建议先运行 `check_versions.sh` 检查版本一致性 - 如需更新版本,请使用 `update_polaris_versions.sh` 统一更新 - 定期使用 `batch_mod_tidy.sh` 整理依赖关系 +- 使用 `batch_make_clean.sh` 可以快速清理所有子目录的构建产物 ## 🔗 相关链接 diff --git a/examples/batch_make_clean.sh b/examples/batch_make_clean.sh new file mode 100755 index 00000000..91155aa3 --- /dev/null +++ b/examples/batch_make_clean.sh @@ -0,0 +1,68 @@ +#!/bin/bash + +# 批量执行 make clean 脚本 +# 用于递归清理所有包含 Makefile 的子目录 + +set -e # 遇到错误立即退出 + +echo "🚀 开始批量执行 make clean..." + +# 计数器 +success_count=0 +fail_count=0 +total_count=0 + +# 查找所有包含 Makefile 或 makefile 的目录 +find . \( -name "Makefile" -o -name "makefile" \) -type f | while read -r makefile; do + # 获取目录路径 + dir=$(dirname "$makefile") + + # 跳过根目录的 Makefile(如果存在) + if [ "$dir" = "." ]; then + continue + fi + + total_count=$((total_count + 1)) + + echo "📁 处理目录: $dir" + + # 进入目录并执行 make clean + if cd "$dir"; then + echo " 🧹 执行: make clean" + + if make clean 2>&1; then + echo " ✅ 成功: $dir" + success_count=$((success_count + 1)) + else + echo " ❌ 失败: $dir" + fail_count=$((fail_count + 1)) + fi + + # 返回上级目录 + cd - > /dev/null + else + echo " ❌ 无法进入目录: $dir" + fail_count=$((fail_count + 1)) + fi + + echo "---" +done + +echo "📊 执行结果统计:" +echo " 总模块数: $total_count" +echo " 成功数: $success_count" +echo " 失败数: $fail_count" + +if [ $fail_count -eq 0 ]; then + echo "🎉 所有模块的 make clean 执行成功!" +else + echo "⚠️ 有 $fail_count 个模块执行失败,请检查相关目录" +fi + +# 可选:显示使用说明 +echo "" +echo "💡 使用说明:" +echo " 1. 给脚本执行权限: chmod +x batch_make_clean.sh" +echo " 2. 运行脚本: ./batch_make_clean.sh" +echo " 3. 脚本会自动跳过根目录的 Makefile 文件(如果存在)" +echo " 4. 每个模块执行完成后会显示状态" diff --git a/examples/circuitbreaker/instance/consumer/main.go b/examples/circuitbreaker/instance/consumer/main.go index 2f6f8391..f46ba546 100644 --- a/examples/circuitbreaker/instance/consumer/main.go +++ b/examples/circuitbreaker/instance/consumer/main.go @@ -33,6 +33,7 @@ import ( "time" "github.com/polarismesh/polaris-go" + "github.com/polarismesh/polaris-go/api" "github.com/polarismesh/polaris-go/pkg/config" "github.com/polarismesh/polaris-go/pkg/model" ) @@ -46,6 +47,7 @@ var ( port int token string configPath string + debug bool ) func initArgs() { @@ -57,6 +59,7 @@ func initArgs() { flag.IntVar(&port, "port", 18080, "port") flag.StringVar(&token, "token", "", "token") flag.StringVar(&configPath, "config", "./polaris.yaml", "path for config file") + flag.BoolVar(&debug, "debug", false, "debug") } // PolarisClient is a consumer of the circuit breaker calleeService. @@ -69,6 +72,26 @@ type PolarisClient struct { webSvr *http.Server } +// reportServiceCallResult 上报服务调用结果的辅助方法 +func (svr *PolarisClient) reportServiceCallResult(instance model.Instance, retStatus model.RetStatus, statusCode int, delay time.Duration) { + ret := &polaris.ServiceCallResult{ + ServiceCallResult: model.ServiceCallResult{ + EmptyInstanceGauge: model.EmptyInstanceGauge{}, + CalledInstance: instance, + Method: "/echo", + RetStatus: retStatus, + }, + } + ret.SetDelay(delay) + ret.SetRetCode(int32(statusCode)) + if err := svr.consumer.UpdateServiceCallResult(ret); err != nil { + log.Printf("do report service call result : %+v", err) + } else { + log.Printf("report service call result success: instance=%s:%d, status=%v, retCode=%d, delay=%v", + instance.GetHost(), instance.GetPort(), ret.RetStatus, ret.GetRetCode(), delay) + } +} + func (svr *PolarisClient) discoverInstance() (model.Instance, error) { svr.printAllInstances() getOneRequest := &polaris.GetOneInstanceRequest{} @@ -111,6 +134,11 @@ func (svr *PolarisClient) runWebServer() { instance.GetHost(), instance.GetPort(), err))) time.Sleep(time.Millisecond * time.Duration(rand.Intn(10))) + + // 上报服务调用结果 + delay := time.Since(start) + svr.reportServiceCallResult(instance, model.RetFail, http.StatusInternalServerError, delay) + // 上报熔断结果,用于熔断计算 svr.reportCircuitBreak(instance, model.RetFail, strconv.Itoa(http.StatusInternalServerError), start) return @@ -119,6 +147,16 @@ func (svr *PolarisClient) runWebServer() { defer resp.Body.Close() + // 上报服务调用结果 + delay := time.Since(start) + retStatus := model.RetSuccess + if resp.StatusCode == http.StatusTooManyRequests { + retStatus = model.RetFlowControl + } else if resp.StatusCode != http.StatusOK { + retStatus = model.RetFail + } + svr.reportServiceCallResult(instance, retStatus, resp.StatusCode, delay) + // 上报熔断结果,用于熔断计算 if resp.StatusCode != http.StatusOK { svr.reportCircuitBreak(instance, model.RetFail, @@ -271,6 +309,14 @@ func main() { log.Print("calleeNamespace and calleeService are required") return } + if debug { + // 设置日志级别为DEBUG + if err := api.SetLoggersLevel(api.DebugLog); err != nil { + log.Printf("fail to set log level to DEBUG, err is %v", err) + } else { + log.Printf("successfully set log level to DEBUG") + } + } cfg, err := config.LoadConfigurationByFile(configPath) if err != nil { log.Fatalf("load configuration by file %s failed: %v", configPath, err) diff --git a/examples/circuitbreaker/instance/consumer/polaris.yaml b/examples/circuitbreaker/instance/consumer/polaris.yaml index 999b47b6..c1f1d80a 100644 --- a/examples/circuitbreaker/instance/consumer/polaris.yaml +++ b/examples/circuitbreaker/instance/consumer/polaris.yaml @@ -12,3 +12,45 @@ global: type: push address: ${POLARIS_SERVER}:9091 interval: 10s +#描述:主调端配置 +consumer: + #描述:节点熔断相关配置 + circuitBreaker: + #描述:是否启用节点熔断功能 + #类型:bool + #默认值:true + enable: true + # 描述:是否启用默认熔断规则 + #类型:bool + #默认值:true + defaultRuleEnable: true + # 描述:连续错误数熔断器默认连续错误数 + #类型:int + #默认值:10 + defaultErrorCount: 10 + # 描述:错误率熔断器默认错误率 + #类型:int + #默认值:50 + defaultErrorPercent: 50 + # 描述:错误率熔断器默认统计周期 + #类型:int64 + #默认值:60s + defaultInterval: 60s + # 描述:错误率熔断器默认最小请求数 + #类型:int + #默认值:10 + defaultMinimumRequest: 10 + #描述:熔断周期,被熔断后多久可以变为半开 + #类型:duration + #默认值:30s + sleepWindow: 30s + #描述:半开状态后多少个成功请求则恢复 + #类型:int + #默认值:3 + successCountAfterHalfOpen: 3 + #描述:熔断策略,SDK会根据策略名称加载对应的熔断器插件 + #类型:list + #范围:已注册的熔断器插件名 + #默认值:composite 适配服务/接口/实例 熔断插件 + chain: + - composite diff --git a/examples/quickstart/consumer/main.go b/examples/quickstart/consumer/main.go index a3922087..85ea09bd 100644 --- a/examples/quickstart/consumer/main.go +++ b/examples/quickstart/consumer/main.go @@ -37,12 +37,14 @@ import ( var ( namespace string service string + hashkey string port int64 ) func initArgs() { flag.StringVar(&namespace, "namespace", "default", "namespace") flag.StringVar(&service, "service", "DiscoverEchoServer", "service") + flag.StringVar(&hashkey, "hashkey", "", "") flag.Int64Var(&port, "port", 18080, "port") } @@ -106,6 +108,7 @@ func (svr *PolarisConsumer) runWebServer() { getOneRequest := &polaris.GetOneInstanceRequest{} getOneRequest.Namespace = req.Namespace getOneRequest.Service = req.Service + getOneRequest.HashKey = []byte(hashkey) oneInstResp, err := svr.consumer.GetOneInstance(getOneRequest) if err != nil { log.Printf("[error] fail to getOneInstance, err is %v", err) diff --git a/pkg/config/api.go b/pkg/config/api.go index 6de3e5b5..d4c2d8a3 100644 --- a/pkg/config/api.go +++ b/pkg/config/api.go @@ -432,16 +432,13 @@ type CircuitBreakerConfig interface { // SetChain 设置熔断器插件链 SetChain([]string) // GetCheckPeriod 熔断器定时检测时间 - // Deprecated: 不在使用 GetCheckPeriod() time.Duration // SetCheckPeriod 设置熔断器定时检测时间 // Deprecated: 不在使用 SetCheckPeriod(time.Duration) // GetSleepWindow 获取熔断周期 - // Deprecated: 不在使用 GetSleepWindow() time.Duration // SetSleepWindow 设置熔断周期 - // Deprecated: 不在使用 SetSleepWindow(interval time.Duration) // GetRequestCountAfterHalfOpen 获取半开状态后最多分配多少个探测请求 // Deprecated: 不在使用 @@ -450,10 +447,8 @@ type CircuitBreakerConfig interface { // Deprecated: 不在使用 SetRequestCountAfterHalfOpen(count int) // GetSuccessCountAfterHalfOpen 获取半开状态后多少个成功请求则恢复 - // Deprecated: 不在使用 GetSuccessCountAfterHalfOpen() int // SetSuccessCountAfterHalfOpen 设置半开状态后多少个成功请求则恢复 - // Deprecated: 不在使用 SetSuccessCountAfterHalfOpen(count int) // GetRecoverWindow 获取半开后的恢复周期,按周期来进行半开放量的统计 // Deprecated: 不在使用 @@ -473,6 +468,26 @@ type CircuitBreakerConfig interface { // GetErrorRateConfig 错误率熔断配置 // Deprecated: 不在使用 GetErrorRateConfig() ErrorRateConfig + // IsDefaultRuleEnable 是否启用默认实例级熔断规则 + IsDefaultRuleEnable() bool + // SetDefaultRuleEnable 设置是否启用默认实例级熔断规则 + SetDefaultRuleEnable(enable bool) + // GetDefaultErrorCount 获取默认实例级熔断连续错误数阈值 + GetDefaultErrorCount() int + // SetDefaultErrorCount 设置默认实例级熔断连续错误数阈值 + SetDefaultErrorCount(count int) + // GetDefaultErrorPercent 获取默认实例级熔断错误率阈值(百分比) + GetDefaultErrorPercent() int + // SetDefaultErrorPercent 设置默认实例级熔断错误率阈值(百分比) + SetDefaultErrorPercent(rate int) + // GetDefaultInterval 获取默认实例级熔断统计时间窗口 + GetDefaultInterval() time.Duration + // SetDefaultInterval 设置默认实例级熔断统计时间窗口 + SetDefaultInterval(interval time.Duration) + // GetDefaultMinimumRequest 获取默认实例级熔断最小请求数阈值 + GetDefaultMinimumRequest() int + // SetDefaultMinimumRequest 设置默认实例级熔断最小请求数阈值 + SetDefaultMinimumRequest(count int) } // Configuration 全量配置对象. diff --git a/pkg/config/circuitbreaker.go b/pkg/config/circuitbreaker.go index f82585ab..6f498dff 100644 --- a/pkg/config/circuitbreaker.go +++ b/pkg/config/circuitbreaker.go @@ -46,6 +46,16 @@ type CircuitBreakerConfigImpl struct { RecoverWindow *time.Duration `yaml:"recoverWindow" json:"recoverWindow"` // RecoverNumBuckets 半开后的统计的滑窗数 RecoverNumBuckets int `yaml:"recoverNumBuckets" json:"recoverNumBuckets"` + // DefaultRuleEnable 是否启用默认熔断规则 + DefaultRuleEnable *bool `yaml:"defaultRuleEnable" json:"defaultRuleEnable"` + // DefaultErrorCount 连续错误数熔断器默认连续错误数 + DefaultErrorCount *int `yaml:"defaultErrorCount" json:"defaultErrorCount"` + // DefaultErrorPercent 错误率熔断器默认错误率 + DefaultErrorPercent *int `yaml:"defaultErrorPercent" json:"defaultErrorPercent"` + // DefaultInterval 错误率熔断器默认统计周期 + DefaultInterval *time.Duration `yaml:"defaultInterval" json:"defaultInterval"` + // DefaultMinimumRequest 错误率熔断器默认最小请求数 + DefaultMinimumRequest *int `yaml:"defaultMinimumRequest" json:"defaultMinimumRequest"` // Plugin 插件配置反序列化后的对象 Plugin PluginConfigs `yaml:"plugin" json:"plugin"` } @@ -130,6 +140,71 @@ func (c *CircuitBreakerConfigImpl) SetRecoverNumBuckets(value int) { c.RecoverNumBuckets = value } +// IsDefaultRuleEnable 是否启用默认熔断规则 +func (c *CircuitBreakerConfigImpl) IsDefaultRuleEnable() bool { + if c.DefaultRuleEnable == nil { + return false + } + return *c.DefaultRuleEnable +} + +// SetDefaultRuleEnable 设置是否启用默认熔断规则 +func (c *CircuitBreakerConfigImpl) SetDefaultRuleEnable(enable bool) { + c.DefaultRuleEnable = &enable +} + +// GetDefaultErrorCount 获取连续错误数熔断器默认连续错误数 +func (c *CircuitBreakerConfigImpl) GetDefaultErrorCount() int { + if c.DefaultErrorCount == nil { + return 0 + } + return *c.DefaultErrorCount +} + +// SetDefaultErrorCount 设置连续错误数熔断器默认连续错误数 +func (c *CircuitBreakerConfigImpl) SetDefaultErrorCount(count int) { + c.DefaultErrorCount = &count +} + +// GetDefaultErrorPercent 获取错误率熔断器默认错误率 +func (c *CircuitBreakerConfigImpl) GetDefaultErrorPercent() int { + if c.DefaultErrorPercent == nil { + return 0 + } + return *c.DefaultErrorPercent +} + +// SetDefaultErrorPercent 设置错误率熔断器默认错误率 +func (c *CircuitBreakerConfigImpl) SetDefaultErrorPercent(percent int) { + c.DefaultErrorPercent = &percent +} + +// GetDefaultInterval 获取错误率熔断器默认统计周期 +func (c *CircuitBreakerConfigImpl) GetDefaultInterval() time.Duration { + if c.DefaultInterval == nil { + return 0 + } + return *c.DefaultInterval +} + +// SetDefaultInterval 设置错误率熔断器默认统计周期 +func (c *CircuitBreakerConfigImpl) SetDefaultInterval(interval time.Duration) { + c.DefaultInterval = &interval +} + +// GetDefaultMinimumRequest 获取错误率熔断器默认最小请求数 +func (c *CircuitBreakerConfigImpl) GetDefaultMinimumRequest() int { + if c.DefaultMinimumRequest == nil { + return 0 + } + return *c.DefaultMinimumRequest +} + +// SetDefaultMinimumRequest 设置错误率熔断器默认最小请求数 +func (c *CircuitBreakerConfigImpl) SetDefaultMinimumRequest(count int) { + c.DefaultMinimumRequest = &count +} + // GetErrorCountConfig 获取连续错误数熔断配置 func (c *CircuitBreakerConfigImpl) GetErrorCountConfig() ErrorCountConfig { return c.Plugin[DefaultCircuitBreakerErrCount].(ErrorCountConfig) @@ -173,6 +248,23 @@ func (c *CircuitBreakerConfigImpl) Verify() error { fmt.Errorf( "consumer.circuitbreaker.recoverNumBuckets must be greater than %d", MinRecoverNumBuckets)) } + // 校验默认熔断规则参数 + if nil != c.DefaultErrorCount && *c.DefaultErrorCount <= 0 { + errs = multierror.Append(errs, + fmt.Errorf("consumer.circuitbreaker.defaultErrorCount must be greater than 0")) + } + if nil != c.DefaultErrorPercent && (*c.DefaultErrorPercent < 0 || *c.DefaultErrorPercent > 100) { + errs = multierror.Append(errs, + fmt.Errorf("consumer.circuitbreaker.defaultErrorPercent must be in range [0, 100]")) + } + if nil != c.DefaultInterval && *c.DefaultInterval <= 0 { + errs = multierror.Append(errs, + fmt.Errorf("consumer.circuitbreaker.defaultInterval must be greater than 0")) + } + if nil != c.DefaultMinimumRequest && *c.DefaultMinimumRequest <= 0 { + errs = multierror.Append(errs, + fmt.Errorf("consumer.circuitbreaker.defaultMinimumRequest must be greater than 0")) + } if err := c.Plugin.Verify(); err != nil { errs = multierror.Append(errs, err) } @@ -206,6 +298,26 @@ func (c *CircuitBreakerConfigImpl) SetDefault() { if c.RecoverNumBuckets == 0 { c.RecoverNumBuckets = DefaultRecoverNumBuckets } + if nil == c.DefaultRuleEnable { + enable := DefaultRuleEnable + c.DefaultRuleEnable = &enable + } + if nil == c.DefaultErrorCount { + count := DefaultErrorCount + c.DefaultErrorCount = &count + } + if nil == c.DefaultErrorPercent { + percent := DefaultErrorPercent + c.DefaultErrorPercent = &percent + } + if nil == c.DefaultInterval { + interval := DefaultInterval + c.DefaultInterval = &interval + } + if nil == c.DefaultMinimumRequest { + count := DefaultMinimumRequest + c.DefaultMinimumRequest = &count + } c.Plugin.SetDefault(common.TypeCircuitBreaker) } diff --git a/pkg/config/default.go b/pkg/config/default.go index 82a7d55c..da9d5b36 100644 --- a/pkg/config/default.go +++ b/pkg/config/default.go @@ -108,7 +108,17 @@ const ( // DefaultRequestCountAfterHalfOpen 半开状态后分配的探测请求数. DefaultRequestCountAfterHalfOpen = 10 // DefaultSuccessCountAfterHalfOpen 半开状态后恢复的成功请求数. - DefaultSuccessCountAfterHalfOpen = 8 + DefaultSuccessCountAfterHalfOpen = 3 + // DefaultRuleEnable 默认是否启用默认熔断规则. + DefaultRuleEnable bool = true + // DefaultErrorCount 连续错误数熔断器默认连续错误数. + DefaultErrorCount = 10 + // DefaultErrorPercent 错误率熔断器默认错误率. + DefaultErrorPercent = 50 + // DefaultInterval 错误率熔断器默认统计周期, 60s. + DefaultInterval = 60 * time.Second + // DefaultMinimumRequest 错误率熔断器默认最小请求数. + DefaultMinimumRequest = 10 // DefaultRateLimitWindowCount 限流上报时间窗数量,上报间隔=时间间隔/时间窗数量. DefaultRateLimitWindowCount = 10 // MinRateLimitReportInterval 最小限流上报周期. diff --git a/pkg/flow/sync_flow.go b/pkg/flow/sync_flow.go index bb6a1b99..beab437e 100644 --- a/pkg/flow/sync_flow.go +++ b/pkg/flow/sync_flow.go @@ -545,10 +545,10 @@ func (e *Engine) doSyncGetServiceRule(commonRequest *data.CommonRuleRequest) (*m } } log.GetBaseLogger().Warnf("retry GetRoutes from cache loaded from cache files because of timeout, "+ - " Namespace: %s, Service: %s", - commonRequest.DstService.Namespace, commonRequest.DstService.Service) + " Namespace: %s, Service: %s, type: %s", + commonRequest.DstService.Namespace, commonRequest.DstService.Service, commonRequest.DstService.String()) // 上面的尝试超时之后,向尝试获取从缓存文件加载的信息 - svcRule := e.registry.GetServiceRouteRule(&commonRequest.DstService.ServiceKey, true) + svcRule := e.registry.GetServiceRule(&commonRequest.DstService, true) if svcRule.IsInitialized() { commonRequest.CallResult.SetSuccess(e.globalCtx.Since(apiStartTime)) return commonRequest.BuildServiceRuleResponse(svcRule), nil diff --git a/pkg/model/circuitbreaker.go b/pkg/model/circuitbreaker.go index be30c4a3..e88d66db 100644 --- a/pkg/model/circuitbreaker.go +++ b/pkg/model/circuitbreaker.go @@ -424,7 +424,7 @@ func (c *BaseCircuitBreakerStatus) IsAvailable() bool { type HalfOpenStatus struct { BaseCircuitBreakerStatus - maxRequest int + maxRequest int // 半开后请求总数, 存储半开到关闭所必须的最少成功请求数 scheduled int32 calledResult []bool triggered bool @@ -447,9 +447,11 @@ func (c *HalfOpenStatus) Report(success bool) bool { defer c.lock.Unlock() c.calledResult = append(c.calledResult, success) + // 请求失败了 OR 已经探测够了 needTrigger := !success || (len(c.calledResult) >= c.maxRequest) if needTrigger && !c.triggered { c.triggered = true + // 需要执行状态转换 return true } return false @@ -464,14 +466,17 @@ func (c *HalfOpenStatus) CalNextStatus() Status { defer c.lock.Unlock() if !c.triggered { + // 不需要执行状态转换, 保持半开状态 return HalfOpen } for _, ret := range c.calledResult { if !ret { + // 任意一次失败, 熔断器打开 return Open } } + // 连续成功数达到最大请求数,熔断器关闭 return Close } diff --git a/pkg/plugin/servicerouter/util.go b/pkg/plugin/servicerouter/util.go index bfd06cbd..01c8c1ce 100644 --- a/pkg/plugin/servicerouter/util.go +++ b/pkg/plugin/servicerouter/util.go @@ -60,8 +60,10 @@ func processServiceRouters(ctx model.ValueContext, routers []ServiceRouter, rout svcClusters model.ServiceClusters, cluster *model.Cluster) (*RouteResult, model.SDKError) { var result *RouteResult var err error + sourceStr := model.ToStringService(routeInfo.SourceService, true) + destStr := model.ToStringService(routeInfo.DestService, true) log.GetBaseLogger().Debugf("processServiceRouters: start, source=%s, dest=%s, routers=%d, instances=%d", - routeInfo.SourceService, routeInfo.DestService, len(routers), cluster.GetClusterValue().GetInstancesSet(false, false).Count()) + sourceStr, destStr, len(routers), cluster.GetClusterValue().GetInstancesSet(false, false).Count()) for _, router := range routers { routerName := router.Name() @@ -89,8 +91,9 @@ func processServiceRouters(ctx model.ValueContext, routers []ServiceRouter, rout } if nil != result.RedirectDestService { // 转发规则 + redirectStr := model.ToStringService(result.RedirectDestService, true) log.GetBaseLogger().Debugf("processServiceRouters: router=%v redirect to %s", - routerName, result.RedirectDestService) + routerName, redirectStr) return result, nil } cluster = result.OutputCluster diff --git a/plugin/circuitbreaker/composite/breaker.go b/plugin/circuitbreaker/composite/breaker.go index 3c38b0f0..293609f5 100644 --- a/plugin/circuitbreaker/composite/breaker.go +++ b/plugin/circuitbreaker/composite/breaker.go @@ -63,6 +63,8 @@ type CompositeCircuitBreaker struct { checkPeriod time.Duration // healthCheckInstanceExpireInterval healthCheckInstanceExpireInterval time.Duration + // defaultInstanceCircuitBreakerConfig + defaultInstanceCircuitBreakerConfig defaultInstanceCircuitBreakerConfig // localCache localCache localregistry.LocalRegistry // log . @@ -108,6 +110,16 @@ func (c *CompositeCircuitBreaker) Start() error { c.checkPeriod = defaultCheckPeriod } c.healthCheckInstanceExpireInterval = c.checkPeriod * defaultCheckPeriodMultiple + c.defaultInstanceCircuitBreakerConfig = defaultInstanceCircuitBreakerConfig{ + defaultRuleEnable: c.pluginCtx.Config.GetConsumer().GetCircuitBreaker().IsDefaultRuleEnable(), + defaultErrorCount: c.pluginCtx.Config.GetConsumer().GetCircuitBreaker().GetDefaultErrorCount(), + defaultErrorPercent: c.pluginCtx.Config.GetConsumer().GetCircuitBreaker().GetDefaultErrorPercent(), + defaultInterval: int(c.pluginCtx.Config.GetConsumer().GetCircuitBreaker().GetDefaultInterval(). + Seconds()), + defaultMinimumRequest: c.pluginCtx.Config.GetConsumer().GetCircuitBreaker().GetDefaultMinimumRequest(), + sleepWindow: int(c.pluginCtx.Config.GetConsumer().GetCircuitBreaker().GetSleepWindow().Seconds()), + successCountAfterHalfOpen: c.pluginCtx.Config.GetConsumer().GetCircuitBreaker().GetSuccessCountAfterHalfOpen(), + } c.engineFlow = c.pluginCtx.ValueCtx.GetEngine() c.start = 1 @@ -233,6 +245,7 @@ func (c *CompositeCircuitBreaker) Name() string { func (c *CompositeCircuitBreaker) OnEvent(event *common.PluginEvent) error { if c.isDestroyed() || c.start == 0 { + c.log.Debugf("[CircuitBreaker] OnEvent ignored, destroyed: %v, started: %v", c.isDestroyed(), c.start) return nil } @@ -241,26 +254,37 @@ func (c *CompositeCircuitBreaker) OnEvent(event *common.PluginEvent) error { ok bool ) if eventObject, ok = event.EventObject.(*common.ServiceEventObject); !ok { + c.log.Debugf("[CircuitBreaker] OnEvent ignored, event object is not ServiceEventObject") return nil } if eventObject.SvcEventKey.Type != model.EventCircuitBreaker && eventObject.SvcEventKey.Type != model.EventFaultDetect { + c.log.Debugf("[CircuitBreaker] OnEvent ignored, event type: %v", eventObject.SvcEventKey.Type) return nil } + c.log.Infof("[CircuitBreaker] OnEvent processing, namespace: %s, service: %s, eventType: %v", + eventObject.SvcEventKey.Namespace, eventObject.SvcEventKey.Service, eventObject.SvcEventKey.Type) c.doSchedule(eventObject.SvcEventKey) return nil } func (c *CompositeCircuitBreaker) doSchedule(expectKey model.ServiceEventKey) { + c.log.Debugf("[CircuitBreaker] doSchedule started, namespace: %s, service: %s, eventType: %v", + expectKey.Namespace, expectKey.Service, expectKey.Type) c.containers.Range(func(key, value interface{}) bool { ruleC := value.(*RuleContainer) resource := ruleC.res - actualKey := resource.GetService() if actualKey.Namespace == expectKey.Namespace && actualKey.Service == expectKey.Service { + c.log.Debugf("[CircuitBreaker] doSchedule matched resource: %s, eventType: %v", + resource.String(), expectKey.Type) switch expectKey.Type { case model.EventCircuitBreaker: + c.log.Debugf("[CircuitBreaker] doSchedule triggering scheduleCircuitBreaker for resource: %s", + resource.String()) ruleC.scheduleCircuitBreaker() case model.EventFaultDetect: + c.log.Debugf("[CircuitBreaker] doSchedule triggering scheduleHealthCheck for resource: %s", + resource.String()) ruleC.scheduleHealthCheck() } } diff --git a/plugin/circuitbreaker/composite/counter.go b/plugin/circuitbreaker/composite/counter.go index 0d02db00..345be960 100644 --- a/plugin/circuitbreaker/composite/counter.go +++ b/plugin/circuitbreaker/composite/counter.go @@ -226,7 +226,7 @@ func (rc *ResourceCounters) Report(stat *model.ResourceStat) { rc.executor.AffinityExecute(rc.activeRule.Id, rc.HalfOpenToOpen) } } else { - log.GetBaseLogger().Debugf("[CircuitBreaker] report resource stat to counter %s", stat.Resource.String()) + rc.log.Debugf("[CircuitBreaker] report resource stat to counter %s", stat.Resource.String()) for _, counter := range rc.counters { counter.Report(isSuccess) } diff --git a/plugin/circuitbreaker/composite/default.go b/plugin/circuitbreaker/composite/default.go new file mode 100644 index 00000000..1dc7fa40 --- /dev/null +++ b/plugin/circuitbreaker/composite/default.go @@ -0,0 +1,130 @@ +/** + * Tencent is pleased to support the open source community by making polaris-go available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package composite + +import ( + "github.com/polarismesh/specification/source/go/api/v1/fault_tolerance" + apimodel "github.com/polarismesh/specification/source/go/api/v1/model" + "google.golang.org/protobuf/types/known/wrapperspb" + + "github.com/polarismesh/polaris-go/pkg/model" +) + +const ( + _defaultInstanceCircuitBreakerRuleName = "default-polaris-instance-circuit-breaker" + _defaultFailureBlockConfigName = "failure-block-config" + _defaultRetCodeRange = "500~599" +) + +type defaultInstanceCircuitBreakerConfig struct { + // 是否启用默认熔断规则 + defaultRuleEnable bool + // 连续错误数熔断器默认连续错误数 + defaultErrorCount int + // 错误率熔断器默认错误率 + defaultErrorPercent int + // 错误率熔断器默认统计周期 + defaultInterval int + // 错误率熔断器默认最小请求数 + defaultMinimumRequest int + // 熔断打开到半开的时长, 单位秒 + sleepWindow int + // 熔断器半开到关闭所必须的最少成功请求数 + successCountAfterHalfOpen int +} + +func (c *RuleContainer) getCircuitBreakerRule(object *model.ServiceRuleResponse) *fault_tolerance.CircuitBreakerRule { + rule := selectCircuitBreakerRule(c.res, object, c.regexFunction) + if rule != nil { + c.log.Debugf("[CircuitBreaker] found matched rule: %s for resource: %s", rule.Name, c.res.String()) + return rule + } + if !c.breaker.defaultInstanceCircuitBreakerConfig.defaultRuleEnable || c.res.GetLevel() != + fault_tolerance.Level_INSTANCE { + c.log.Debugf("[CircuitBreaker] default rule disabled or level mismatch (defaultRuleEnable=%v, level=%s), "+ + "resource: %s", c.breaker.defaultInstanceCircuitBreakerConfig.defaultRuleEnable, c.res.GetLevel(), + c.res.String()) + return nil + } + // 实例级熔断,如果没有找到规则,默认规则没有被关闭,则使用默认规则 + defaultInstanceCircuitBreakerRule := &fault_tolerance.CircuitBreakerRule{ + Enable: true, + Name: _defaultInstanceCircuitBreakerRuleName, + Level: fault_tolerance.Level_INSTANCE, + RuleMatcher: &fault_tolerance.RuleMatcher{}, + BlockConfigs: make([]*fault_tolerance.BlockConfig, 0), + ErrorConditions: make([]*fault_tolerance.ErrorCondition, 0), + TriggerCondition: make([]*fault_tolerance.TriggerCondition, 0), + } + // 主调服务 + if c.res.GetCallerService() != nil { + defaultInstanceCircuitBreakerRule.RuleMatcher.Source = &fault_tolerance.RuleMatcher_SourceService{ + Namespace: c.res.GetCallerService().Namespace, + Service: c.res.GetCallerService().Service, + } + } + // 被调服务 + if c.res.GetService() != nil { + defaultInstanceCircuitBreakerRule.RuleMatcher.Destination = &fault_tolerance.RuleMatcher_DestinationService{ + Namespace: c.res.GetService().Namespace, + Service: c.res.GetService().Service, + } + } + // 错误判断条件 + errorCondition := &fault_tolerance.ErrorCondition{ + InputType: fault_tolerance.ErrorCondition_RET_CODE, + Condition: &apimodel.MatchString{ + Type: apimodel.MatchString_RANGE, + Value: &wrapperspb.StringValue{Value: _defaultRetCodeRange}, + }, + } + // 熔断触发条件 + triggerConditions := []*fault_tolerance.TriggerCondition{ + // 错误率触发条件 + { + TriggerType: fault_tolerance.TriggerCondition_ERROR_RATE, + ErrorPercent: uint32(c.breaker.defaultInstanceCircuitBreakerConfig.defaultErrorPercent), + Interval: uint32(c.breaker.defaultInstanceCircuitBreakerConfig.defaultInterval), + MinimumRequest: uint32(c.breaker.defaultInstanceCircuitBreakerConfig.defaultMinimumRequest), + }, + // 连续错误触发条件 + { + TriggerType: fault_tolerance.TriggerCondition_CONSECUTIVE_ERROR, + ErrorCount: uint32(c.breaker.defaultInstanceCircuitBreakerConfig.defaultErrorCount), + }, + } + // 熔断恢复策略 + defaultInstanceCircuitBreakerRule.RecoverCondition = &fault_tolerance.RecoverCondition{ + SleepWindow: uint32(c.breaker.defaultInstanceCircuitBreakerConfig.sleepWindow), + ConsecutiveSuccess: uint32(c.breaker.defaultInstanceCircuitBreakerConfig.successCountAfterHalfOpen), + } + blockConf := &fault_tolerance.BlockConfig{ + Name: _defaultFailureBlockConfigName, + ErrorConditions: []*fault_tolerance.ErrorCondition{errorCondition}, + } + blockConf.TriggerConditions = triggerConditions + defaultInstanceCircuitBreakerRule.BlockConfigs = append(defaultInstanceCircuitBreakerRule.BlockConfigs, blockConf) + defaultInstanceCircuitBreakerRule.TriggerCondition = append(defaultInstanceCircuitBreakerRule.TriggerCondition, + triggerConditions...) + defaultInstanceCircuitBreakerRule.ErrorConditions = append(defaultInstanceCircuitBreakerRule.ErrorConditions, + errorCondition) + c.log.Infof("[CircuitBreaker] successfully created default instance circuit breaker rule: %s for resource: %s, "+ + "default rule details: %s", defaultInstanceCircuitBreakerRule.Name, c.res.String(), + defaultInstanceCircuitBreakerRule.String()) + return defaultInstanceCircuitBreakerRule +} diff --git a/plugin/circuitbreaker/composite/rule.go b/plugin/circuitbreaker/composite/rule.go index c7347141..8effb164 100644 --- a/plugin/circuitbreaker/composite/rule.go +++ b/plugin/circuitbreaker/composite/rule.go @@ -76,6 +76,7 @@ func (c *RuleContainer) scheduleHealthCheck() { } func (c *RuleContainer) realRefreshCircuitBreaker() { + c.log.Debugf("[CircuitBreaker] refreshing circuit breaker rule for resource: %s", c.res.String()) engineFlow := c.engineFlow resp, err := engineFlow.SyncGetServiceRule(model.EventCircuitBreaker, &model.GetServiceRuleRequest{ Namespace: c.res.GetService().Namespace, @@ -86,19 +87,25 @@ func (c *RuleContainer) realRefreshCircuitBreaker() { return } resourceCounters := c.breaker.getLevelResourceCounters(c.res.GetLevel()) - cbRule := selectCircuitBreakerRule(c.res, resp, c.regexFunction) + cbRule := c.getCircuitBreakerRule(resp) if cbRule == nil { if _, exist := resourceCounters.remove(c.res); exist { + c.log.Infof("[CircuitBreaker] removed counters for resource: %s, scheduling health check", c.res.String()) c.scheduleHealthCheck() } return } + c.log.Debugf("[CircuitBreaker] matched rule: %s (id: %s, revision: %s) for resource: %s", + cbRule.Name, cbRule.Id, cbRule.Revision, c.res.String()) counters, exist := resourceCounters.get(c.res) if exist { activeRule := counters.CurrentActiveRule() if activeRule.Id == cbRule.Id && activeRule.Revision == cbRule.Revision { + c.log.Debugf("[CircuitBreaker] rule unchanged for resource: %s, skipping update", c.res.String()) return } + c.log.Infof("[CircuitBreaker] rule changed for resource: %s, old rule: %s (revision: %s), new rule: %s "+ + "(revision: %s)", c.res.String(), activeRule.Name, activeRule.Revision, cbRule.Name, cbRule.Revision) } counters, err = newResourceCounters(c.res, cbRule, c.breaker) if err != nil { @@ -106,6 +113,8 @@ func (c *RuleContainer) realRefreshCircuitBreaker() { return } resourceCounters.put(c.res, counters) + c.log.Infof("[CircuitBreaker] created new counters, applied rule: %s (id: %s, revision: %s) for resource: %s", + cbRule.Name, cbRule.Id, cbRule.Revision, c.res.String()) c.scheduleHealthCheck() } @@ -117,7 +126,8 @@ func (c *RuleContainer) realRefreshHealthCheck() { if exist { currentActiveRule = counters.CurrentActiveRule() } - if currentActiveRule != nil && currentActiveRule.Enable && currentActiveRule.GetFallbackConfig().Enable { + if currentActiveRule != nil && currentActiveRule.Enable && currentActiveRule.GetFallbackConfig() != nil && + currentActiveRule.GetFallbackConfig().Enable { engineFlow := c.engineFlow resp, err := engineFlow.SyncGetServiceRule(model.EventFaultDetect, &model.GetServiceRuleRequest{ Namespace: c.res.GetService().Namespace, @@ -162,7 +172,8 @@ func (c *RuleContainer) realRefreshHealthCheck() { } } -func selectCircuitBreakerRule(res model.Resource, object *model.ServiceRuleResponse, regexFunc func(string) *regexp.Regexp) *fault_tolerance.CircuitBreakerRule { +func selectCircuitBreakerRule(res model.Resource, object *model.ServiceRuleResponse, + regexFunc func(string) *regexp.Regexp) *fault_tolerance.CircuitBreakerRule { if object == nil || object.Value == nil { return nil } @@ -178,23 +189,33 @@ func selectCircuitBreakerRule(res model.Resource, object *model.ServiceRuleRespo for i := range sortedRules { cbRule := sortedRules[i] if !cbRule.Enable { + log.GetBaseLogger().Debugf("[CircuitBreaker] rule %s skipped: disabled", cbRule.Name) continue } if cbRule.Level != res.GetLevel() { + log.GetBaseLogger().Debugf("[CircuitBreaker] rule %s skipped: level mismatch (rule level: %v, resource "+ + "level: %v, resource: %s)", cbRule.Name, cbRule.Level, res.GetLevel(), res.String()) continue } ruleMatcher := cbRule.RuleMatcher destination := ruleMatcher.Destination if !match.MatchService(res.GetService(), destination.Namespace, destination.Service) { + log.GetBaseLogger().Debugf("[CircuitBreaker] rule %s skipped: destination service mismatch (rule: %s/%s, "+ + "resource: %s)", + cbRule.Name, destination.Namespace, destination.Service, res.GetService().String()) continue } source := ruleMatcher.Source if !match.MatchService(res.GetCallerService(), source.Namespace, source.Service) { + log.GetBaseLogger().Debugf("[CircuitBreaker] rule %s skipped: source service mismatch (rule: %s/%s, "+ + "resource caller: %s)", cbRule.Name, source.Namespace, source.Service, res.GetCallerService().String()) continue } if ok := matchMethod(res, destination.GetMethod(), regexFunc); !ok { + log.GetBaseLogger().Debugf("[CircuitBreaker] rule %s skipped: method mismatch", cbRule.Name) continue } + log.GetBaseLogger().Infof("[CircuitBreaker] rule %s matched for resource: %s", cbRule.Name, res.String()) return cbRule } return nil diff --git a/plugin/circuitbreaker/composite/trigger/consecutive.go b/plugin/circuitbreaker/composite/trigger/consecutive.go index 67d2a9e9..7eb18891 100644 --- a/plugin/circuitbreaker/composite/trigger/consecutive.go +++ b/plugin/circuitbreaker/composite/trigger/consecutive.go @@ -36,7 +36,8 @@ func NewConsecutiveCounter(name string, opt *Options) *ConsecutiveCounter { } func (c *ConsecutiveCounter) init() { - c.log.Infof("[CircuitBreaker][Counter] consecutiveCounter(%s) initialized, resource(%s)", c.ruleName, c.res.String()) + c.log.Infof("[CircuitBreaker][ConsecutiveCounter] initialized, ruleName: %s, resource(%s)", c.ruleName, + c.res.String()) c.maxCount = int64(c.triggerCondition.GetErrorCount()) } @@ -46,9 +47,13 @@ func (c *ConsecutiveCounter) Report(success bool) { } if !success { currentSum := atomic.AddInt32(&c.consecutiveErrors, 1) + c.log.Debugf("[CircuitBreaker][ConsecutiveCounter] add, ruleName: %s, currentSum(%d), maxCount(%d), "+ + "resource(%s)", c.ruleName, currentSum, c.maxCount, c.res.String()) if currentSum == int32(c.maxCount) { c.suspend() atomic.StoreInt32(&c.consecutiveErrors, 0) + c.log.Infof("[CircuitBreaker][ConsecutiveCounter] triggered CloseToOpen, ruleName: %s, currentSum(%d), "+ + "maxCount(%d), resource(%s)", c.ruleName, currentSum, c.maxCount, c.res.String()) c.handler.CloseToOpen(c.ruleName) } } else { @@ -59,5 +64,7 @@ func (c *ConsecutiveCounter) Report(success bool) { func (c *ConsecutiveCounter) Resume() { if c.isSuspend() { c.resume() + c.log.Infof("[CircuitBreaker][ConsecutiveCounter] resumed, ruleName: %s, resource(%s)", c.ruleName, + c.res.String()) } } diff --git a/plugin/circuitbreaker/composite/trigger/err_rate.go b/plugin/circuitbreaker/composite/trigger/err_rate.go index a727799f..3fba3e73 100644 --- a/plugin/circuitbreaker/composite/trigger/err_rate.go +++ b/plugin/circuitbreaker/composite/trigger/err_rate.go @@ -68,19 +68,22 @@ func NewErrRateCounter(name string, opt *Options) *ErrRateCounter { } func (c *ErrRateCounter) init() { - c.log.Infof("[CircuitBreaker][Counter] errRateCounter(%s) initialized, resource(%s)", c.ruleName, c.res.String()) + c.log.Infof("[CircuitBreaker][ErrRateCounter] initialized, ruleName:%s, resource(%s)", c.ruleName, c.res.String()) c.metricWindow = time.Duration(c.triggerCondition.Interval) * time.Second c.errorPercent = int(c.triggerCondition.ErrorPercent) c.minimumRequest = int32(c.triggerCondition.MinimumRequest) - c.sliceWindow = metric.NewSliceWindow(c.res.String(), bucketCount, getBucketInterval(c.metricWindow), maxDimension, clock.GetClock().Now().UnixNano()) + c.sliceWindow = metric.NewSliceWindow(c.res.String(), bucketCount, getBucketInterval(c.metricWindow), maxDimension, + clock.GetClock().Now().UnixNano()) } func (c *ErrRateCounter) Report(success bool) { if c.isSuspend() { - c.log.Debugf("[CircuitBreaker][Counter] errRateCounter(%s) suspended, skip report", c.ruleName) + c.log.Debugf("[CircuitBreaker][ErrRateCounter] suspended, skip report, ruleName:%s, resource(%s)", + c.ruleName, c.res.String()) return } - c.log.Debugf("[CircuitBreaker][Counter] errRateCounter(%s): add requestCount 1, success(%+v)", c.ruleName, success) + c.log.Debugf("[CircuitBreaker][ErrRateCounter] report request, ruleName:%s, success(%v), resource(%s)", + c.ruleName, success, c.res.String()) retStatus := model.RetSuccess if !success { @@ -98,7 +101,8 @@ func (c *ErrRateCounter) Report(success bool) { return 0 }) if !success && atomic.CompareAndSwapInt32(&c.scheduled, 0, 1) { - c.log.Infof("[CircuitBreaker][Counter] errRateCounter: trigger error rate callback on failure, name(%s)", c.ruleName) + c.log.Infof("[CircuitBreaker][ErrRateCounter] scheduled error rate check, ruleName:%s, metricWindow(%v), "+ + "resource(%s)", c.ruleName, c.metricWindow, c.res.String()) c.delayExecutor(c.metricWindow, func() { currentTime := time.Now() timeRange := &metric.TimeRange{ @@ -107,18 +111,20 @@ func (c *ErrRateCounter) Report(success bool) { } reqCount := c.sliceWindow.CalcMetrics(keyRequestCount, timeRange) reqFailCount := c.sliceWindow.CalcMetrics(keyFailCount, timeRange) - c.log.Infof("[CircuitBreaker][Counter] errRateCounter: requestCount(%d) failCount(%d), minimumRequest(%d), name(%s)", - reqCount, reqFailCount, c.minimumRequest, c.ruleName) - if reqCount < int64(c.minimumRequest) { + failRatio := (float64(reqFailCount) / float64(reqCount)) * 100 + if reqCount < int64(c.minimumRequest) || failRatio < float64(c.errorPercent) { atomic.StoreInt32(&c.scheduled, 0) + c.log.Debugf("[CircuitBreaker][ErrRateCounter] threshold not reached, skip trigger, ruleName:%s, "+ + "reqCount(%d), minimumRequest(%d), failRatio(%.2f%%), errorPercent(%d%%), failCount(%d), "+ + "resource(%s)", c.ruleName, reqCount, c.minimumRequest, failRatio, c.errorPercent, reqFailCount, + c.res.String()) return } - failCount := c.sliceWindow.CalcMetrics(keyFailCount, timeRange) - failRatio := (float64(failCount) / float64(reqCount)) * 100 - if failRatio >= float64(c.errorPercent) { - c.suspend() - c.handler.CloseToOpen(c.ruleName) - } + c.suspend() + c.log.Infof("[CircuitBreaker][ErrRateCounter] triggered CloseToOpen, ruleName:%s, reqCount(%d), "+ + "failCount(%d), failRatio(%.2f%%), minimumRequest(%d), errorPercent(%d%%), resource(%s)", + c.ruleName, reqCount, reqFailCount, failRatio, c.minimumRequest, c.errorPercent, c.res.String()) + c.handler.CloseToOpen(c.ruleName) atomic.StoreInt32(&c.scheduled, 0) }) } @@ -127,6 +133,8 @@ func (c *ErrRateCounter) Report(success bool) { func (c *ErrRateCounter) Resume() { if c.isSuspend() { c.resume() + c.log.Infof("[CircuitBreaker][ErrRateCounter] resumed counter, ruleName:%s, resource(%s)", + c.ruleName, c.res.String()) } } diff --git a/plugin/servicerouter/rulebase/rule.go b/plugin/servicerouter/rulebase/rule.go index d43b0054..31f8788e 100644 --- a/plugin/servicerouter/rulebase/rule.go +++ b/plugin/servicerouter/rulebase/rule.go @@ -88,8 +88,9 @@ func (g *RuleBasedInstancesFilter) Enable(routeInfo *servicerouter.RouteInfo, cl dstRoutes := g.getRoutesFromRule(routeInfo, dstRouteRuleMatch) sourceRoutes := g.getRoutesFromRule(routeInfo, sourceRouteRuleMatch) enabled := len(dstRoutes) > 0 || len(sourceRoutes) > 0 + destStr := model.ToStringService(routeInfo.DestService, true) log.GetBaseLogger().Debugf("RuleBasedRouter.Enable: dest=%s, dstRoutes=%d, sourceRoutes=%d, enabled=%v", - routeInfo.DestService, len(dstRoutes), len(sourceRoutes), enabled) + destStr, len(dstRoutes), len(sourceRoutes), enabled) return enabled } diff --git a/polaris.yaml b/polaris.yaml index 1ed994f4..dbc0b564 100644 --- a/polaris.yaml +++ b/polaris.yaml @@ -259,6 +259,38 @@ consumer: #类型:bool #默认值:true enable: true + #描述:熔断器定时检查周期 + #类型:duration + #默认值:10s + checkPeriod: 10s + #描述:熔断周期,被熔断后多久可以变为半开 + #类型:duration + #默认值:30s + sleepWindow: 30s + #描述:半开状态后多少个成功请求则恢复 + #类型:int + #默认值:8 + successCountAfterHalfOpen: 3 + # 描述:是否启用默认熔断规则 + #类型:bool + #默认值:true + defaultRuleEnable: true + # 描述:连续错误数熔断器默认连续错误数 + #类型:int + #默认值:10 + defaultErrorCount: 10 + # 描述:错误率熔断器默认错误率 + #类型:int + #默认值:50 + defaultErrorPercent: 50 + # 描述:错误率熔断器默认统计周期 + #类型:int64 + #默认值:60s + defaultInterval: 60s + # 描述:错误率熔断器默认最小请求数 + #类型:int + #默认值:10 + defaultMinimumRequest: 10 #描述:熔断策略,SDK会根据策略名称加载对应的熔断器插件 #类型:list #范围:已注册的熔断器插件名