diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index 97e51ea0..cc84e4bb 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -37,7 +37,9 @@ jobs: - uses: actions/setup-go@v3 - uses: actions/checkout@v3 - name: golangci-lint - uses: golangci/golangci-lint-action@v3.3.0 + uses: golangci/golangci-lint-action@v3.6.0 with: # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version. version: latest + args: --timeout=30m + \ No newline at end of file diff --git a/api.go b/api.go index 26a43567..5ca082b0 100644 --- a/api.go +++ b/api.go @@ -154,6 +154,20 @@ type ConfigGroupAPI interface { GetConfigGroup(namesapce, group string) (model.ConfigFileGroup, error) } +type CircuitBreakerAPI interface { + api.SDKOwner + // Check + Check(model.Resource) (*model.CheckResult, error) + // Report + Report(*model.ResourceStat) error + // MakeFunctionDecorator + MakeFunctionDecorator(model.CustomerFunction, *api.RequestContext) model.DecoratorFunction + // MakeInvokeHandler + MakeInvokeHandler(*api.RequestContext) model.InvokeHandler + // Destroy the api is destroyed and cannot be called again + Destroy() +} + // RouterAPI routing api methods type RouterAPI interface { api.SDKOwner diff --git a/api/circuitbreaker.go b/api/circuitbreaker.go new file mode 100644 index 00000000..29607fc8 --- /dev/null +++ b/api/circuitbreaker.go @@ -0,0 +1,87 @@ +/** + * 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 api + +import ( + "github.com/polarismesh/polaris-go/pkg/config" + "github.com/polarismesh/polaris-go/pkg/model" +) + +// CircuitBreakerAPI . +type CircuitBreakerAPI interface { + SDKOwner + // Check + Check(model.Resource) (*model.CheckResult, error) + // Report + Report(*model.ResourceStat) error + // MakeFunctionDecorator + MakeFunctionDecorator(model.CustomerFunction, *RequestContext) model.DecoratorFunction + // MakeInvokeHandler + MakeInvokeHandler(*RequestContext) model.InvokeHandler +} + +type ResultToErrorCode interface { + model.ResultToErrorCode +} + +type RequestContext struct { + model.RequestContext +} + +type ResponseContext struct { + model.ResponseContext +} + +var ( + // NewConsumerAPI 通过以默认域名为埋点server的默认配置创建 CircuitBreakerAPI + NewCircuitBreakerAPI = newCircuitBreakerAPI + // NewCircuitBreakerByFile 通过配置文件创建SDK CircuitBreakerAPI 对象 + NewCircuitBreakerByFile = newCircuitBreakerAPIByFile + // NewCircuitBreakerByConfig 通过配置对象创建SDK CircuitBreakerAPI 对象 + NewCircuitBreakerByConfig = newCircuitBreakerAPIByConfig + // NewCircuitBreakerByContext 通过上下文创建SDK CircuitBreakerAPI 对象 + NewCircuitBreakerByContext = newCircuitBreakerAPIByContext +) + +// 通过以默认域名为埋点server的默认配置创建 CircuitBreakerAPI +func newCircuitBreakerAPI() (CircuitBreakerAPI, error) { + return newCircuitBreakerAPIByConfig(config.NewDefaultConfigurationWithDomain()) +} + +// newCircuitBreakerAPIByFile 通过配置文件创建SDK CircuitBreakerAPI 对象 +func newCircuitBreakerAPIByFile(path string) (CircuitBreakerAPI, error) { + context, err := InitContextByFile(path) + if err != nil { + return nil, err + } + return &circuitBreakerAPI{context}, nil +} + +// newCircuitBreakerAPIByConfig 通过配置对象创建SDK CircuitBreakerAPI 对象 +func newCircuitBreakerAPIByConfig(cfg config.Configuration) (CircuitBreakerAPI, error) { + context, err := InitContextByConfig(cfg) + if err != nil { + return nil, err + } + return &circuitBreakerAPI{context}, nil +} + +// newCircuitBreakerAPIByContext 通过上下文创建SDK CircuitBreakerAPI 对象 +func newCircuitBreakerAPIByContext(context SDKContext) CircuitBreakerAPI { + return &circuitBreakerAPI{context} +} diff --git a/api/circuitbreaker_impl.go b/api/circuitbreaker_impl.go new file mode 100644 index 00000000..9979e19d --- /dev/null +++ b/api/circuitbreaker_impl.go @@ -0,0 +1,48 @@ +/** + * 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 api + +import ( + "github.com/polarismesh/polaris-go/pkg/model" + _ "github.com/polarismesh/polaris-go/pkg/plugin/register" +) + +type circuitBreakerAPI struct { + context SDKContext +} + +// SDKContext 获取SDK上下文 +func (c *circuitBreakerAPI) SDKContext() SDKContext { + return c.context +} + +func (c *circuitBreakerAPI) Check(resource model.Resource) (*model.CheckResult, error) { + return c.context.GetEngine().Check(resource) +} + +func (c *circuitBreakerAPI) Report(reportStat *model.ResourceStat) error { + return c.context.GetEngine().Report(reportStat) +} + +func (c *circuitBreakerAPI) MakeFunctionDecorator(f model.CustomerFunction, reqCtx *RequestContext) model.DecoratorFunction { + return c.context.GetEngine().MakeFunctionDecorator(f, &reqCtx.RequestContext) +} + +func (c *circuitBreakerAPI) MakeInvokeHandler(reqCtx *RequestContext) model.InvokeHandler { + return c.context.GetEngine().MakeInvokeHandler(&reqCtx.RequestContext) +} diff --git a/api/provider_impl.go b/api/provider_impl.go index 89e6d72e..9c96546d 100644 --- a/api/provider_impl.go +++ b/api/provider_impl.go @@ -38,7 +38,7 @@ func (c *providerAPI) RegisterInstance(instance *InstanceRegisterRequest) (*mode if err := instance.Validate(); err != nil { return nil, err } - return c.context.GetEngine().SyncRegisterV2(&instance.InstanceRegisterRequest) + return c.context.GetEngine().SyncRegister(&instance.InstanceRegisterRequest) } // Register 同步注册服务,服务注册成功后会填充instance中的InstanceId字段 diff --git a/api_circuitbreaker.go b/api_circuitbreaker.go new file mode 100644 index 00000000..d0bc9b4c --- /dev/null +++ b/api_circuitbreaker.go @@ -0,0 +1,98 @@ +/** + * 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 polaris + +import ( + "github.com/polarismesh/polaris-go/api" + "github.com/polarismesh/polaris-go/pkg/config" + "github.com/polarismesh/polaris-go/pkg/model" +) + +// NewCircuitBreakerAPI 获取 CircuitBreakerAPI +func NewCircuitBreakerAPI() (CircuitBreakerAPI, error) { + rawAPI, err := api.NewCircuitBreakerAPI() + if err != nil { + return nil, err + } + return &circuitBreakerAPI{ + rawAPI: rawAPI, + }, nil +} + +// NewCircuitBreakerAPIByConfig 通过配置对象获取 CircuitBreakerAPI +func NewCircuitBreakerAPIByConfig(cfg config.Configuration) (CircuitBreakerAPI, error) { + rawAPI, err := api.NewCircuitBreakerByConfig(cfg) + if err != nil { + return nil, err + } + return &circuitBreakerAPI{ + rawAPI: rawAPI, + }, nil +} + +// NewCircuitBreakerAPIByFile 通过配置文件获取 CircuitBreakerAPI +func NewCircuitBreakerAPIByFile(path string) (CircuitBreakerAPI, error) { + rawAPI, err := api.NewCircuitBreakerByFile(path) + if err != nil { + return nil, err + } + return &circuitBreakerAPI{ + rawAPI: rawAPI, + }, nil +} + +// NewCircuitBreakerAPIByContext 通过上下文对象获取 CircuitBreakerAPI +func NewCircuitBreakerAPIByContext(context api.SDKContext) CircuitBreakerAPI { + rawAPI := api.NewCircuitBreakerByContext(context) + return &circuitBreakerAPI{ + rawAPI: rawAPI, + } +} + +type circuitBreakerAPI struct { + rawAPI api.CircuitBreakerAPI +} + +// Check +func (c *circuitBreakerAPI) Check(res model.Resource) (*model.CheckResult, error) { + return c.rawAPI.Check(res) +} + +// Report +func (c *circuitBreakerAPI) Report(stat *model.ResourceStat) error { + return c.rawAPI.Report(stat) +} + +// MakeFunctionDecorator +func (c *circuitBreakerAPI) MakeFunctionDecorator(f model.CustomerFunction, reqCtx *api.RequestContext) model.DecoratorFunction { + return c.rawAPI.MakeFunctionDecorator(f, reqCtx) +} + +// MakeInvokeHandler +func (c *circuitBreakerAPI) MakeInvokeHandler(reqCtx *api.RequestContext) model.InvokeHandler { + return c.rawAPI.MakeInvokeHandler(reqCtx) +} + +func (c *circuitBreakerAPI) SDKContext() api.SDKContext { + return c.rawAPI.SDKContext() +} + +// Destroy the api is destroyed and cannot be called again +func (c *circuitBreakerAPI) Destroy() { + c.rawAPI.SDKContext().Destroy() +} diff --git a/api_provider.go b/api_provider.go index 458bef61..b86f546c 100644 --- a/api_provider.go +++ b/api_provider.go @@ -36,7 +36,8 @@ func (p *providerAPI) SDKContext() api.SDKContext { // RegisterInstance // minimum supported version of polaris-server is v1.10.0 func (p *providerAPI) RegisterInstance(instance *InstanceRegisterRequest) (*model.InstanceRegisterResponse, error) { - return p.rawAPI.RegisterInstance((*api.InstanceRegisterRequest)(instance)) + instance.AutoHeartbeat = true + return p.rawAPI.Register((*api.InstanceRegisterRequest)(instance)) } // Register diff --git a/examples/circuitbreaker/consumer/main.go b/examples/circuitbreaker/consumer/main.go deleted file mode 100644 index e26d5547..00000000 --- a/examples/circuitbreaker/consumer/main.go +++ /dev/null @@ -1,149 +0,0 @@ -/** - * 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 main - -import ( - "flag" - "fmt" - "io/ioutil" - "log" - "net/http" - "time" - - "github.com/polarismesh/polaris-go" - "github.com/polarismesh/polaris-go/api" -) - -var ( - namespace string - service string - port int64 -) - -func initArgs() { - flag.StringVar(&namespace, "namespace", "default", "namespace") - flag.StringVar(&service, "service", "CircuitBreakerEchoServer", "service") - flag.Int64Var(&port, "port", 18080, "port") -} - -// PolarisConsumer is a consumer of the circuit breaker service. -type PolarisConsumer struct { - consumer polaris.ConsumerAPI - namespace string - service string -} - -// Run is the consumer's main function. -func (svr *PolarisConsumer) Run() { - svr.runWebServer() -} - -func (svr *PolarisConsumer) runWebServer() { - http.HandleFunc("/echo", func(rw http.ResponseWriter, r *http.Request) { - for i := 0; i < 10; i++ { - log.Printf("start to invoke getOneInstance operation") - getOneRequest := &polaris.GetOneInstanceRequest{} - getOneRequest.Namespace = namespace - getOneRequest.Service = service - oneInstResp, err := svr.consumer.GetOneInstance(getOneRequest) - if err != nil { - log.Printf("[error] fail to getOneInstance, err is %v", err) - rw.WriteHeader(http.StatusInternalServerError) - _, _ = rw.Write([]byte(fmt.Sprintf("[error] fail to getOneInstance, err is %v", err))) - _, _ = rw.Write([]byte("\n")) - continue - } - instance := oneInstResp.GetInstance() - if nil != instance { - log.Printf("instance getOneInstance is %s:%d", instance.GetHost(), instance.GetPort()) - } - - start := time.Now() - resp, err := http.Get(fmt.Sprintf("http://%s:%d/echo", instance.GetHost(), instance.GetPort())) - if resp != nil { - defer resp.Body.Close() - } - - if err != nil || resp.StatusCode != http.StatusOK { - delay := time.Now().Sub(start) - callRet := &polaris.ServiceCallResult{} - callRet.CalledInstance = instance - callRet.SetRetCode(int32(http.StatusInternalServerError)) - callRet.SetDelay(delay) - callRet.SetRetStatus(api.RetFail) - - if err := svr.consumer.UpdateServiceCallResult(callRet); err != nil { - log.Printf("[errot] fail to UpdateServiceCallResult, err is %v", err) - rw.WriteHeader(http.StatusInternalServerError) - _, _ = rw.Write([]byte(fmt.Sprintf("[errot] fail to UpdateServiceCallResult, err is %v", err))) - _, _ = rw.Write([]byte("\n")) - continue - } - log.Printf("[errot] send request to %s:%d fail : %s", instance.GetHost(), instance.GetPort(), err) - rw.WriteHeader(http.StatusInternalServerError) - _, _ = rw.Write([]byte(fmt.Sprintf("[errot] send request to %s:%d fail : %s", instance.GetHost(), instance.GetPort(), err))) - _, _ = rw.Write([]byte("\n")) - continue - } - - data, err := ioutil.ReadAll(resp.Body) - if err != nil { - log.Printf("[error] read resp from %s:%d fail : %s", instance.GetHost(), instance.GetPort(), err) - rw.WriteHeader(http.StatusInternalServerError) - _, _ = rw.Write([]byte(fmt.Sprintf("[error] read resp from %s:%d fail : %s", instance.GetHost(), instance.GetPort(), err))) - _, _ = rw.Write([]byte("\n")) - continue - } - rw.WriteHeader(http.StatusOK) - _, _ = rw.Write(data) - _, _ = rw.Write([]byte("\n")) - } - }) - - log.Printf("start run web server, port : %d", port) - - if err := http.ListenAndServe(fmt.Sprintf("0.0.0.0:%d", port), nil); err != nil { - log.Fatalf("[ERROR]fail to run webServer, err is %v", err) - } -} - -func main() { - initArgs() - flag.Parse() - if len(namespace) == 0 || len(service) == 0 { - log.Print("namespace and service are required") - return - } - consumer, err := polaris.NewConsumerAPI() - // 或者使用以下方法,则不需要创建配置文件 - // consumer, err = polaris.NewConsumerAPIByAddress("127.0.0.1:8091") - - if err != nil { - log.Fatalf("fail to create consumerAPI, err is %v", err) - } - defer consumer.Destroy() - - svr := &PolarisConsumer{ - consumer: consumer, - namespace: namespace, - service: service, - } - - svr.Run() - -} diff --git a/examples/circuitbreaker/image/create_circuitbreaker.png b/examples/circuitbreaker/image/create_circuitbreaker.png deleted file mode 100644 index 36a212de..00000000 Binary files a/examples/circuitbreaker/image/create_circuitbreaker.png and /dev/null differ diff --git a/examples/circuitbreaker/README-zh.md b/examples/circuitbreaker/instance/README-zh.md similarity index 67% rename from examples/circuitbreaker/README-zh.md rename to examples/circuitbreaker/instance/README-zh.md index ea8bd655..15b0ee86 100644 --- a/examples/circuitbreaker/README-zh.md +++ b/examples/circuitbreaker/instance/README-zh.md @@ -2,7 +2,7 @@ [English](./README.md) | 中文 -## 使用故障熔断 +## 使用实例故障熔断 北极星支持及时熔断异常的服务、接口、实例或者实例分组,降低请求失败率。 ## 如何使用 @@ -83,27 +83,27 @@ global: -- 第一次发起请求 curl -H 'user-id: polaris' http://127.0.0.1:18080/echo -Hello, I'm CircuitBreakerEchoServer Provider, My host : 127.0.0.1:8888 -Hello, I'm CircuitBreakerEchoServer Provider, My host : 127.0.0.1:9999 +Hello, My host : 127.0.0.1:8888 +Hello, My host : 127.0.0.1:9999 ... -Hello, I'm CircuitBreakerEchoServer Provider, My host : 127.0.0.1:9999 +Hello, My host : 127.0.0.1:9999 -- 关闭某些provider,在发起请求 -Hello, I'm CircuitBreakerEchoServer Provider, My host : 127.0.0.1:9999 -Hello, I'm CircuitBreakerEchoServer Provider, My host : 27.0.0.1:9999 +Hello, My host : 127.0.0.1:9999 +Hello, My host : 27.0.0.1:9999 [errot] send request to 127.0.0.1:8888 fail : %s [errot] send request to 127.0.0.1:8888 fail : %s ... -Hello, I'm CircuitBreakerEchoServer Provider, My host : 27.0.0.1:9999 +Hello, My host : 27.0.0.1:9999 ... -- 触发熔断的 curl 请求, 被熔断的实例不会再被访问 curl -H 'user-id: polaris' http://127.0.0.1:18080/echo -Hello, I'm CircuitBreakerEchoServer Provider, My host : 127.0.0.1:9999 -Hello, I'm CircuitBreakerEchoServer Provider, My host : 27.0.0.1:9999 +Hello, My host : 127.0.0.1:9999 +Hello, My host : 27.0.0.1:9999 ... -Hello, I'm CircuitBreakerEchoServer Provider, My host : 127.0.0.1:9999 +Hello, My host : 127.0.0.1:9999 ``` \ No newline at end of file diff --git a/examples/circuitbreaker/README.md b/examples/circuitbreaker/instance/README.md similarity index 100% rename from examples/circuitbreaker/README.md rename to examples/circuitbreaker/instance/README.md diff --git a/examples/circuitbreaker/consumer/go.mod b/examples/circuitbreaker/instance/consumer/go.mod similarity index 92% rename from examples/circuitbreaker/consumer/go.mod rename to examples/circuitbreaker/instance/consumer/go.mod index d82930af..e511b117 100644 --- a/examples/circuitbreaker/consumer/go.mod +++ b/examples/circuitbreaker/instance/consumer/go.mod @@ -17,7 +17,7 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/natefinch/lumberjack v2.0.0+incompatible // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/polarismesh/specification v1.3.2-alpha.2 // indirect + github.com/polarismesh/specification v1.4.1 // indirect github.com/prometheus/client_golang v1.12.2 // indirect github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.32.1 // indirect @@ -35,4 +35,4 @@ require ( gopkg.in/yaml.v2 v2.4.0 // indirect ) -replace github.com/polarismesh/polaris-go => ../../../ +replace github.com/polarismesh/polaris-go => ../../../../ diff --git a/examples/circuitbreaker/provider/go.sum b/examples/circuitbreaker/instance/consumer/go.sum similarity index 99% rename from examples/circuitbreaker/provider/go.sum rename to examples/circuitbreaker/instance/consumer/go.sum index 093a5191..0a144418 100644 --- a/examples/circuitbreaker/provider/go.sum +++ b/examples/circuitbreaker/instance/consumer/go.sum @@ -375,8 +375,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/polarismesh/specification v1.3.2-alpha.2 h1:cMghyvCnRVM5ca2kYCGHOgIIxVnokiMvw0720q8a8RA= -github.com/polarismesh/specification v1.3.2-alpha.2/go.mod h1:rDvMMtl5qebPmqiBLNa5Ps0XtwkP31ZLirbH4kXA0YU= +github.com/polarismesh/specification v1.4.1 h1:lTZqeyUhhWuKyr6NDKBwmUrNfcUDvKLxWT/uOq71T5A= +github.com/polarismesh/specification v1.4.1/go.mod h1:rDvMMtl5qebPmqiBLNa5Ps0XtwkP31ZLirbH4kXA0YU= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= diff --git a/examples/circuitbreaker/instance/consumer/main.go b/examples/circuitbreaker/instance/consumer/main.go new file mode 100644 index 00000000..16c039f3 --- /dev/null +++ b/examples/circuitbreaker/instance/consumer/main.go @@ -0,0 +1,157 @@ +/** + * 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 main + +import ( + "flag" + "fmt" + "io/ioutil" + "log" + "net/http" + "strconv" + "time" + + "github.com/polarismesh/polaris-go" + "github.com/polarismesh/polaris-go/pkg/model" +) + +var ( + namespace string + service string + port int64 +) + +func initArgs() { + flag.StringVar(&namespace, "namespace", "default", "namespace") + flag.StringVar(&service, "service", "CircuitBreakerEchoServer", "service") + flag.Int64Var(&port, "port", 18080, "port") +} + +// PolarisConsumer is a consumer of the circuit breaker service. +type PolarisConsumer struct { + consumer polaris.ConsumerAPI + circuitbreak polaris.CircuitBreakerAPI + namespace string + service string +} + +// Run is the consumer's main function. +func (svr *PolarisConsumer) Run() { + svr.runWebServer() +} + +func (svr *PolarisConsumer) discoverInstance() (string, model.Resource, error) { + getOneRequest := &polaris.GetOneInstanceRequest{} + getOneRequest.Namespace = namespace + getOneRequest.Service = service + getOneRequest.IncludeCircuitBreakInstances = true + oneInstResp, err := svr.consumer.GetOneInstance(getOneRequest) + if err != nil { + log.Printf("[error] fail to getOneInstance, err is %v", err) + return "", nil, err + } + instance := oneInstResp.GetInstance() + if nil != instance { + log.Printf("instance getOneInstance is %s:%d", instance.GetHost(), instance.GetPort()) + } + + insRes, _ := model.NewInstanceResource(&model.ServiceKey{ + Namespace: namespace, + Service: service, + }, nil, "http", instance.GetHost(), instance.GetPort()) + + return fmt.Sprintf("%s:%d", instance.GetHost(), instance.GetPort()), insRes, nil +} + +func (svr *PolarisConsumer) runWebServer() { + http.HandleFunc("/echo", func(rw http.ResponseWriter, r *http.Request) { + log.Printf("start to invoke getOneInstance operation") + endpoint, insRes, err := svr.discoverInstance() + if err != nil { + rw.WriteHeader(http.StatusInternalServerError) + _, _ = rw.Write([]byte(fmt.Sprintf("[errot] discover instance fail : %s", err))) + return + } + start := time.Now() + resp, err := http.Get(fmt.Sprintf("http://%+v/echo", endpoint)) + if resp != nil { + defer resp.Body.Close() + } + if err != nil { + svr.circuitbreak.Report(&model.ResourceStat{ + Delay: time.Since(start), + RetStatus: model.RetFail, + RetCode: strconv.Itoa(http.StatusInternalServerError), + Resource: insRes, + }) + rw.WriteHeader(http.StatusInternalServerError) + _, _ = rw.Write([]byte(fmt.Sprintf("[errot] discover instance fail : %s", err))) + return + } + if resp.StatusCode != http.StatusOK { + svr.circuitbreak.Report(&model.ResourceStat{ + Delay: time.Since(start), + RetStatus: model.RetFail, + RetCode: strconv.Itoa(resp.StatusCode), + Resource: insRes, + }) + } else { + svr.circuitbreak.Report(&model.ResourceStat{ + Delay: time.Since(start), + RetStatus: model.RetSuccess, + RetCode: strconv.Itoa(http.StatusOK), + Resource: insRes, + }) + } + ret, _ := ioutil.ReadAll(resp.Body) + rw.WriteHeader(http.StatusOK) + _, _ = rw.Write([]byte(ret)) + }) + + log.Printf("start run web server, port : %d", port) + if err := http.ListenAndServe(fmt.Sprintf("0.0.0.0:%d", port), nil); err != nil { + log.Fatalf("[ERROR]fail to run webServer, err is %v", err) + } +} + +func main() { + initArgs() + flag.Parse() + if len(namespace) == 0 || len(service) == 0 { + log.Print("namespace and service are required") + return + } + sdkCtx, err := polaris.NewSDKContext() + if err != nil { + log.Fatalf("fail to create consumerAPI, err is %v", err) + } + consumer := polaris.NewConsumerAPIByContext(sdkCtx) + circuitBreaker := polaris.NewCircuitBreakerAPIByContext(sdkCtx) + defer func() { + sdkCtx.Destroy() + }() + + svr := &PolarisConsumer{ + consumer: consumer, + circuitbreak: circuitBreaker, + namespace: namespace, + service: service, + } + + svr.Run() +} diff --git a/examples/circuitbreaker/consumer/polaris.yaml b/examples/circuitbreaker/instance/consumer/polaris.yaml similarity index 100% rename from examples/circuitbreaker/consumer/polaris.yaml rename to examples/circuitbreaker/instance/consumer/polaris.yaml diff --git a/examples/circuitbreaker/instance/image/create_circuitbreaker.png b/examples/circuitbreaker/instance/image/create_circuitbreaker.png new file mode 100644 index 00000000..7c94a72c Binary files /dev/null and b/examples/circuitbreaker/instance/image/create_circuitbreaker.png differ diff --git a/examples/circuitbreaker/provider/go.mod b/examples/circuitbreaker/instance/provider/go.mod similarity index 92% rename from examples/circuitbreaker/provider/go.mod rename to examples/circuitbreaker/instance/provider/go.mod index 4aeb4a32..3905d3c8 100644 --- a/examples/circuitbreaker/provider/go.mod +++ b/examples/circuitbreaker/instance/provider/go.mod @@ -17,7 +17,7 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/natefinch/lumberjack v2.0.0+incompatible // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/polarismesh/specification v1.3.2-alpha.2 // indirect + github.com/polarismesh/specification v1.4.1 // indirect github.com/prometheus/client_golang v1.12.2 // indirect github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.32.1 // indirect @@ -35,4 +35,4 @@ require ( gopkg.in/yaml.v2 v2.4.0 // indirect ) -replace github.com/polarismesh/polaris-go => ../../../ +replace github.com/polarismesh/polaris-go => ../../../../ diff --git a/examples/circuitbreaker/consumer/go.sum b/examples/circuitbreaker/instance/provider/go.sum similarity index 99% rename from examples/circuitbreaker/consumer/go.sum rename to examples/circuitbreaker/instance/provider/go.sum index 093a5191..0a144418 100644 --- a/examples/circuitbreaker/consumer/go.sum +++ b/examples/circuitbreaker/instance/provider/go.sum @@ -375,8 +375,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/polarismesh/specification v1.3.2-alpha.2 h1:cMghyvCnRVM5ca2kYCGHOgIIxVnokiMvw0720q8a8RA= -github.com/polarismesh/specification v1.3.2-alpha.2/go.mod h1:rDvMMtl5qebPmqiBLNa5Ps0XtwkP31ZLirbH4kXA0YU= +github.com/polarismesh/specification v1.4.1 h1:lTZqeyUhhWuKyr6NDKBwmUrNfcUDvKLxWT/uOq71T5A= +github.com/polarismesh/specification v1.4.1/go.mod h1:rDvMMtl5qebPmqiBLNa5Ps0XtwkP31ZLirbH4kXA0YU= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= diff --git a/examples/circuitbreaker/provider/main.go b/examples/circuitbreaker/instance/provider/main.go similarity index 85% rename from examples/circuitbreaker/provider/main.go rename to examples/circuitbreaker/instance/provider/main.go index 4e5f5dda..9d24dd8c 100644 --- a/examples/circuitbreaker/provider/main.go +++ b/examples/circuitbreaker/instance/provider/main.go @@ -25,6 +25,7 @@ import ( "os" "os/signal" "strings" + "sync/atomic" "syscall" "github.com/polarismesh/polaris-go" @@ -52,6 +53,7 @@ type PolarisProvider struct { service string host string port int + needErr int32 } // Run . execute @@ -68,8 +70,24 @@ func (svr *PolarisProvider) Run() { func (svr *PolarisProvider) runWebServer() { http.HandleFunc("/echo", func(rw http.ResponseWriter, r *http.Request) { + if atomic.LoadInt32(&svr.needErr) == 1 { + rw.WriteHeader(http.StatusInternalServerError) + _, _ = rw.Write([]byte(fmt.Sprintf("Fatal, My host : %s:%d", svr.host, svr.port))) + return + } + + rw.WriteHeader(http.StatusOK) + _, _ = rw.Write([]byte(fmt.Sprintf("Hello, My host : %s:%d", svr.host, svr.port))) + }) + + http.HandleFunc("/switch", func(rw http.ResponseWriter, r *http.Request) { + val := r.URL.Query().Get("openError") + if val == "true" { + atomic.StoreInt32(&svr.needErr, 1) + } else { + atomic.StoreInt32(&svr.needErr, 0) + } rw.WriteHeader(http.StatusOK) - _, _ = rw.Write([]byte(fmt.Sprintf("Hello, I'm CircuitBreakerEchoServer Provider, My host : %s:%d", svr.host, svr.port))) }) ln, err := net.Listen("tcp", fmt.Sprintf("0.0.0.0:%d", port)) @@ -95,7 +113,7 @@ func (svr *PolarisProvider) registerService() { registerRequest.Host = svr.host registerRequest.Port = svr.port registerRequest.ServiceToken = token - resp, err := svr.provider.RegisterInstance(registerRequest) + resp, err := svr.provider.Register(registerRequest) if err != nil { log.Fatalf("fail to register instance, err is %v", err) } diff --git a/examples/circuitbreaker/provider/polaris.yaml b/examples/circuitbreaker/instance/provider/polaris.yaml similarity index 100% rename from examples/circuitbreaker/provider/polaris.yaml rename to examples/circuitbreaker/instance/provider/polaris.yaml diff --git a/examples/circuitbreaker/interface/README.md b/examples/circuitbreaker/interface/README.md new file mode 100644 index 00000000..510e6fd7 --- /dev/null +++ b/examples/circuitbreaker/interface/README.md @@ -0,0 +1,96 @@ +# Polaris Go + +## 使用服务级/接口级故障熔断 + +北极星支持及时熔断异常的服务、接口,降低请求失败率。 +## 如何使用 + +### 构建可执行文件 + +构建 provider + +``` +# linux/mac +cd ./provider +go build -o provider + +# windows +cd ./consumer +go build -o provider.exe +``` + +构建 consumer + +``` +# linux/mac +cd ./consumer +go build -o consumer + +# windows +cd ./consumer +go build -o consumer.exe +``` +### 进入控制台 + +预先通过北极星控制台创建对应的服务,如果是通过本地一键安装包的方式安装,直接在浏览器通过127.0.0.1:8080打开控制台 + +### 设置熔断规则 + +![create_circuitbreaker](./image/create_circuitbreaker.png) + +### 修改配置 + +指定北极星服务端地址,需编辑polaris.yaml文件,填入服务端地址 + +``` +global: + serverConnector: + addresses: + - 127.0.0.1:8091 +``` + +### 执行程序 + +### 执行程序 + +运行构建出的**provider**可执行文件 + +``` +# linux/mac运行命令 +./provider + +# windows运行命令 +./provider.exe +``` + +运行构建出的**consumer**可执行文件 + +``` +# linux/mac运行命令 +./provider + +# windows运行命令 +./provider.exe +``` + +### 验证 + +快速的发起多次**curl**请求命令 + +``` +-- 第一次发起请求 +curl http://127.0.0.1:18080/echo + +"Hello, My host : %s:%d", svr.host, svr.port +"Hello, My host : %s:%d", svr.host, svr.port +... + +-- 关闭某些provider,在发起请求 + +"Hello, My host : %s:%d", svr.host, svr.port +dial tcp 127.0.0.1:37907: connect: connection refused + +<多次调用触发熔断> + +it's fallback +... diff --git a/examples/circuitbreaker/interface/consumer/go.mod b/examples/circuitbreaker/interface/consumer/go.mod new file mode 100644 index 00000000..e511b117 --- /dev/null +++ b/examples/circuitbreaker/interface/consumer/go.mod @@ -0,0 +1,38 @@ +module github.com/polarismesh/polaris-go-circuitbreaker-consumer + +go 1.17 + +require github.com/polarismesh/polaris-go v1.5.5 + +require ( + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/dlclark/regexp2 v1.7.0 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/natefinch/lumberjack v2.0.0+incompatible // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/polarismesh/specification v1.4.1 // indirect + github.com/prometheus/client_golang v1.12.2 // indirect + github.com/prometheus/client_model v0.2.0 // indirect + github.com/prometheus/common v0.32.1 // indirect + github.com/prometheus/procfs v0.7.3 // indirect + github.com/spaolacci/murmur3 v1.1.0 // indirect + go.uber.org/atomic v1.10.0 // indirect + go.uber.org/multierr v1.8.0 // indirect + go.uber.org/zap v1.21.0 // indirect + golang.org/x/net v0.2.0 // indirect + golang.org/x/sys v0.2.0 // indirect + golang.org/x/text v0.4.0 // indirect + google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a // indirect + google.golang.org/grpc v1.51.0 // indirect + google.golang.org/protobuf v1.28.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect +) + +replace github.com/polarismesh/polaris-go => ../../../../ diff --git a/examples/circuitbreaker/interface/consumer/go.sum b/examples/circuitbreaker/interface/consumer/go.sum new file mode 100644 index 00000000..0a144418 --- /dev/null +++ b/examples/circuitbreaker/interface/consumer/go.sum @@ -0,0 +1,967 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= +cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= +cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= +cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= +cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= +cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= +cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= +cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= +cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= +cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= +cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU= +cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA= +cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw= +cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY= +cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI= +cloud.google.com/go/analytics v0.12.0/go.mod h1:gkfj9h6XRf9+TS4bmuhPEShsh3hH8PAZzm/41OOhQd4= +cloud.google.com/go/area120 v0.5.0/go.mod h1:DE/n4mp+iqVyvxHN41Vf1CR602GiHQjFPusMFW6bGR4= +cloud.google.com/go/area120 v0.6.0/go.mod h1:39yFJqWVgm0UZqWTOdqkLhjoC7uFfgXRC8g/ZegeAh0= +cloud.google.com/go/artifactregistry v1.6.0/go.mod h1:IYt0oBPSAGYj/kprzsBjZ/4LnG/zOcHyFHjWPCi6SAQ= +cloud.google.com/go/artifactregistry v1.7.0/go.mod h1:mqTOFOnGZx8EtSqK/ZWcsm/4U8B77rbcLP6ruDU2Ixk= +cloud.google.com/go/asset v1.5.0/go.mod h1:5mfs8UvcM5wHhqtSv8J1CtxxaQq3AdBxxQi2jGW/K4o= +cloud.google.com/go/asset v1.7.0/go.mod h1:YbENsRK4+xTiL+Ofoj5Ckf+O17kJtgp3Y3nn4uzZz5s= +cloud.google.com/go/assuredworkloads v1.5.0/go.mod h1:n8HOZ6pff6re5KYfBXcFvSViQjDwxFkAkmUFffJRbbY= +cloud.google.com/go/assuredworkloads v1.6.0/go.mod h1:yo2YOk37Yc89Rsd5QMVECvjaMKymF9OP+QXWlKXUkXw= +cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0= +cloud.google.com/go/automl v1.6.0/go.mod h1:ugf8a6Fx+zP0D59WLhqgTDsQI9w07o64uf/Is3Nh5p8= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/bigquery v1.42.0/go.mod h1:8dRTJxhtG+vwBKzE5OseQn/hiydoQN3EedCaOdYmxRA= +cloud.google.com/go/billing v1.4.0/go.mod h1:g9IdKBEFlItS8bTtlrZdVLWSSdSyFUZKXNS02zKMOZY= +cloud.google.com/go/billing v1.5.0/go.mod h1:mztb1tBc3QekhjSgmpf/CV4LzWXLzCArwpLmP2Gm88s= +cloud.google.com/go/binaryauthorization v1.1.0/go.mod h1:xwnoWu3Y84jbuHa0zd526MJYmtnVXn0syOjaJgy4+dM= +cloud.google.com/go/binaryauthorization v1.2.0/go.mod h1:86WKkJHtRcv5ViNABtYMhhNWRrD1Vpi//uKEy7aYEfI= +cloud.google.com/go/cloudtasks v1.5.0/go.mod h1:fD92REy1x5woxkKEkLdvavGnPJGEn8Uic9nWuLzqCpY= +cloud.google.com/go/cloudtasks v1.6.0/go.mod h1:C6Io+sxuke9/KNRkbQpihnW93SWDU3uXt92nu85HkYI= +cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= +cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= +cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= +cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= +cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= +cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= +cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I= +cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4= +cloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSiyOzm8kpLq0a0= +cloud.google.com/go/datacatalog v1.5.0/go.mod h1:M7GPLNQeLfWqeIm3iuiruhPzkt65+Bx8dAKvScX8jvs= +cloud.google.com/go/datacatalog v1.6.0/go.mod h1:+aEyF8JKg+uXcIdAmmaMUmZ3q1b/lKLtXCmXdnc0lbc= +cloud.google.com/go/dataflow v0.6.0/go.mod h1:9QwV89cGoxjjSR9/r7eFDqqjtvbKxAK2BaYU6PVk9UM= +cloud.google.com/go/dataflow v0.7.0/go.mod h1:PX526vb4ijFMesO1o202EaUmouZKBpjHsTlCtB4parQ= +cloud.google.com/go/dataform v0.3.0/go.mod h1:cj8uNliRlHpa6L3yVhDOBrUXH+BPAO1+KFMQQNSThKo= +cloud.google.com/go/dataform v0.4.0/go.mod h1:fwV6Y4Ty2yIFL89huYlEkwUPtS7YZinZbzzj5S9FzCE= +cloud.google.com/go/datalabeling v0.5.0/go.mod h1:TGcJ0G2NzcsXSE/97yWjIZO0bXj0KbVlINXMG9ud42I= +cloud.google.com/go/datalabeling v0.6.0/go.mod h1:WqdISuk/+WIGeMkpw/1q7bK/tFEZxsrFJOJdY2bXvTQ= +cloud.google.com/go/dataqna v0.5.0/go.mod h1:90Hyk596ft3zUQ8NkFfvICSIfHFh1Bc7C4cK3vbhkeo= +cloud.google.com/go/dataqna v0.6.0/go.mod h1:1lqNpM7rqNLVgWBJyk5NF6Uen2PHym0jtVJonplVsDA= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/datastream v1.2.0/go.mod h1:i/uTP8/fZwgATHS/XFu0TcNUhuA0twZxxQ3EyCUQMwo= +cloud.google.com/go/datastream v1.3.0/go.mod h1:cqlOX8xlyYF/uxhiKn6Hbv6WjwPPuI9W2M9SAXwaLLQ= +cloud.google.com/go/dialogflow v1.15.0/go.mod h1:HbHDWs33WOGJgn6rfzBW1Kv807BE3O1+xGbn59zZWI4= +cloud.google.com/go/dialogflow v1.16.1/go.mod h1:po6LlzGfK+smoSmTBnbkIZY2w8ffjz/RcGSS+sh1el0= +cloud.google.com/go/documentai v1.7.0/go.mod h1:lJvftZB5NRiFSX4moiye1SMxHx0Bc3x1+p9e/RfXYiU= +cloud.google.com/go/documentai v1.8.0/go.mod h1:xGHNEB7CtsnySCNrCFdCyyMz44RhFEEX2Q7UD0c5IhU= +cloud.google.com/go/domains v0.6.0/go.mod h1:T9Rz3GasrpYk6mEGHh4rymIhjlnIuB4ofT1wTxDeT4Y= +cloud.google.com/go/domains v0.7.0/go.mod h1:PtZeqS1xjnXuRPKE/88Iru/LdfoRyEHYA9nFQf4UKpg= +cloud.google.com/go/edgecontainer v0.1.0/go.mod h1:WgkZ9tp10bFxqO8BLPqv2LlfmQF1X8lZqwW4r1BTajk= +cloud.google.com/go/functions v1.6.0/go.mod h1:3H1UA3qiIPRWD7PeZKLvHZ9SaQhR26XIJcC0A5GbvAk= +cloud.google.com/go/functions v1.7.0/go.mod h1:+d+QBcWM+RsrgZfV9xo6KfA1GlzJfxcfZcRPEhDDfzg= +cloud.google.com/go/gaming v1.5.0/go.mod h1:ol7rGcxP/qHTRQE/RO4bxkXq+Fix0j6D4LFPzYTIrDM= +cloud.google.com/go/gaming v1.6.0/go.mod h1:YMU1GEvA39Qt3zWGyAVA9bpYz/yAhTvaQ1t2sK4KPUA= +cloud.google.com/go/gkeconnect v0.5.0/go.mod h1:c5lsNAg5EwAy7fkqX/+goqFsU1Da/jQFqArp+wGNr/o= +cloud.google.com/go/gkeconnect v0.6.0/go.mod h1:Mln67KyU/sHJEBY8kFZ0xTeyPtzbq9StAVvEULYK16A= +cloud.google.com/go/gkehub v0.9.0/go.mod h1:WYHN6WG8w9bXU0hqNxt8rm5uxnk8IH+lPY9J2TV7BK0= +cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y977wO+hBH0= +cloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc= +cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= +cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic= +cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI= +cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8= +cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08= +cloud.google.com/go/mediatranslation v0.5.0/go.mod h1:jGPUhGTybqsPQn91pNXw0xVHfuJ3leR1wj37oU3y1f4= +cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w= +cloud.google.com/go/memcache v1.4.0/go.mod h1:rTOfiGZtJX1AaFUrOgsMHX5kAzaTQ8azHiuDoTPzNsE= +cloud.google.com/go/memcache v1.5.0/go.mod h1:dk3fCK7dVo0cUU2c36jKb4VqKPS22BTkf81Xq617aWM= +cloud.google.com/go/metastore v1.5.0/go.mod h1:2ZNrDcQwghfdtCwJ33nM0+GrBGlVuh8rakL3vdPY3XY= +cloud.google.com/go/metastore v1.6.0/go.mod h1:6cyQTls8CWXzk45G55x57DVQ9gWg7RiH65+YgPsNh9s= +cloud.google.com/go/networkconnectivity v1.4.0/go.mod h1:nOl7YL8odKyAOtzNX73/M5/mGZgqqMeryi6UPZTk/rA= +cloud.google.com/go/networkconnectivity v1.5.0/go.mod h1:3GzqJx7uhtlM3kln0+x5wyFvuVH1pIBJjhCpjzSt75o= +cloud.google.com/go/networksecurity v0.5.0/go.mod h1:xS6fOCoqpVC5zx15Z/MqkfDwH4+m/61A3ODiDV1xmiQ= +cloud.google.com/go/networksecurity v0.6.0/go.mod h1:Q5fjhTr9WMI5mbpRYEbiexTzROf7ZbDzvzCrNl14nyU= +cloud.google.com/go/notebooks v1.2.0/go.mod h1:9+wtppMfVPUeJ8fIWPOq1UnATHISkGXGqTkxeieQ6UY= +cloud.google.com/go/notebooks v1.3.0/go.mod h1:bFR5lj07DtCPC7YAAJ//vHskFBxA5JzYlH68kXVdk34= +cloud.google.com/go/osconfig v1.7.0/go.mod h1:oVHeCeZELfJP7XLxcBGTMBvRO+1nQ5tFG9VQTmYS2Fs= +cloud.google.com/go/osconfig v1.8.0/go.mod h1:EQqZLu5w5XA7eKizepumcvWx+m8mJUhEwiPqWiZeEdg= +cloud.google.com/go/oslogin v1.4.0/go.mod h1:YdgMXWRaElXz/lDk1Na6Fh5orF7gvmJ0FGLIs9LId4E= +cloud.google.com/go/oslogin v1.5.0/go.mod h1:D260Qj11W2qx/HVF29zBg+0fd6YCSjSqLUkY/qEenQU= +cloud.google.com/go/phishingprotection v0.5.0/go.mod h1:Y3HZknsK9bc9dMi+oE8Bim0lczMU6hrX0UpADuMefr0= +cloud.google.com/go/phishingprotection v0.6.0/go.mod h1:9Y3LBLgy0kDTcYET8ZH3bq/7qni15yVUoAxiFxnlSUA= +cloud.google.com/go/privatecatalog v0.5.0/go.mod h1:XgosMUvvPyxDjAVNDYxJ7wBW8//hLDDYmnsNcMGq1K0= +cloud.google.com/go/privatecatalog v0.6.0/go.mod h1:i/fbkZR0hLN29eEWiiwue8Pb+GforiEIBnV9yrRUOKI= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/recaptchaenterprise v1.3.1/go.mod h1:OdD+q+y4XGeAlxRaMn1Y7/GveP6zmq76byL6tjPE7d4= +cloud.google.com/go/recaptchaenterprise/v2 v2.1.0/go.mod h1:w9yVqajwroDNTfGuhmOjPDN//rZGySaf6PtFVcSCa7o= +cloud.google.com/go/recaptchaenterprise/v2 v2.2.0/go.mod h1:/Zu5jisWGeERrd5HnlS3EUGb/D335f9k51B/FVil0jk= +cloud.google.com/go/recommendationengine v0.5.0/go.mod h1:E5756pJcVFeVgaQv3WNpImkFP8a+RptV6dDLGPILjvg= +cloud.google.com/go/recommendationengine v0.6.0/go.mod h1:08mq2umu9oIqc7tDy8sx+MNJdLG0fUi3vaSVbztHgJ4= +cloud.google.com/go/recommender v1.5.0/go.mod h1:jdoeiBIVrJe9gQjwd759ecLJbxCDED4A6p+mqoqDvTg= +cloud.google.com/go/recommender v1.6.0/go.mod h1:+yETpm25mcoiECKh9DEScGzIRyDKpZ0cEhWGo+8bo+c= +cloud.google.com/go/redis v1.7.0/go.mod h1:V3x5Jq1jzUcg+UNsRvdmsfuFnit1cfe3Z/PGyq/lm4Y= +cloud.google.com/go/redis v1.8.0/go.mod h1:Fm2szCDavWzBk2cDKxrkmWBqoCiL1+Ctwq7EyqBCA/A= +cloud.google.com/go/retail v1.8.0/go.mod h1:QblKS8waDmNUhghY2TI9O3JLlFk8jybHeV4BF19FrE4= +cloud.google.com/go/retail v1.9.0/go.mod h1:g6jb6mKuCS1QKnH/dpu7isX253absFl6iE92nHwlBUY= +cloud.google.com/go/scheduler v1.4.0/go.mod h1:drcJBmxF3aqZJRhmkHQ9b3uSSpQoltBPGPxGAWROx6s= +cloud.google.com/go/scheduler v1.5.0/go.mod h1:ri073ym49NW3AfT6DZi21vLZrG07GXr5p3H1KxN5QlI= +cloud.google.com/go/secretmanager v1.6.0/go.mod h1:awVa/OXF6IiyaU1wQ34inzQNc4ISIDIrId8qE5QGgKA= +cloud.google.com/go/security v1.5.0/go.mod h1:lgxGdyOKKjHL4YG3/YwIL2zLqMFCKs0UbQwgyZmfJl4= +cloud.google.com/go/security v1.7.0/go.mod h1:mZklORHl6Bg7CNnnjLH//0UlAlaXqiG7Lb9PsPXLfD0= +cloud.google.com/go/security v1.8.0/go.mod h1:hAQOwgmaHhztFhiQ41CjDODdWP0+AE1B3sX4OFlq+GU= +cloud.google.com/go/securitycenter v1.13.0/go.mod h1:cv5qNAqjY84FCN6Y9z28WlkKXyWsgLO832YiWwkCWcU= +cloud.google.com/go/securitycenter v1.14.0/go.mod h1:gZLAhtyKv85n52XYWt6RmeBdydyxfPeTrpToDPw4Auc= +cloud.google.com/go/servicedirectory v1.4.0/go.mod h1:gH1MUaZCgtP7qQiI+F+A+OpeKF/HQWgtAddhTbhL2bs= +cloud.google.com/go/servicedirectory v1.5.0/go.mod h1:QMKFL0NUySbpZJ1UZs3oFAmdvVxhhxB6eJ/Vlp73dfg= +cloud.google.com/go/speech v1.6.0/go.mod h1:79tcr4FHCimOp56lwC01xnt/WPJZc4v3gzyT7FoBkCM= +cloud.google.com/go/speech v1.7.0/go.mod h1:KptqL+BAQIhMsj1kOP2la5DSEEerPDuOP/2mmkhHhZQ= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= +cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc= +cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw= +cloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g= +cloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU= +cloud.google.com/go/videointelligence v1.7.0/go.mod h1:k8pI/1wAhjznARtVT9U1llUaFNPh7muw8QyOUpavru4= +cloud.google.com/go/vision v1.2.0/go.mod h1:SmNwgObm5DpFBme2xpyOyasvBc1aPdjvMk2bBk0tKD0= +cloud.google.com/go/vision/v2 v2.2.0/go.mod h1:uCdV4PpN1S0jyCyq8sIM42v2Y6zOLkZs+4R9LrGYwFo= +cloud.google.com/go/vision/v2 v2.3.0/go.mod h1:UO61abBx9QRMFkNBbf1D8B1LXdS2cGiiCRx0vSpZoUo= +cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xXZmFiHmGE= +cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg= +cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0= +cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/agiledragon/gomonkey v2.0.2+incompatible h1:eXKi9/piiC3cjJD1658mEE2o3NjkJ5vDLgYjCQu0Xlw= +github.com/agiledragon/gomonkey v2.0.2+incompatible/go.mod h1:2NGfXu1a80LLr2cmWXGBDaHEjb1idR6+FVlX5T3D9hw= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dlclark/regexp2 v1.7.0 h1:7lJfhqlPssTb1WQx4yvTHN0uElPEv52sbaECrAQxjAo= +github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/gonum/blas v0.0.0-20181208220705-f22b278b28ac/go.mod h1:P32wAyui1PQ58Oce/KYkOqQv8cVw1zAapXOl+dRFGbc= +github.com/gonum/floats v0.0.0-20181209220543-c233463c7e82/go.mod h1:PxC8OnwL11+aosOB5+iEPoV3picfs8tUpkVd0pDo+Kg= +github.com/gonum/integrate v0.0.0-20181209220457-a422b5c0fdf2/go.mod h1:pDgmNM6seYpwvPos3q+zxlXMsbve6mOIPucUnUOrI7Y= +github.com/gonum/internal v0.0.0-20181124074243-f884aa714029/go.mod h1:Pu4dmpkhSyOzRwuXkOgAvijx4o+4YMUJJo9OvPYMkks= +github.com/gonum/lapack v0.0.0-20181123203213-e4cdc5a0bff9/go.mod h1:XA3DeT6rxh2EAE789SSiSJNqxPaC0aE9J8NTOI0Jo/A= +github.com/gonum/matrix v0.0.0-20181209220409-c518dec07be9/go.mod h1:0EXg4mc1CNP0HCqCz+K4ts155PXIlUywf0wqN+GfPZw= +github.com/gonum/stat v0.0.0-20181125101827-41a0da705a5b/go.mod h1:Z4GIJBJO3Wa4gD4vbwQxXXZ+WHmW6E9ixmNrwvs0iZs= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= +github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= +github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= +github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= +github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= +github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= +github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo= +github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= +github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM= +github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/polarismesh/specification v1.4.1 h1:lTZqeyUhhWuKyr6NDKBwmUrNfcUDvKLxWT/uOq71T5A= +github.com/polarismesh/specification v1.4.1/go.mod h1:rDvMMtl5qebPmqiBLNa5Ps0XtwkP31ZLirbH4kXA0YU= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_golang v1.12.2 h1:51L9cDoUHVrXx4zWYlcLQIZ+d+VXHgqnYKkIuq4g/34= +github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4= +github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= +github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs= +github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= +github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg= +github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= +go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= +go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= +go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= +go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8= +go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= +golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= +golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= +google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= +google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= +google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= +google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= +google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= +google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= +google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= +google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= +google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= +google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= +google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= +google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= +google.golang.org/api v0.77.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= +google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw= +google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg= +google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o= +google.golang.org/api v0.85.0/go.mod h1:AqZf8Ep9uZ2pyTvgL+x0D3Zt0eoT9b5E8fmzfu6FO2g= +google.golang.org/api v0.90.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= +google.golang.org/api v0.93.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= +google.golang.org/api v0.95.0/go.mod h1:eADj+UBuxkh5zlrSntJghuNeg8HwQ1w5lTKkuqaETEI= +google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= +google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= +google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= +google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220722212130-b98a9ff5e252/go.mod h1:GkXuJDJ6aQ7lnJcRF+SJVgFdQhypqgl3LB1C9vabdRE= +google.golang.org/genproto v0.0.0-20220801145646-83ce21fca29f/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc= +google.golang.org/genproto v0.0.0-20220815135757-37a418bb8959/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220817144833-d7fd3f11b9b1/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220829144015-23454907ede3/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220913154956-18f8339a66a5/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220914142337-ca0e39ece12f/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220915135415-7fd63a7952de/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220916172020-2692e8806bfa/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220919141832-68c03719ef51/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006/go.mod h1:ht8XFiar2npT/g4vkk7O0WYS1sHOHbdujxbEp7CJWbw= +google.golang.org/genproto v0.0.0-20220926165614-551eb538f295/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= +google.golang.org/genproto v0.0.0-20220926220553-6981cbe3cfce/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= +google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a h1:GH6UPn3ixhWcKDhpnEC55S75cerLPdpp3hrhfKYjZgw= +google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= +google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U= +google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= +gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/examples/circuitbreaker/interface/consumer/main.go b/examples/circuitbreaker/interface/consumer/main.go new file mode 100644 index 00000000..54378546 --- /dev/null +++ b/examples/circuitbreaker/interface/consumer/main.go @@ -0,0 +1,153 @@ +/** + * 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 main + +import ( + "context" + "flag" + "fmt" + "io/ioutil" + "log" + "net/http" + + "github.com/polarismesh/polaris-go" + "github.com/polarismesh/polaris-go/api" + "github.com/polarismesh/polaris-go/pkg/model" +) + +var ( + namespace string + service string + port int64 +) + +func initArgs() { + flag.StringVar(&namespace, "namespace", "default", "namespace") + flag.StringVar(&service, "service", "CircuitBreakerInterfaceServer", "service") + flag.Int64Var(&port, "port", 18080, "port") +} + +// PolarisConsumer is a consumer of the circuit breaker service. +type PolarisConsumer struct { + consumer polaris.ConsumerAPI + circuitbreak polaris.CircuitBreakerAPI + namespace string + service string +} + +// Run is the consumer's main function. +func (svr *PolarisConsumer) Run() { + svr.runWebServer() +} + +func (svr *PolarisConsumer) discoverInstance() (string, error) { + getOneRequest := &polaris.GetOneInstanceRequest{} + getOneRequest.Namespace = namespace + getOneRequest.Service = service + oneInstResp, err := svr.consumer.GetOneInstance(getOneRequest) + if err != nil { + log.Printf("[error] fail to getOneInstance, err is %v", err) + return "", err + } + instance := oneInstResp.GetInstance() + if nil != instance { + log.Printf("instance getOneInstance is %s:%d", instance.GetHost(), instance.GetPort()) + } + return fmt.Sprintf("%s:%d", instance.GetHost(), instance.GetPort()), nil +} + +func (svr *PolarisConsumer) runWebServer() { + dealF := svr.circuitbreak.MakeFunctionDecorator(func(ctx context.Context, args interface{}) (interface{}, error) { + resp, err := http.Get(fmt.Sprintf("http://%+v/echo", args)) + if resp != nil { + defer resp.Body.Close() + } + if err != nil { + return nil, err + } + data, _ := ioutil.ReadAll(resp.Body) + return string(data), nil + }, &api.RequestContext{ + RequestContext: model.RequestContext{ + Callee: &model.ServiceKey{ + Namespace: namespace, + Service: service, + }, + Method: "/echo", + }, + }) + + http.HandleFunc("/echo", func(rw http.ResponseWriter, r *http.Request) { + log.Printf("start to invoke getOneInstance operation") + endpoint, err := svr.discoverInstance() + if err != nil { + rw.WriteHeader(http.StatusInternalServerError) + _, _ = rw.Write([]byte(fmt.Sprintf("[errot] discover instance fail : %s", err))) + return + } + ret, abort, err := dealF(context.Background(), endpoint) + if err != nil { + rw.WriteHeader(http.StatusInternalServerError) + _, _ = rw.Write([]byte(fmt.Sprintf("[errot] fail : %s", err))) + return + } + if abort != nil { + rw.WriteHeader(abort.GetFallbackCode()) + for k, v := range abort.GetFallbackHeaders() { + rw.Header().Add(k, v) + } + _, _ = rw.Write([]byte(abort.GetFallbackBody())) + return + } + rw.WriteHeader(http.StatusOK) + _, _ = rw.Write([]byte(ret.(string))) + return + }) + log.Printf("start run web server, port : %d", port) + if err := http.ListenAndServe(fmt.Sprintf("0.0.0.0:%d", port), nil); err != nil { + log.Fatalf("[ERROR]fail to run webServer, err is %v", err) + } +} + +func main() { + initArgs() + flag.Parse() + if len(namespace) == 0 || len(service) == 0 { + log.Print("namespace and service are required") + return + } + sdkCtx, err := polaris.NewSDKContext() + if err != nil { + log.Fatalf("fail to create consumerAPI, err is %v", err) + } + consumer := polaris.NewConsumerAPIByContext(sdkCtx) + circuitBreaker := polaris.NewCircuitBreakerAPIByContext(sdkCtx) + defer func() { + sdkCtx.Destroy() + }() + + svr := &PolarisConsumer{ + consumer: consumer, + circuitbreak: circuitBreaker, + namespace: namespace, + service: service, + } + + svr.Run() + +} diff --git a/examples/circuitbreaker/interface/consumer/polaris.yaml b/examples/circuitbreaker/interface/consumer/polaris.yaml new file mode 100644 index 00000000..07e08516 --- /dev/null +++ b/examples/circuitbreaker/interface/consumer/polaris.yaml @@ -0,0 +1,12 @@ +global: + serverConnector: + addresses: + - 127.0.0.1:8091 + statReporter: + enable: true + chain: + - prometheus + plugin: + prometheus: + type: pull + metricPort: 0 diff --git a/examples/circuitbreaker/interface/image/create_circuitbreaker.png b/examples/circuitbreaker/interface/image/create_circuitbreaker.png new file mode 100644 index 00000000..49fee1a0 Binary files /dev/null and b/examples/circuitbreaker/interface/image/create_circuitbreaker.png differ diff --git a/examples/circuitbreaker/interface/provider/go.mod b/examples/circuitbreaker/interface/provider/go.mod new file mode 100644 index 00000000..3905d3c8 --- /dev/null +++ b/examples/circuitbreaker/interface/provider/go.mod @@ -0,0 +1,38 @@ +module github.com/polarismesh/polaris-go-circuitbreaker-provider + +go 1.17 + +require github.com/polarismesh/polaris-go v1.5.5 + +require ( + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/dlclark/regexp2 v1.7.0 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/natefinch/lumberjack v2.0.0+incompatible // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/polarismesh/specification v1.4.1 // indirect + github.com/prometheus/client_golang v1.12.2 // indirect + github.com/prometheus/client_model v0.2.0 // indirect + github.com/prometheus/common v0.32.1 // indirect + github.com/prometheus/procfs v0.7.3 // indirect + github.com/spaolacci/murmur3 v1.1.0 // indirect + go.uber.org/atomic v1.10.0 // indirect + go.uber.org/multierr v1.8.0 // indirect + go.uber.org/zap v1.21.0 // indirect + golang.org/x/net v0.2.0 // indirect + golang.org/x/sys v0.2.0 // indirect + golang.org/x/text v0.4.0 // indirect + google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a // indirect + google.golang.org/grpc v1.51.0 // indirect + google.golang.org/protobuf v1.28.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect +) + +replace github.com/polarismesh/polaris-go => ../../../../ diff --git a/examples/circuitbreaker/interface/provider/go.sum b/examples/circuitbreaker/interface/provider/go.sum new file mode 100644 index 00000000..0a144418 --- /dev/null +++ b/examples/circuitbreaker/interface/provider/go.sum @@ -0,0 +1,967 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= +cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= +cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= +cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= +cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= +cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= +cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= +cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= +cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= +cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= +cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU= +cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA= +cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw= +cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY= +cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI= +cloud.google.com/go/analytics v0.12.0/go.mod h1:gkfj9h6XRf9+TS4bmuhPEShsh3hH8PAZzm/41OOhQd4= +cloud.google.com/go/area120 v0.5.0/go.mod h1:DE/n4mp+iqVyvxHN41Vf1CR602GiHQjFPusMFW6bGR4= +cloud.google.com/go/area120 v0.6.0/go.mod h1:39yFJqWVgm0UZqWTOdqkLhjoC7uFfgXRC8g/ZegeAh0= +cloud.google.com/go/artifactregistry v1.6.0/go.mod h1:IYt0oBPSAGYj/kprzsBjZ/4LnG/zOcHyFHjWPCi6SAQ= +cloud.google.com/go/artifactregistry v1.7.0/go.mod h1:mqTOFOnGZx8EtSqK/ZWcsm/4U8B77rbcLP6ruDU2Ixk= +cloud.google.com/go/asset v1.5.0/go.mod h1:5mfs8UvcM5wHhqtSv8J1CtxxaQq3AdBxxQi2jGW/K4o= +cloud.google.com/go/asset v1.7.0/go.mod h1:YbENsRK4+xTiL+Ofoj5Ckf+O17kJtgp3Y3nn4uzZz5s= +cloud.google.com/go/assuredworkloads v1.5.0/go.mod h1:n8HOZ6pff6re5KYfBXcFvSViQjDwxFkAkmUFffJRbbY= +cloud.google.com/go/assuredworkloads v1.6.0/go.mod h1:yo2YOk37Yc89Rsd5QMVECvjaMKymF9OP+QXWlKXUkXw= +cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0= +cloud.google.com/go/automl v1.6.0/go.mod h1:ugf8a6Fx+zP0D59WLhqgTDsQI9w07o64uf/Is3Nh5p8= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/bigquery v1.42.0/go.mod h1:8dRTJxhtG+vwBKzE5OseQn/hiydoQN3EedCaOdYmxRA= +cloud.google.com/go/billing v1.4.0/go.mod h1:g9IdKBEFlItS8bTtlrZdVLWSSdSyFUZKXNS02zKMOZY= +cloud.google.com/go/billing v1.5.0/go.mod h1:mztb1tBc3QekhjSgmpf/CV4LzWXLzCArwpLmP2Gm88s= +cloud.google.com/go/binaryauthorization v1.1.0/go.mod h1:xwnoWu3Y84jbuHa0zd526MJYmtnVXn0syOjaJgy4+dM= +cloud.google.com/go/binaryauthorization v1.2.0/go.mod h1:86WKkJHtRcv5ViNABtYMhhNWRrD1Vpi//uKEy7aYEfI= +cloud.google.com/go/cloudtasks v1.5.0/go.mod h1:fD92REy1x5woxkKEkLdvavGnPJGEn8Uic9nWuLzqCpY= +cloud.google.com/go/cloudtasks v1.6.0/go.mod h1:C6Io+sxuke9/KNRkbQpihnW93SWDU3uXt92nu85HkYI= +cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= +cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= +cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= +cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= +cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= +cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= +cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I= +cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4= +cloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSiyOzm8kpLq0a0= +cloud.google.com/go/datacatalog v1.5.0/go.mod h1:M7GPLNQeLfWqeIm3iuiruhPzkt65+Bx8dAKvScX8jvs= +cloud.google.com/go/datacatalog v1.6.0/go.mod h1:+aEyF8JKg+uXcIdAmmaMUmZ3q1b/lKLtXCmXdnc0lbc= +cloud.google.com/go/dataflow v0.6.0/go.mod h1:9QwV89cGoxjjSR9/r7eFDqqjtvbKxAK2BaYU6PVk9UM= +cloud.google.com/go/dataflow v0.7.0/go.mod h1:PX526vb4ijFMesO1o202EaUmouZKBpjHsTlCtB4parQ= +cloud.google.com/go/dataform v0.3.0/go.mod h1:cj8uNliRlHpa6L3yVhDOBrUXH+BPAO1+KFMQQNSThKo= +cloud.google.com/go/dataform v0.4.0/go.mod h1:fwV6Y4Ty2yIFL89huYlEkwUPtS7YZinZbzzj5S9FzCE= +cloud.google.com/go/datalabeling v0.5.0/go.mod h1:TGcJ0G2NzcsXSE/97yWjIZO0bXj0KbVlINXMG9ud42I= +cloud.google.com/go/datalabeling v0.6.0/go.mod h1:WqdISuk/+WIGeMkpw/1q7bK/tFEZxsrFJOJdY2bXvTQ= +cloud.google.com/go/dataqna v0.5.0/go.mod h1:90Hyk596ft3zUQ8NkFfvICSIfHFh1Bc7C4cK3vbhkeo= +cloud.google.com/go/dataqna v0.6.0/go.mod h1:1lqNpM7rqNLVgWBJyk5NF6Uen2PHym0jtVJonplVsDA= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/datastream v1.2.0/go.mod h1:i/uTP8/fZwgATHS/XFu0TcNUhuA0twZxxQ3EyCUQMwo= +cloud.google.com/go/datastream v1.3.0/go.mod h1:cqlOX8xlyYF/uxhiKn6Hbv6WjwPPuI9W2M9SAXwaLLQ= +cloud.google.com/go/dialogflow v1.15.0/go.mod h1:HbHDWs33WOGJgn6rfzBW1Kv807BE3O1+xGbn59zZWI4= +cloud.google.com/go/dialogflow v1.16.1/go.mod h1:po6LlzGfK+smoSmTBnbkIZY2w8ffjz/RcGSS+sh1el0= +cloud.google.com/go/documentai v1.7.0/go.mod h1:lJvftZB5NRiFSX4moiye1SMxHx0Bc3x1+p9e/RfXYiU= +cloud.google.com/go/documentai v1.8.0/go.mod h1:xGHNEB7CtsnySCNrCFdCyyMz44RhFEEX2Q7UD0c5IhU= +cloud.google.com/go/domains v0.6.0/go.mod h1:T9Rz3GasrpYk6mEGHh4rymIhjlnIuB4ofT1wTxDeT4Y= +cloud.google.com/go/domains v0.7.0/go.mod h1:PtZeqS1xjnXuRPKE/88Iru/LdfoRyEHYA9nFQf4UKpg= +cloud.google.com/go/edgecontainer v0.1.0/go.mod h1:WgkZ9tp10bFxqO8BLPqv2LlfmQF1X8lZqwW4r1BTajk= +cloud.google.com/go/functions v1.6.0/go.mod h1:3H1UA3qiIPRWD7PeZKLvHZ9SaQhR26XIJcC0A5GbvAk= +cloud.google.com/go/functions v1.7.0/go.mod h1:+d+QBcWM+RsrgZfV9xo6KfA1GlzJfxcfZcRPEhDDfzg= +cloud.google.com/go/gaming v1.5.0/go.mod h1:ol7rGcxP/qHTRQE/RO4bxkXq+Fix0j6D4LFPzYTIrDM= +cloud.google.com/go/gaming v1.6.0/go.mod h1:YMU1GEvA39Qt3zWGyAVA9bpYz/yAhTvaQ1t2sK4KPUA= +cloud.google.com/go/gkeconnect v0.5.0/go.mod h1:c5lsNAg5EwAy7fkqX/+goqFsU1Da/jQFqArp+wGNr/o= +cloud.google.com/go/gkeconnect v0.6.0/go.mod h1:Mln67KyU/sHJEBY8kFZ0xTeyPtzbq9StAVvEULYK16A= +cloud.google.com/go/gkehub v0.9.0/go.mod h1:WYHN6WG8w9bXU0hqNxt8rm5uxnk8IH+lPY9J2TV7BK0= +cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y977wO+hBH0= +cloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc= +cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= +cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic= +cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI= +cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8= +cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08= +cloud.google.com/go/mediatranslation v0.5.0/go.mod h1:jGPUhGTybqsPQn91pNXw0xVHfuJ3leR1wj37oU3y1f4= +cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w= +cloud.google.com/go/memcache v1.4.0/go.mod h1:rTOfiGZtJX1AaFUrOgsMHX5kAzaTQ8azHiuDoTPzNsE= +cloud.google.com/go/memcache v1.5.0/go.mod h1:dk3fCK7dVo0cUU2c36jKb4VqKPS22BTkf81Xq617aWM= +cloud.google.com/go/metastore v1.5.0/go.mod h1:2ZNrDcQwghfdtCwJ33nM0+GrBGlVuh8rakL3vdPY3XY= +cloud.google.com/go/metastore v1.6.0/go.mod h1:6cyQTls8CWXzk45G55x57DVQ9gWg7RiH65+YgPsNh9s= +cloud.google.com/go/networkconnectivity v1.4.0/go.mod h1:nOl7YL8odKyAOtzNX73/M5/mGZgqqMeryi6UPZTk/rA= +cloud.google.com/go/networkconnectivity v1.5.0/go.mod h1:3GzqJx7uhtlM3kln0+x5wyFvuVH1pIBJjhCpjzSt75o= +cloud.google.com/go/networksecurity v0.5.0/go.mod h1:xS6fOCoqpVC5zx15Z/MqkfDwH4+m/61A3ODiDV1xmiQ= +cloud.google.com/go/networksecurity v0.6.0/go.mod h1:Q5fjhTr9WMI5mbpRYEbiexTzROf7ZbDzvzCrNl14nyU= +cloud.google.com/go/notebooks v1.2.0/go.mod h1:9+wtppMfVPUeJ8fIWPOq1UnATHISkGXGqTkxeieQ6UY= +cloud.google.com/go/notebooks v1.3.0/go.mod h1:bFR5lj07DtCPC7YAAJ//vHskFBxA5JzYlH68kXVdk34= +cloud.google.com/go/osconfig v1.7.0/go.mod h1:oVHeCeZELfJP7XLxcBGTMBvRO+1nQ5tFG9VQTmYS2Fs= +cloud.google.com/go/osconfig v1.8.0/go.mod h1:EQqZLu5w5XA7eKizepumcvWx+m8mJUhEwiPqWiZeEdg= +cloud.google.com/go/oslogin v1.4.0/go.mod h1:YdgMXWRaElXz/lDk1Na6Fh5orF7gvmJ0FGLIs9LId4E= +cloud.google.com/go/oslogin v1.5.0/go.mod h1:D260Qj11W2qx/HVF29zBg+0fd6YCSjSqLUkY/qEenQU= +cloud.google.com/go/phishingprotection v0.5.0/go.mod h1:Y3HZknsK9bc9dMi+oE8Bim0lczMU6hrX0UpADuMefr0= +cloud.google.com/go/phishingprotection v0.6.0/go.mod h1:9Y3LBLgy0kDTcYET8ZH3bq/7qni15yVUoAxiFxnlSUA= +cloud.google.com/go/privatecatalog v0.5.0/go.mod h1:XgosMUvvPyxDjAVNDYxJ7wBW8//hLDDYmnsNcMGq1K0= +cloud.google.com/go/privatecatalog v0.6.0/go.mod h1:i/fbkZR0hLN29eEWiiwue8Pb+GforiEIBnV9yrRUOKI= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/recaptchaenterprise v1.3.1/go.mod h1:OdD+q+y4XGeAlxRaMn1Y7/GveP6zmq76byL6tjPE7d4= +cloud.google.com/go/recaptchaenterprise/v2 v2.1.0/go.mod h1:w9yVqajwroDNTfGuhmOjPDN//rZGySaf6PtFVcSCa7o= +cloud.google.com/go/recaptchaenterprise/v2 v2.2.0/go.mod h1:/Zu5jisWGeERrd5HnlS3EUGb/D335f9k51B/FVil0jk= +cloud.google.com/go/recommendationengine v0.5.0/go.mod h1:E5756pJcVFeVgaQv3WNpImkFP8a+RptV6dDLGPILjvg= +cloud.google.com/go/recommendationengine v0.6.0/go.mod h1:08mq2umu9oIqc7tDy8sx+MNJdLG0fUi3vaSVbztHgJ4= +cloud.google.com/go/recommender v1.5.0/go.mod h1:jdoeiBIVrJe9gQjwd759ecLJbxCDED4A6p+mqoqDvTg= +cloud.google.com/go/recommender v1.6.0/go.mod h1:+yETpm25mcoiECKh9DEScGzIRyDKpZ0cEhWGo+8bo+c= +cloud.google.com/go/redis v1.7.0/go.mod h1:V3x5Jq1jzUcg+UNsRvdmsfuFnit1cfe3Z/PGyq/lm4Y= +cloud.google.com/go/redis v1.8.0/go.mod h1:Fm2szCDavWzBk2cDKxrkmWBqoCiL1+Ctwq7EyqBCA/A= +cloud.google.com/go/retail v1.8.0/go.mod h1:QblKS8waDmNUhghY2TI9O3JLlFk8jybHeV4BF19FrE4= +cloud.google.com/go/retail v1.9.0/go.mod h1:g6jb6mKuCS1QKnH/dpu7isX253absFl6iE92nHwlBUY= +cloud.google.com/go/scheduler v1.4.0/go.mod h1:drcJBmxF3aqZJRhmkHQ9b3uSSpQoltBPGPxGAWROx6s= +cloud.google.com/go/scheduler v1.5.0/go.mod h1:ri073ym49NW3AfT6DZi21vLZrG07GXr5p3H1KxN5QlI= +cloud.google.com/go/secretmanager v1.6.0/go.mod h1:awVa/OXF6IiyaU1wQ34inzQNc4ISIDIrId8qE5QGgKA= +cloud.google.com/go/security v1.5.0/go.mod h1:lgxGdyOKKjHL4YG3/YwIL2zLqMFCKs0UbQwgyZmfJl4= +cloud.google.com/go/security v1.7.0/go.mod h1:mZklORHl6Bg7CNnnjLH//0UlAlaXqiG7Lb9PsPXLfD0= +cloud.google.com/go/security v1.8.0/go.mod h1:hAQOwgmaHhztFhiQ41CjDODdWP0+AE1B3sX4OFlq+GU= +cloud.google.com/go/securitycenter v1.13.0/go.mod h1:cv5qNAqjY84FCN6Y9z28WlkKXyWsgLO832YiWwkCWcU= +cloud.google.com/go/securitycenter v1.14.0/go.mod h1:gZLAhtyKv85n52XYWt6RmeBdydyxfPeTrpToDPw4Auc= +cloud.google.com/go/servicedirectory v1.4.0/go.mod h1:gH1MUaZCgtP7qQiI+F+A+OpeKF/HQWgtAddhTbhL2bs= +cloud.google.com/go/servicedirectory v1.5.0/go.mod h1:QMKFL0NUySbpZJ1UZs3oFAmdvVxhhxB6eJ/Vlp73dfg= +cloud.google.com/go/speech v1.6.0/go.mod h1:79tcr4FHCimOp56lwC01xnt/WPJZc4v3gzyT7FoBkCM= +cloud.google.com/go/speech v1.7.0/go.mod h1:KptqL+BAQIhMsj1kOP2la5DSEEerPDuOP/2mmkhHhZQ= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= +cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc= +cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw= +cloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g= +cloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU= +cloud.google.com/go/videointelligence v1.7.0/go.mod h1:k8pI/1wAhjznARtVT9U1llUaFNPh7muw8QyOUpavru4= +cloud.google.com/go/vision v1.2.0/go.mod h1:SmNwgObm5DpFBme2xpyOyasvBc1aPdjvMk2bBk0tKD0= +cloud.google.com/go/vision/v2 v2.2.0/go.mod h1:uCdV4PpN1S0jyCyq8sIM42v2Y6zOLkZs+4R9LrGYwFo= +cloud.google.com/go/vision/v2 v2.3.0/go.mod h1:UO61abBx9QRMFkNBbf1D8B1LXdS2cGiiCRx0vSpZoUo= +cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xXZmFiHmGE= +cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg= +cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0= +cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/agiledragon/gomonkey v2.0.2+incompatible h1:eXKi9/piiC3cjJD1658mEE2o3NjkJ5vDLgYjCQu0Xlw= +github.com/agiledragon/gomonkey v2.0.2+incompatible/go.mod h1:2NGfXu1a80LLr2cmWXGBDaHEjb1idR6+FVlX5T3D9hw= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dlclark/regexp2 v1.7.0 h1:7lJfhqlPssTb1WQx4yvTHN0uElPEv52sbaECrAQxjAo= +github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/gonum/blas v0.0.0-20181208220705-f22b278b28ac/go.mod h1:P32wAyui1PQ58Oce/KYkOqQv8cVw1zAapXOl+dRFGbc= +github.com/gonum/floats v0.0.0-20181209220543-c233463c7e82/go.mod h1:PxC8OnwL11+aosOB5+iEPoV3picfs8tUpkVd0pDo+Kg= +github.com/gonum/integrate v0.0.0-20181209220457-a422b5c0fdf2/go.mod h1:pDgmNM6seYpwvPos3q+zxlXMsbve6mOIPucUnUOrI7Y= +github.com/gonum/internal v0.0.0-20181124074243-f884aa714029/go.mod h1:Pu4dmpkhSyOzRwuXkOgAvijx4o+4YMUJJo9OvPYMkks= +github.com/gonum/lapack v0.0.0-20181123203213-e4cdc5a0bff9/go.mod h1:XA3DeT6rxh2EAE789SSiSJNqxPaC0aE9J8NTOI0Jo/A= +github.com/gonum/matrix v0.0.0-20181209220409-c518dec07be9/go.mod h1:0EXg4mc1CNP0HCqCz+K4ts155PXIlUywf0wqN+GfPZw= +github.com/gonum/stat v0.0.0-20181125101827-41a0da705a5b/go.mod h1:Z4GIJBJO3Wa4gD4vbwQxXXZ+WHmW6E9ixmNrwvs0iZs= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= +github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= +github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= +github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= +github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= +github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= +github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo= +github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= +github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM= +github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/polarismesh/specification v1.4.1 h1:lTZqeyUhhWuKyr6NDKBwmUrNfcUDvKLxWT/uOq71T5A= +github.com/polarismesh/specification v1.4.1/go.mod h1:rDvMMtl5qebPmqiBLNa5Ps0XtwkP31ZLirbH4kXA0YU= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_golang v1.12.2 h1:51L9cDoUHVrXx4zWYlcLQIZ+d+VXHgqnYKkIuq4g/34= +github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4= +github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= +github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs= +github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= +github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg= +github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= +go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= +go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= +go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= +go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8= +go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= +golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= +golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= +google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= +google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= +google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= +google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= +google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= +google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= +google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= +google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= +google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= +google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= +google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= +google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= +google.golang.org/api v0.77.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= +google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw= +google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg= +google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o= +google.golang.org/api v0.85.0/go.mod h1:AqZf8Ep9uZ2pyTvgL+x0D3Zt0eoT9b5E8fmzfu6FO2g= +google.golang.org/api v0.90.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= +google.golang.org/api v0.93.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= +google.golang.org/api v0.95.0/go.mod h1:eADj+UBuxkh5zlrSntJghuNeg8HwQ1w5lTKkuqaETEI= +google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= +google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= +google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= +google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220722212130-b98a9ff5e252/go.mod h1:GkXuJDJ6aQ7lnJcRF+SJVgFdQhypqgl3LB1C9vabdRE= +google.golang.org/genproto v0.0.0-20220801145646-83ce21fca29f/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc= +google.golang.org/genproto v0.0.0-20220815135757-37a418bb8959/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220817144833-d7fd3f11b9b1/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220829144015-23454907ede3/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220913154956-18f8339a66a5/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220914142337-ca0e39ece12f/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220915135415-7fd63a7952de/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220916172020-2692e8806bfa/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220919141832-68c03719ef51/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006/go.mod h1:ht8XFiar2npT/g4vkk7O0WYS1sHOHbdujxbEp7CJWbw= +google.golang.org/genproto v0.0.0-20220926165614-551eb538f295/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= +google.golang.org/genproto v0.0.0-20220926220553-6981cbe3cfce/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= +google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a h1:GH6UPn3ixhWcKDhpnEC55S75cerLPdpp3hrhfKYjZgw= +google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= +google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U= +google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= +gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/examples/circuitbreaker/interface/provider/main.go b/examples/circuitbreaker/interface/provider/main.go new file mode 100644 index 00000000..5d0534a7 --- /dev/null +++ b/examples/circuitbreaker/interface/provider/main.go @@ -0,0 +1,174 @@ +/** + * 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 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 main + +import ( + "flag" + "fmt" + "log" + "net" + "net/http" + "os" + "os/signal" + "strings" + "sync/atomic" + "syscall" + + "github.com/polarismesh/polaris-go" +) + +var ( + namespace string + service string + token string + port int64 +) + +func initArgs() { + flag.StringVar(&namespace, "namespace", "default", "namespace") + flag.StringVar(&service, "service", "CircuitBreakerInterfaceServer", "service") + // 当北极星开启鉴权时,需要配置此参数完成相关的权限检查 + flag.StringVar(&token, "token", "", "token") + flag.Int64Var(&port, "port", 0, "port") +} + +// PolarisProvider is a provider for polaris +type PolarisProvider struct { + provider polaris.ProviderAPI + namespace string + service string + host string + port int + needErr int32 +} + +// Run . execute +func (svr *PolarisProvider) Run() { + tmpHost, err := getLocalHost(svr.provider.SDKContext().GetConfig().GetGlobal().GetServerConnector().GetAddresses()[0]) + if err != nil { + panic(fmt.Errorf("error occur while fetching localhost: %v", err)) + } + + svr.host = tmpHost + svr.runWebServer() + svr.registerService() +} + +func (svr *PolarisProvider) runWebServer() { + http.HandleFunc("/echo", func(rw http.ResponseWriter, r *http.Request) { + if atomic.LoadInt32(&svr.needErr) == 1 { + rw.WriteHeader(http.StatusInternalServerError) + _, _ = rw.Write([]byte(fmt.Sprintf("Fatal, My host : %s:%d", svr.host, svr.port))) + return + } + + rw.WriteHeader(http.StatusOK) + _, _ = rw.Write([]byte(fmt.Sprintf("Hello, My host : %s:%d", svr.host, svr.port))) + }) + + http.HandleFunc("/switch", func(rw http.ResponseWriter, r *http.Request) { + val := r.URL.Query().Get("openError") + if val == "true" { + atomic.StoreInt32(&svr.needErr, 1) + } else { + atomic.StoreInt32(&svr.needErr, 0) + } + rw.WriteHeader(http.StatusOK) + }) + + ln, err := net.Listen("tcp", fmt.Sprintf("0.0.0.0:%d", port)) + if err != nil { + log.Fatalf("[ERROR]fail to listen tcp, err is %v", err) + } + + svr.port = ln.Addr().(*net.TCPAddr).Port + + go func() { + log.Printf("[INFO] start http server, listen port is %v", svr.port) + if err := http.Serve(ln, nil); err != nil { + log.Fatalf("[ERROR]fail to run webServer, err is %v", err) + } + }() +} + +func (svr *PolarisProvider) registerService() { + log.Printf("start to invoke register operation") + registerRequest := &polaris.InstanceRegisterRequest{} + registerRequest.Service = service + registerRequest.Namespace = namespace + registerRequest.Host = svr.host + registerRequest.Port = svr.port + registerRequest.ServiceToken = token + resp, err := svr.provider.RegisterInstance(registerRequest) + if err != nil { + log.Fatalf("fail to register instance, err is %v", err) + } + log.Printf("register response: instanceId %s", resp.InstanceID) +} + +func runMainLoop() { + ch := make(chan os.Signal, 1) + signal.Notify(ch, []os.Signal{ + syscall.SIGINT, syscall.SIGTERM, + syscall.SIGSEGV, + }...) + + for s := range ch { + log.Printf("catch signal(%+v), stop servers", s) + return + } +} + +func main() { + initArgs() + flag.Parse() + if len(namespace) == 0 || len(service) == 0 { + log.Print("namespace and service are required") + return + } + provider, err := polaris.NewProviderAPI() + // 或者使用以下方法,则不需要创建配置文件 + // provider, err = polaris.NewProviderAPIByAddress("127.0.0.1:8091") + + if err != nil { + log.Fatalf("fail to create consumerAPI, err is %v", err) + } + defer provider.Destroy() + + svr := &PolarisProvider{ + provider: provider, + namespace: namespace, + service: service, + } + + svr.Run() + + runMainLoop() +} + +func getLocalHost(serverAddr string) (string, error) { + conn, err := net.Dial("tcp", serverAddr) + if err != nil { + return "", err + } + localAddr := conn.LocalAddr().String() + colonIdx := strings.LastIndex(localAddr, ":") + if colonIdx > 0 { + return localAddr[:colonIdx], nil + } + return localAddr, nil +} diff --git a/examples/circuitbreaker/interface/provider/polaris.yaml b/examples/circuitbreaker/interface/provider/polaris.yaml new file mode 100644 index 00000000..fe7366f5 --- /dev/null +++ b/examples/circuitbreaker/interface/provider/polaris.yaml @@ -0,0 +1,12 @@ +global: + serverConnector: + addresses: + - 127.0.0.1:8091 + statReporter: + enable: true + chain: + - prometheus + plugin: + prometheus: + type: pull + metricPort: 0 \ No newline at end of file diff --git a/examples/quickstart/consumer/polaris.yaml b/examples/quickstart/consumer/polaris.yaml index 07e08516..c6ac55cf 100644 --- a/examples/quickstart/consumer/polaris.yaml +++ b/examples/quickstart/consumer/polaris.yaml @@ -1,7 +1,7 @@ global: serverConnector: addresses: - - 127.0.0.1:8091 + - 119.91.66.223:8091 statReporter: enable: true chain: diff --git a/examples/quickstart/provider/polaris.yaml b/examples/quickstart/provider/polaris.yaml index 07e08516..c6ac55cf 100644 --- a/examples/quickstart/provider/polaris.yaml +++ b/examples/quickstart/provider/polaris.yaml @@ -1,7 +1,7 @@ global: serverConnector: addresses: - - 127.0.0.1:8091 + - 119.91.66.223:8091 statReporter: enable: true chain: diff --git a/pkg/algorithm/match/match.go b/pkg/algorithm/match/match.go new file mode 100644 index 00000000..7eb83e61 --- /dev/null +++ b/pkg/algorithm/match/match.go @@ -0,0 +1,116 @@ +/** + * 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 match + +import ( + "strconv" + "strings" + + regexp "github.com/dlclark/regexp2" + apimodel "github.com/polarismesh/specification/source/go/api/v1/model" + + "github.com/polarismesh/polaris-go/pkg/model" +) + +const ( + MatchAll = "*" +) + +func IsMatchAll(v string) bool { + return v == "" || v == MatchAll +} + +func MatchService(svcKey *model.ServiceKey, ns, svc string) bool { + inputNamespace := "" + inputService := "" + if svcKey != nil { + inputNamespace = svcKey.Namespace + inputService = svcKey.Service + } + ns = strings.TrimSpace(ns) + svc = strings.TrimSpace(svc) + if ns != "" && ns != MatchAll && ns != inputNamespace { + return false + } + if svc != "" && svc != MatchAll && svc != inputService { + return false + } + return true +} + +func MatchString(srcMetaValue string, matchValule *apimodel.MatchString, regexToPattern func(string) *regexp.Regexp) bool { + rawMetaValue := matchValule.GetValue().GetValue() + if IsMatchAll(rawMetaValue) { + return true + } + + switch matchValule.Type { + case apimodel.MatchString_REGEX: + matchExp := regexToPattern(rawMetaValue) + if matchExp == nil { + return false + } + match, err := matchExp.MatchString(srcMetaValue) + if err != nil { + return false + } + return match + case apimodel.MatchString_NOT_EQUALS: + return srcMetaValue != rawMetaValue + case apimodel.MatchString_EXACT: + return srcMetaValue == rawMetaValue + case apimodel.MatchString_IN: + find := false + tokens := strings.Split(rawMetaValue, ",") + for _, token := range tokens { + if token == srcMetaValue { + find = true + break + } + } + return find + case apimodel.MatchString_NOT_IN: + tokens := strings.Split(rawMetaValue, ",") + for _, token := range tokens { + if token == srcMetaValue { + return false + } + } + return true + case apimodel.MatchString_RANGE: + // range 模式只支持数字 + tokens := strings.Split(rawMetaValue, "~") + if len(tokens) != 2 { + return false + } + left, err := strconv.ParseInt(tokens[0], 10, 64) + if err != nil { + return false + } + right, err := strconv.ParseInt(tokens[1], 10, 64) + if err != nil { + return false + } + srcVal, err := strconv.ParseInt(srcMetaValue, 10, 64) + if err != nil { + return false + } + return srcVal >= left && srcVal <= right + } + return true +} diff --git a/pkg/algorithm/match/match_test.go b/pkg/algorithm/match/match_test.go new file mode 100644 index 00000000..8cb5d3dc --- /dev/null +++ b/pkg/algorithm/match/match_test.go @@ -0,0 +1,341 @@ +/** + * 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 match + +import ( + "testing" + + regexp "github.com/dlclark/regexp2" + apimodel "github.com/polarismesh/specification/source/go/api/v1/model" + "google.golang.org/protobuf/types/known/wrapperspb" +) + +func TestMatchString(t *testing.T) { + type args struct { + srcMetaValue string + matchValule *apimodel.MatchString + regexToPattern func(string) *regexp.Regexp + } + tests := []struct { + name string + args args + want bool + }{ + // 测试等值匹配 + { + name: "等式匹配", + args: args{ + srcMetaValue: "123", + matchValule: &apimodel.MatchString{ + Type: apimodel.MatchString_EXACT, + Value: &wrapperspb.StringValue{ + Value: "123", + }, + ValueType: apimodel.MatchString_TEXT, + }, + regexToPattern: func(s string) *regexp.Regexp { + return regexp.MustCompile(s, regexp.RE2) + }, + }, + want: true, + }, + { + name: "等式匹配", + args: args{ + srcMetaValue: "1234", + matchValule: &apimodel.MatchString{ + Type: apimodel.MatchString_EXACT, + Value: &wrapperspb.StringValue{ + Value: "123", + }, + ValueType: apimodel.MatchString_TEXT, + }, + regexToPattern: func(s string) *regexp.Regexp { + return regexp.MustCompile(s, regexp.RE2) + }, + }, + want: false, + }, + // 不等式匹配 + { + name: "不等式匹配", + args: args{ + srcMetaValue: "1234", + matchValule: &apimodel.MatchString{ + Type: apimodel.MatchString_NOT_EQUALS, + Value: &wrapperspb.StringValue{ + Value: "123", + }, + ValueType: apimodel.MatchString_TEXT, + }, + regexToPattern: func(s string) *regexp.Regexp { + return regexp.MustCompile(s, regexp.RE2) + }, + }, + want: true, + }, + { + name: "不等式匹配", + args: args{ + srcMetaValue: "123", + matchValule: &apimodel.MatchString{ + Type: apimodel.MatchString_NOT_EQUALS, + Value: &wrapperspb.StringValue{ + Value: "123", + }, + ValueType: apimodel.MatchString_TEXT, + }, + regexToPattern: func(s string) *regexp.Regexp { + return regexp.MustCompile(s, regexp.RE2) + }, + }, + want: false, + }, + // 包含匹配 + { + name: "包含匹配", + args: args{ + srcMetaValue: "123", + matchValule: &apimodel.MatchString{ + Type: apimodel.MatchString_IN, + Value: &wrapperspb.StringValue{ + Value: "123,456,123456", + }, + ValueType: apimodel.MatchString_TEXT, + }, + regexToPattern: func(s string) *regexp.Regexp { + return regexp.MustCompile(s, regexp.RE2) + }, + }, + want: true, + }, + { + name: "包含匹配", + args: args{ + srcMetaValue: "123", + matchValule: &apimodel.MatchString{ + Type: apimodel.MatchString_IN, + Value: &wrapperspb.StringValue{ + Value: "456,123456", + }, + ValueType: apimodel.MatchString_TEXT, + }, + regexToPattern: func(s string) *regexp.Regexp { + return regexp.MustCompile(s, regexp.RE2) + }, + }, + want: false, + }, + { + name: "包含匹配", + args: args{ + srcMetaValue: "123", + matchValule: &apimodel.MatchString{ + Type: apimodel.MatchString_IN, + Value: &wrapperspb.StringValue{ + Value: "456,asdad", + }, + ValueType: apimodel.MatchString_TEXT, + }, + regexToPattern: func(s string) *regexp.Regexp { + return regexp.MustCompile(s, regexp.RE2) + }, + }, + want: false, + }, + // 不包含匹配 + { + name: "不包含匹配", + args: args{ + srcMetaValue: "123", + matchValule: &apimodel.MatchString{ + Type: apimodel.MatchString_NOT_IN, + Value: &wrapperspb.StringValue{ + Value: "123,456,123456", + }, + ValueType: apimodel.MatchString_TEXT, + }, + regexToPattern: func(s string) *regexp.Regexp { + return regexp.MustCompile(s, regexp.RE2) + }, + }, + want: false, + }, + { + name: "不包含匹配", + args: args{ + srcMetaValue: "123", + matchValule: &apimodel.MatchString{ + Type: apimodel.MatchString_NOT_IN, + Value: &wrapperspb.StringValue{ + Value: "456,asdad", + }, + ValueType: apimodel.MatchString_TEXT, + }, + regexToPattern: func(s string) *regexp.Regexp { + return regexp.MustCompile(s, regexp.RE2) + }, + }, + want: true, + }, + { + name: "不包含匹配", + args: args{ + srcMetaValue: "123", + matchValule: &apimodel.MatchString{ + Type: apimodel.MatchString_NOT_IN, + Value: &wrapperspb.StringValue{ + Value: "456,123456", + }, + ValueType: apimodel.MatchString_TEXT, + }, + regexToPattern: func(s string) *regexp.Regexp { + return regexp.MustCompile(s, regexp.RE2) + }, + }, + want: true, + }, + // 范围匹配 + { + name: "范围匹配", + args: args{ + srcMetaValue: "123", + matchValule: &apimodel.MatchString{ + Type: apimodel.MatchString_RANGE, + Value: &wrapperspb.StringValue{ + Value: "123~123456", + }, + ValueType: apimodel.MatchString_TEXT, + }, + regexToPattern: func(s string) *regexp.Regexp { + return regexp.MustCompile(s, regexp.RE2) + }, + }, + want: true, + }, + { + name: "范围匹配", + args: args{ + srcMetaValue: "1234", + matchValule: &apimodel.MatchString{ + Type: apimodel.MatchString_RANGE, + Value: &wrapperspb.StringValue{ + Value: "123~123456", + }, + ValueType: apimodel.MatchString_TEXT, + }, + regexToPattern: func(s string) *regexp.Regexp { + return regexp.MustCompile(s, regexp.RE2) + }, + }, + want: true, + }, + { + name: "范围匹配", + args: args{ + srcMetaValue: "12", + matchValule: &apimodel.MatchString{ + Type: apimodel.MatchString_RANGE, + Value: &wrapperspb.StringValue{ + Value: "123~123456", + }, + ValueType: apimodel.MatchString_TEXT, + }, + regexToPattern: func(s string) *regexp.Regexp { + return regexp.MustCompile(s, regexp.RE2) + }, + }, + want: false, + }, + { + name: "范围匹配", + args: args{ + srcMetaValue: "12345643", + matchValule: &apimodel.MatchString{ + Type: apimodel.MatchString_RANGE, + Value: &wrapperspb.StringValue{ + Value: "123~123456", + }, + ValueType: apimodel.MatchString_TEXT, + }, + regexToPattern: func(s string) *regexp.Regexp { + return regexp.MustCompile(s, regexp.RE2) + }, + }, + want: false, + }, + { + name: "范围匹配", + args: args{ + srcMetaValue: "12345643", + matchValule: &apimodel.MatchString{ + Type: apimodel.MatchString_RANGE, + Value: &wrapperspb.StringValue{ + Value: "a~123456", + }, + ValueType: apimodel.MatchString_TEXT, + }, + regexToPattern: func(s string) *regexp.Regexp { + return regexp.MustCompile(s, regexp.RE2) + }, + }, + want: false, + }, + { + name: "范围匹配", + args: args{ + srcMetaValue: "12345643", + matchValule: &apimodel.MatchString{ + Type: apimodel.MatchString_RANGE, + Value: &wrapperspb.StringValue{ + Value: "1231~3a", + }, + ValueType: apimodel.MatchString_TEXT, + }, + regexToPattern: func(s string) *regexp.Regexp { + return regexp.MustCompile(s, regexp.RE2) + }, + }, + want: false, + }, + { + name: "范围匹配", + args: args{ + srcMetaValue: "wx33", + matchValule: &apimodel.MatchString{ + Type: apimodel.MatchString_RANGE, + Value: &wrapperspb.StringValue{ + Value: "1231~123123", + }, + ValueType: apimodel.MatchString_TEXT, + }, + regexToPattern: func(s string) *regexp.Regexp { + return regexp.MustCompile(s, regexp.RE2) + }, + }, + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := MatchString(tt.args.srcMetaValue, tt.args.matchValule, tt.args.regexToPattern); got != tt.want { + t.Errorf("MatchString() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/pkg/clock/clock.go b/pkg/clock/clock.go index 9f5ff0bc..ca1a4dec 100644 --- a/pkg/clock/clock.go +++ b/pkg/clock/clock.go @@ -73,3 +73,9 @@ func init() { globalClock.currentTime.Store(&now) go globalClock.updateTime() } + +func CurrentMillis() int64 { + tn := time.Now() + curTimeMill := tn.Unix()*1e3 + int64(tn.Nanosecond())/1e6 + return curTimeMill +} diff --git a/pkg/config/api.go b/pkg/config/api.go index 983a48c9..8ad0cf53 100644 --- a/pkg/config/api.go +++ b/pkg/config/api.go @@ -401,32 +401,46 @@ 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: 不在使用 GetRequestCountAfterHalfOpen() int // SetRequestCountAfterHalfOpen 设置半开状态后最多分配多少个探测请求 + // Deprecated: 不在使用 SetRequestCountAfterHalfOpen(count int) // GetSuccessCountAfterHalfOpen 获取半开状态后多少个成功请求则恢复 + // Deprecated: 不在使用 GetSuccessCountAfterHalfOpen() int // SetSuccessCountAfterHalfOpen 设置半开状态后多少个成功请求则恢复 + // Deprecated: 不在使用 SetSuccessCountAfterHalfOpen(count int) // GetRecoverWindow 获取半开后的恢复周期,按周期来进行半开放量的统计 + // Deprecated: 不在使用 GetRecoverWindow() time.Duration // SetRecoverWindow 设置半开后的恢复周期,按周期来进行半开放量的统计 + // Deprecated: 不在使用 SetRecoverWindow(value time.Duration) // GetRecoverNumBuckets 半开后请求数统计滑桶数量 + // Deprecated: 不在使用 GetRecoverNumBuckets() int // SetRecoverNumBuckets 设置半开后请求数统计滑桶数量 + // Deprecated: 不在使用 SetRecoverNumBuckets(value int) // GetErrorCountConfig 连续错误数熔断配置 + // Deprecated: 不在使用 GetErrorCountConfig() ErrorCountConfig // GetErrorRateConfig 错误率熔断配置 + // Deprecated: 不在使用 GetErrorRateConfig() ErrorRateConfig } diff --git a/pkg/config/circuitbreaker.go b/pkg/config/circuitbreaker.go index fa496545..f82585ab 100644 --- a/pkg/config/circuitbreaker.go +++ b/pkg/config/circuitbreaker.go @@ -185,7 +185,7 @@ func (c *CircuitBreakerConfigImpl) SetDefault() { c.CheckPeriod = model.ToDurationPtr(DefaultCircuitBreakerCheckPeriod) } if len(c.Chain) == 0 { - c.Chain = []string{DefaultCircuitBreakerErrCount, DefaultCircuitBreakerErrRate} + c.Chain = []string{DefaultCircuitBreaker} } if nil == c.Enable { enable := DefaultCircuitBreakerEnabled diff --git a/pkg/config/default.go b/pkg/config/default.go index d3a96eff..aa4cb24a 100644 --- a/pkg/config/default.go +++ b/pkg/config/default.go @@ -55,6 +55,8 @@ const ( DefaultRequestQueueSize int = 1000 // DefaultServerSwitchInterval 默认server的切换时间时间. DefaultServerSwitchInterval = 10 * time.Minute + // DefaultCachePersistEnable 默认缓存持久化存储开启. + DefaultCachePersistEnable bool = true // DefaultCachePersistDir 默认缓存持久化存储目录. DefaultCachePersistDir string = "./polaris/backup" // DefaultPersistMaxWriteRetry 持久化缓存写文件的默认重试次数. @@ -188,6 +190,8 @@ const ( DefaultLoadBalancerL5CST string = "l5cst" // DefaultLoadBalancerHash 负载均衡器,普通hash. DefaultLoadBalancerHash string = "hash" + // DefaultCircuitBreaker 默认错误率熔断器. + DefaultCircuitBreaker string = "composite" // DefaultCircuitBreakerErrRate 默认错误率熔断器. DefaultCircuitBreakerErrRate string = "errorRate" // DefaultCircuitBreakerErrCount 默认持续错误熔断器. diff --git a/pkg/config/localcache.go b/pkg/config/localcache.go index 00a62b25..2c434402 100644 --- a/pkg/config/localcache.go +++ b/pkg/config/localcache.go @@ -43,7 +43,7 @@ type LocalCacheConfigImpl struct { // 本地缓存类型,默认inmemory,可修改成具体的缓存插件名 Type string `yaml:"type" json:"type"` // 是否启用本地缓存 - PersistEnable bool `yaml:"persistEnable" json:"persistEnable"` + PersistEnable *bool `yaml:"persistEnable" json:"persistEnable"` // consumer.localCache.persistMaxWriteRetry PersistMaxWriteRetry int `yaml:"persistMaxWriteRetry" json:"persistMaxWriteRetry"` // consumer.localCache.persistReadRetry @@ -92,12 +92,12 @@ func (l *LocalCacheConfigImpl) SetServiceRefreshInterval(interval time.Duration) // IsPersistEnable consumer.localCache.persistEnable // 是否启用本地缓存 func (l *LocalCacheConfigImpl) IsPersistEnable() bool { - return l.PersistEnable + return *l.PersistEnable } // SetPersistEnable 设置是否启用本地缓存 func (l *LocalCacheConfigImpl) SetPersistEnable(enable bool) { - l.PersistEnable = enable + l.PersistEnable = &enable } // GetPersistDir consumer.localCache.persist.path @@ -221,6 +221,9 @@ func (l *LocalCacheConfigImpl) SetDefault() { if nil == l.ServiceRefreshInterval { l.ServiceRefreshInterval = model.ToDurationPtr(DefaultServiceRefreshIntervalDuration) } + if nil == l.PersistEnable { + l.PersistEnable = model.ToBoolPtr(DefaultCachePersistEnable) + } if len(l.PersistDir) == 0 { l.PersistDir = DefaultCachePersistDir } @@ -240,7 +243,7 @@ func (l *LocalCacheConfigImpl) SetDefault() { l.PersistAvailableInterval = model.ToDurationPtr(DefaultPersistAvailableInterval) } if nil == l.StartUseFileCache { - l.StartUseFileCache = &DefaultUseFileCacheFlag + l.StartUseFileCache = model.ToBoolPtr(DefaultUseFileCacheFlag) } if nil == l.PushEmptyProtection { l.PushEmptyProtection = &DefaultPushEmptyProtection diff --git a/pkg/flow/cbcheck/object.go b/pkg/flow/cbcheck/object.go deleted file mode 100644 index 871ac21e..00000000 --- a/pkg/flow/cbcheck/object.go +++ /dev/null @@ -1,136 +0,0 @@ -/** - * 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 cbcheck - -import ( - "fmt" - "sync/atomic" - "time" - - "github.com/polarismesh/polaris-go/pkg/clock" - "github.com/polarismesh/polaris-go/pkg/model" -) - -// circuitBreakerStatus 熔断状态模型,实现了CircuitBreakerStatus接口 -type circuitBreakerStatus struct { - circuitBreaker string - status model.Status - startTime time.Time - finalAllocTime int64 - - // 半开后最多请求此数 - maxHalfOpenAllowReqTimes int - // 半开后配个分配 初始化为maxHalfOpenAllowReqTimes - halfOpenQuota int32 - // 半开后,分配的个数 - allocatedRequestsAfterHalfOpen int32 - - // 半开后发生请求的次数 - halfOpenRequests int32 - // 半开后发生的失败请求数 - halfOpenFailRequests int32 - - // 状态发生转变的锁 - statusLock int32 -} - -// AllocatedRequestsAfterHalfOpen 获取在半开之后,分配出去的请求数,即getOneInstance接口返回这个实例的次数 -func (c *circuitBreakerStatus) AllocatedRequestsAfterHalfOpen() int32 { - return atomic.LoadInt32(&c.allocatedRequestsAfterHalfOpen) -} - -// AcquireStatusLock 获取状态转换锁 -func (c *circuitBreakerStatus) AcquireStatusLock() bool { - return atomic.CompareAndSwapInt32(&c.statusLock, 0, 1) -} - -// GetRequestsAfterHalfOpen 获取进入半开状态之后分配的请求数 -func (c *circuitBreakerStatus) GetRequestsAfterHalfOpen() int32 { - return atomic.LoadInt32(&c.halfOpenRequests) -} - -// GetFailRequestsAfterHalfOpen 获取进入半开状态之后的成功请求数 -func (c *circuitBreakerStatus) GetFailRequestsAfterHalfOpen() int32 { - return atomic.LoadInt32(&c.halfOpenFailRequests) -} - -// AddRequestCountAfterHalfOpen 添加半开状态下面的请求数 -func (c *circuitBreakerStatus) AddRequestCountAfterHalfOpen(n int32, success bool) int32 { - if !success { - atomic.AddInt32(&c.halfOpenFailRequests, n) - } - return atomic.AddInt32(&c.halfOpenRequests, n) -} - -// GetCircuitBreaker 标识被哪个熔断器熔断 -func (c *circuitBreakerStatus) GetCircuitBreaker() string { - return c.circuitBreaker -} - -// GetStatus 熔断状态 -func (c *circuitBreakerStatus) GetStatus() model.Status { - return c.status -} - -// GetStartTime 状态转换的时间 -func (c *circuitBreakerStatus) GetStartTime() time.Time { - return c.startTime -} - -// String 打印熔断状态数据 -func (c circuitBreakerStatus) String() string { - return fmt.Sprintf("{circuitBreaker:%s, status:%v, startTime:%v}", - c.circuitBreaker, c.status, c.startTime) -} - -// IsAvailable 是否可以分配请求 -func (c *circuitBreakerStatus) IsAvailable() bool { - if nil == c || model.Close == c.status { - return true - } - if model.Open == c.status { - return false - } - return atomic.LoadInt32(&c.halfOpenQuota) > 0 -} - -// GetFinalAllocateTimeInt64 获取分配了最后配额的时间 -func (c *circuitBreakerStatus) GetFinalAllocateTimeInt64() int64 { - return atomic.LoadInt64(&c.finalAllocTime) -} - -// Allocate 执行实例到请求的分配 -func (c *circuitBreakerStatus) Allocate() bool { - if nil == c || model.Close == c.status { - return true - } - if model.Open == c.status { - return false - } - left := atomic.AddInt32(&c.halfOpenQuota, -1) - // 如果刚好是0,即分配了最后一个半开配额,记录时间 - if left == 0 { - atomic.StoreInt64(&c.finalAllocTime, clock.GetClock().Now().UnixNano()) - } - // 如果进行了分配的话,那么只要left大于等于0即可 - allocated := left >= 0 - if allocated { - atomic.AddInt32(&c.allocatedRequestsAfterHalfOpen, 1) - } - return allocated -} diff --git a/pkg/flow/cbcheck/realtime.go b/pkg/flow/cbcheck/realtime.go deleted file mode 100644 index 138f3320..00000000 --- a/pkg/flow/cbcheck/realtime.go +++ /dev/null @@ -1,80 +0,0 @@ -/** - * 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 cbcheck - -import ( - "github.com/modern-go/reflect2" - - "github.com/polarismesh/polaris-go/pkg/log" - "github.com/polarismesh/polaris-go/pkg/model" - "github.com/polarismesh/polaris-go/pkg/model/pb" -) - -// RealTimeLimitTask 实时熔断任务 -type RealTimeLimitTask struct { - // 服务信息 - SvcKey model.ServiceKey - // 实例ID - InstID string - // 机器IP - Host string - // 端口号 - Port int - // 熔断器名字 - CbName string -} - -// NewCircuitBreakRealTimeCallBack 创建实时熔断任务 -func NewCircuitBreakRealTimeCallBack( - callBack *CircuitBreakCallBack, task *RealTimeLimitTask) *CircuitBreakRealTimeCallBack { - return &CircuitBreakRealTimeCallBack{ - commonCallBack: callBack, - task: task, - } -} - -// CircuitBreakRealTimeCallBack 实时熔断任务回调 -type CircuitBreakRealTimeCallBack struct { - commonCallBack *CircuitBreakCallBack - task *RealTimeLimitTask -} - -// Process 处理实时任务 -func (c *CircuitBreakRealTimeCallBack) Process() { - svcInstances := c.commonCallBack.registry.GetInstances(&c.task.SvcKey, false, true) - if !svcInstances.IsInitialized() { - return - } - svcInstanceInProto := svcInstances.(*pb.ServiceInstancesInProto) - instance := svcInstanceInProto.GetInstance(c.task.InstID) - if reflect2.IsNil(instance) { - return - } - request, err := c.commonCallBack.doCircuitBreakForService(c.task.SvcKey, nil, instance, c.task.CbName) - var resultStr = "nil" - if nil != request { - resultStr = request.String() - } - if err != nil { - log.GetDetectLogger().Errorf("fail to do realtime circuitBreak check for %s, result is %s, error: %v", - c.task.SvcKey, resultStr, err) - return - } - log.GetDetectLogger().Infof( - "success to realtime circuitBreak check for %s, result is %s", c.task.SvcKey, resultStr) -} diff --git a/pkg/flow/cbcheck/ticker.go b/pkg/flow/cbcheck/ticker.go deleted file mode 100644 index c6416ba2..00000000 --- a/pkg/flow/cbcheck/ticker.go +++ /dev/null @@ -1,185 +0,0 @@ -/** - * 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 cbcheck - -import ( - "time" - - "github.com/modern-go/reflect2" - - "github.com/polarismesh/polaris-go/pkg/config" - "github.com/polarismesh/polaris-go/pkg/flow/data" - "github.com/polarismesh/polaris-go/pkg/log" - "github.com/polarismesh/polaris-go/pkg/model" - "github.com/polarismesh/polaris-go/pkg/plugin" - "github.com/polarismesh/polaris-go/pkg/plugin/circuitbreaker" - "github.com/polarismesh/polaris-go/pkg/plugin/localregistry" -) - -// NewCircuitBreakCallBack 创建定时熔断任务回调 -func NewCircuitBreakCallBack(cfg config.Configuration, supplier plugin.Supplier) (*CircuitBreakCallBack, error) { - var err error - callBack := &CircuitBreakCallBack{} - if callBack.registry, err = data.GetRegistry(cfg, supplier); err != nil { - return nil, err - } - if callBack.circuitBreakerChain, err = data.GetCircuitBreakers(cfg, supplier); err != nil { - return nil, err - } - callBack.interval = cfg.GetConsumer().GetCircuitBreaker().GetCheckPeriod() - return callBack, nil -} - -// CircuitBreakCallBack 定时熔断任务回调 -type CircuitBreakCallBack struct { - // 熔断器 - circuitBreakerChain []circuitbreaker.InstanceCircuitBreaker - // 本地缓存 - registry localregistry.LocalRegistry - // 轮询间隔 - interval time.Duration -} - -// Process 执行任务 -func (c *CircuitBreakCallBack) Process( - taskKey interface{}, taskValue interface{}, lastProcessTime time.Time) model.TaskResult { - if !lastProcessTime.IsZero() && time.Since(lastProcessTime) < c.interval { - return model.SKIP - } - svc := taskKey.(model.ServiceKey) - log.GetDetectLogger().Debugf("start to do timing circuitBreak check for %s, "+ - "checkPeriod %v, now is %v", svc, c.interval, time.Now()) - svcInstances := c.registry.GetInstances(&svc, false, true) - if !svcInstances.IsInitialized() || len(svcInstances.GetInstances()) == 0 { - log.GetDetectLogger().Infof("instances not initialized for %s", svc) - return model.CONTINUE - } - request, err := c. - doCircuitBreakForService(svc, svcInstances, nil, "") - var resultStr = "nil" - if nil != request { - resultStr = request.String() - } - if err != nil { - log.GetDetectLogger().Errorf( - "fail to do timing circuitBreak check for %s, result is %s, error: %v", svc, resultStr, err) - return model.CONTINUE - } - log.GetDetectLogger().Debugf("success to timing circuitBreak check for %s, result is %s", svc, resultStr) - return model.CONTINUE -} - -// OnTaskEvent 任务事件回调 -func (c *CircuitBreakCallBack) OnTaskEvent(event model.TaskEvent) { - -} - -// doCircuitBreakForService 对服务进行熔断判断操作 -func (c *CircuitBreakCallBack) doCircuitBreakForService(svc model.ServiceKey, svcInstances model.ServiceInstances, - instance model.Instance, cbName string) (*localregistry.ServiceUpdateRequest, error) { - allResults := make(map[string]*circuitbreaker.Result, 0) - var instances []model.Instance - if reflect2.IsNil(instance) { - instances = svcInstances.GetInstances() - } else { - instances = []model.Instance{instance} - } - if len(instances) == 0 { - return nil, nil - } - for _, instance := range instances { - if len(c.circuitBreakerChain) == 0 { - continue - } - for _, circuitBreaker := range c.circuitBreakerChain { - if len(cbName) > 0 && circuitBreaker.Name() != cbName { - continue - } - result, err := circuitBreaker.CircuitBreak([]model.Instance{instance}) - if err != nil { - log.GetBaseLogger().Errorf("fail to do timingCircuitBreak %s for %v, instance %s:%d, error: %v", - circuitBreaker.Name(), svc, instance.GetHost(), instance.GetPort(), err) - continue - } - if nil == result { - continue - } - if lastResult, ok := allResults[circuitBreaker.Name()]; ok { - lastResult.Merge(result) - } else { - allResults[circuitBreaker.Name()] = result - } - break - } - } - // 批量更新状态 - updateRequest := buildServiceUpdateRequest(svc, allResults) - if len(updateRequest.Properties) == 0 { - return nil, nil - } - return updateRequest, c.registry.UpdateInstances(updateRequest) -} - -// cleanInstanceSet 清理实例集合,剔除重复数 -func cleanInstanceSet(instanceSet model.HashSet, allInstances model.HashSet) { - for instID := range instanceSet { - if allInstances.Contains(instID) { - instanceSet.Delete(instID) - } else { - allInstances.Add(instID) - } - } -} - -// buildInstanceProperty 构建实例更新数据 -func buildInstanceProperty(now time.Time, allowedRequests int, instances model.HashSet, - request *localregistry.ServiceUpdateRequest, cbName string, status model.Status) { - if len(instances) == 0 { - return - } - for instID := range instances { - request.Properties = append(request.Properties, localregistry.InstanceProperties{ - ID: instID.(string), - Service: &request.ServiceKey, - Properties: map[string]interface{}{localregistry.PropertyCircuitBreakerStatus: &circuitBreakerStatus{ - circuitBreaker: cbName, - status: status, - startTime: now, - maxHalfOpenAllowReqTimes: allowedRequests, - halfOpenQuota: int32(allowedRequests), - }}, - }) - } -} - -// buildServiceUpdateRequest 构造服务更新数据 -func buildServiceUpdateRequest( - svc model.ServiceKey, results map[string]*circuitbreaker.Result) *localregistry.ServiceUpdateRequest { - request := &localregistry.ServiceUpdateRequest{ - ServiceKey: svc, - } - for cbName, result := range results { - buildInstanceProperty(result.Now, result.RequestCountAfterHalfOpen, result.InstancesToHalfOpen, - request, cbName, model.HalfOpen) - buildInstanceProperty(result.Now, result.RequestCountAfterHalfOpen, result.InstancesToOpen, - request, cbName, model.Open) - buildInstanceProperty(result.Now, result.RequestCountAfterHalfOpen, result.InstancesToClose, - request, cbName, model.Close) - } - return request -} diff --git a/pkg/flow/circuitbreaker_flow.go b/pkg/flow/circuitbreaker_flow.go new file mode 100644 index 00000000..1c20a59b --- /dev/null +++ b/pkg/flow/circuitbreaker_flow.go @@ -0,0 +1,267 @@ +/** + * 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 flow + +import ( + "context" + "errors" + "time" + + "github.com/polarismesh/polaris-go/pkg/log" + "github.com/polarismesh/polaris-go/pkg/model" + "github.com/polarismesh/polaris-go/pkg/plugin/circuitbreaker" +) + +// Check +func (e *Engine) Check(resource model.Resource) (*model.CheckResult, error) { + return e.circuitBreakerFlow.Check(resource) +} + +// Report +func (e *Engine) Report(reportStat *model.ResourceStat) error { + return e.circuitBreakerFlow.Report(reportStat) +} + +// MakeFunctionDecorator +func (e *Engine) MakeFunctionDecorator(f model.CustomerFunction, reqCtx *model.RequestContext) model.DecoratorFunction { + return e.circuitBreakerFlow.MakeFunctionDecorator(f, reqCtx) +} + +// MakeInvokeHandler +func (e *Engine) MakeInvokeHandler(reqCtx *model.RequestContext) model.InvokeHandler { + return e.circuitBreakerFlow.MakeInvokeHandler(reqCtx) +} + +type CircuitBreakerFlow struct { + engine *Engine + resourceBreaker circuitbreaker.CircuitBreaker +} + +func newCircuitBreakerFlow(e *Engine, breaker circuitbreaker.CircuitBreaker) *CircuitBreakerFlow { + return &CircuitBreakerFlow{ + engine: e, + resourceBreaker: breaker, + } +} + +func (e *CircuitBreakerFlow) Check(resource model.Resource) (*model.CheckResult, error) { + if e.resourceBreaker == nil { + return nil, model.NewSDKError(model.ErrCodeInternalError, nil, "circuitbreaker not found") + } + + status := e.resourceBreaker.CheckResource(resource) + if status != nil { + return circuitBreakerStatusToResult(status), nil + } + + return &model.CheckResult{ + Pass: true, + RuleName: "", + FallbackInfo: nil, + }, nil +} + +func circuitBreakerStatusToResult(breakerStatus model.CircuitBreakerStatus) *model.CheckResult { + status := breakerStatus.GetStatus() + if status == model.Open { + return &model.CheckResult{ + Pass: false, + RuleName: breakerStatus.GetCircuitBreaker(), + FallbackInfo: breakerStatus.GetFallbackInfo(), + } + } + return &model.CheckResult{ + Pass: true, + RuleName: breakerStatus.GetCircuitBreaker(), + FallbackInfo: breakerStatus.GetFallbackInfo(), + } +} + +func (e *CircuitBreakerFlow) Report(reportStat *model.ResourceStat) error { + if e.resourceBreaker == nil { + return model.NewSDKError(model.ErrCodeInternalError, nil, "circuitbreaker not found") + } + return e.resourceBreaker.Report(reportStat) +} + +func (e *CircuitBreakerFlow) MakeFunctionDecorator(f model.CustomerFunction, reqCtx *model.RequestContext) model.DecoratorFunction { + decorator := &DefaultFunctionalDecorator{ + invoke: &DefaultInvokeHandler{ + flow: e, + reqCtx: reqCtx, + }, + customerFunc: f, + } + return decorator.Decorator +} + +func (e *CircuitBreakerFlow) MakeInvokeHandler(reqCtx *model.RequestContext) model.InvokeHandler { + return &DefaultInvokeHandler{ + flow: e, + reqCtx: reqCtx, + } +} + +type DefaultFunctionalDecorator struct { + invoke model.InvokeHandler + customerFunc model.CustomerFunction +} + +func (df *DefaultFunctionalDecorator) Decorator(ctx context.Context, args interface{}) (interface{}, *model.CallAborted, error) { + invoke := df.invoke + pass, aborted, err := invoke.AcquirePermission() + if err != nil { + return nil, nil, err + } + var ( + ret interface{} + start = time.Now() + cerr error + ) + if !pass { + cerr = aborted.GetError() + return nil, aborted, cerr + } + + defer func() { + delay := time.Since(start) + if cerr != nil { + rspCtx := &model.ResponseContext{ + Duration: delay, + Err: cerr, + } + invoke.OnError(rspCtx) + } else { + rspCtx := &model.ResponseContext{ + Duration: delay, + Result: ret, + } + invoke.OnError(rspCtx) + } + }() + + ret, cerr = df.customerFunc(ctx, args) + if cerr != nil { + return nil, nil, cerr + } + return ret, nil, nil +} + +type DefaultInvokeHandler struct { + flow *CircuitBreakerFlow + reqCtx *model.RequestContext +} + +func (h *DefaultInvokeHandler) AcquirePermission() (bool, *model.CallAborted, error) { + check, err := h.commonCheck(h.reqCtx) + if err != nil { + return true, nil, err + } + if check != nil { + return false, model.NewCallAborted(model.ErrorCallAborted, check.RuleName, check.FallbackInfo), nil + } + return true, nil, nil +} + +func (h *DefaultInvokeHandler) OnSuccess(respCtx *model.ResponseContext) { + delay := respCtx.Duration + code := "0" + retStatus := model.RetUnknown + if h.reqCtx.CodeConvert != nil { + code = h.reqCtx.CodeConvert.OnSuccess(respCtx.Result) + } + if err := h.commonReport(h.reqCtx, delay, code, retStatus); err != nil { + log.GetBaseLogger().Errorf("DefaultInvokeHandler.commonReport in OnSuccess: %v", err) + } +} + +func (h *DefaultInvokeHandler) OnError(respCtx *model.ResponseContext) { + delay := respCtx.Duration + code := "-1" + retStatus := model.RetUnknown + if h.reqCtx.CodeConvert != nil { + code = h.reqCtx.CodeConvert.OnError(respCtx.Err) + } + if errors.Is(respCtx.Err, model.ErrorCallAborted) { + retStatus = model.RetReject + } + if err := h.commonReport(h.reqCtx, delay, code, retStatus); err != nil { + log.GetBaseLogger().Errorf("DefaultInvokeHandler.commonReport in OnError: %v", err) + } +} + +func (h *DefaultInvokeHandler) commonCheck(reqCtx *model.RequestContext) (*model.CheckResult, error) { + svcRes, err := model.NewServiceResource(reqCtx.Callee, reqCtx.Caller) + if err != nil { + return nil, err + } + result, err := h.flow.Check(svcRes) + if err != nil { + return nil, err + } + if !result.Pass { + return result, nil + } + if reqCtx.Method != "" { + methodSvc, err := model.NewMethodResource(reqCtx.Callee, reqCtx.Caller, reqCtx.Method) + if err != nil { + return nil, err + } + result, err := h.flow.Check(methodSvc) + if err != nil { + return nil, err + } + if !result.Pass { + return result, nil + } + } + return nil, nil +} + +func (h *DefaultInvokeHandler) commonReport(reqCtx *model.RequestContext, delay time.Duration, code string, + retStatus model.RetStatus) error { + svcRes, err := model.NewServiceResource(reqCtx.Callee, reqCtx.Caller) + if err != nil { + return err + } + resourceStat := &model.ResourceStat{ + Resource: svcRes, + RetCode: code, + Delay: delay, + RetStatus: retStatus, + } + if err := h.flow.Report(resourceStat); err != nil { + return err + } + if reqCtx.Method != "" { + methodSvc, err := model.NewMethodResource(reqCtx.Callee, reqCtx.Caller, reqCtx.Method) + if err != nil { + return err + } + resourceStat = &model.ResourceStat{ + Resource: methodSvc, + RetCode: code, + Delay: delay, + RetStatus: retStatus, + } + if err := h.flow.Report(resourceStat); err != nil { + return err + } + } + return nil +} diff --git a/pkg/flow/configuration/flow.go b/pkg/flow/configuration/flow.go index 249216a4..21421347 100644 --- a/pkg/flow/configuration/flow.go +++ b/pkg/flow/configuration/flow.go @@ -31,7 +31,6 @@ type ConfigFlow struct { // NewConfigFlow 创建配置中心服务 func NewConfigFlow(connector configconnector.ConfigConnector, chain configfilter.Chain, configuration config.Configuration) (*ConfigFlow, error) { - fileFlow, err := NewConfigFileFlow(connector, chain, configuration) if err != nil { return nil, err diff --git a/pkg/flow/configuration/group_flow.go b/pkg/flow/configuration/group_flow.go index 2af748a3..5915dfa1 100644 --- a/pkg/flow/configuration/group_flow.go +++ b/pkg/flow/configuration/group_flow.go @@ -39,9 +39,7 @@ type ConfigGroupFlow struct { configuration config.Configuration } -func newConfigGroupFlow(connector configconnector.ConfigConnector, - configuration config.Configuration) (*ConfigGroupFlow, error) { - +func newConfigGroupFlow(connector configconnector.ConfigConnector, configuration config.Configuration) (*ConfigGroupFlow, error) { ctx, cancel := context.WithCancel(context.Background()) groupFlow := &ConfigGroupFlow{ diff --git a/pkg/flow/configuration/group_repo.go b/pkg/flow/configuration/group_repo.go index 53978edd..b0af29dd 100644 --- a/pkg/flow/configuration/group_repo.go +++ b/pkg/flow/configuration/group_repo.go @@ -23,11 +23,12 @@ import ( "sync/atomic" "time" + apimodel "github.com/polarismesh/specification/source/go/api/v1/model" + "go.uber.org/zap" + "github.com/polarismesh/polaris-go/pkg/config" "github.com/polarismesh/polaris-go/pkg/log" "github.com/polarismesh/polaris-go/pkg/plugin/configconnector" - apimodel "github.com/polarismesh/specification/source/go/api/v1/model" - "go.uber.org/zap" ) // ConfigGroupRepo 服务端配置文件代理类,从服务端拉取配置并同步数据 @@ -45,10 +46,8 @@ type ConfigGroupRepo struct { listeners []func(*configconnector.ConfigGroupResponse) } -func newConfigGroupRepo(namespace, group string, - connector configconnector.ConfigConnector, +func newConfigGroupRepo(namespace, group string, connector configconnector.ConfigConnector, configuration config.Configuration) (*ConfigGroupRepo, error) { - repo := &ConfigGroupRepo{ namespace: namespace, groupName: group, diff --git a/pkg/flow/data/object.go b/pkg/flow/data/object.go index c71d27a8..281ef713 100644 --- a/pkg/flow/data/object.go +++ b/pkg/flow/data/object.go @@ -215,6 +215,7 @@ func (c *CommonInstancesRequest) InitByGetOneRequest(request *model.GetOneInstan c.DstService.Service = request.Service c.DstService.Namespace = request.Namespace c.RouteInfo.DestService = request + c.RouteInfo.IncludeCircuitBreakInstances = request.IncludeCircuitBreakInstances c.RouteInfo.EnableFailOverDefaultMeta = request.EnableFailOverDefaultMeta c.RouteInfo.FailOverDefaultMeta = request.FailOverDefaultMeta c.RouteInfo.Canary = request.Canary diff --git a/pkg/flow/data/util.go b/pkg/flow/data/util.go index 25172d9d..193f3ae7 100644 --- a/pkg/flow/data/util.go +++ b/pkg/flow/data/util.go @@ -88,29 +88,17 @@ func GetRegistry(cfg config.Configuration, supplier plugin.Supplier) (localregis // GetCircuitBreakers 获取熔断插件链 func GetCircuitBreakers( - cfg config.Configuration, supplier plugin.Supplier) ([]circuitbreaker.InstanceCircuitBreaker, error) { + cfg config.Configuration, supplier plugin.Supplier) ([]circuitbreaker.CircuitBreaker, error) { cbChain := cfg.GetConsumer().GetCircuitBreaker().GetChain() - when := cfg.GetConsumer().GetHealthCheck().GetWhen() - var hasHealthCheckBreaker bool - cbreakers := make([]circuitbreaker.InstanceCircuitBreaker, 0, len(cbChain)) + cbreakers := make([]circuitbreaker.CircuitBreaker, 0, len(cbChain)) if len(cbChain) > 0 { for _, cbName := range cbChain { - if cbName == config.DefaultCircuitBreakerErrCheck { - hasHealthCheckBreaker = true - } targetPlugin, err := supplier.GetPlugin(common.TypeCircuitBreaker, cbName) if err != nil { return nil, err } - cbreakers = append(cbreakers, targetPlugin.(circuitbreaker.InstanceCircuitBreaker)) - } - } - if when == config.HealthCheckAlways && !hasHealthCheckBreaker { - targetPlugin, err := supplier.GetPlugin(common.TypeCircuitBreaker, config.DefaultCircuitBreakerErrCheck) - if err != nil { - return nil, err + cbreakers = append(cbreakers, targetPlugin.(circuitbreaker.CircuitBreaker)) } - cbreakers = append(cbreakers, targetPlugin.(circuitbreaker.InstanceCircuitBreaker)) } return cbreakers, nil } diff --git a/pkg/flow/detect/ticker.go b/pkg/flow/detect/ticker.go deleted file mode 100644 index 526e54fc..00000000 --- a/pkg/flow/detect/ticker.go +++ /dev/null @@ -1,225 +0,0 @@ -/** - * 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 detect - -import ( - "context" - "sync" - "sync/atomic" - "time" - - "github.com/polarismesh/polaris-go/pkg/config" - "github.com/polarismesh/polaris-go/pkg/flow/data" - "github.com/polarismesh/polaris-go/pkg/log" - "github.com/polarismesh/polaris-go/pkg/model" - "github.com/polarismesh/polaris-go/pkg/model/local" - "github.com/polarismesh/polaris-go/pkg/plugin" - "github.com/polarismesh/polaris-go/pkg/plugin/healthcheck" - "github.com/polarismesh/polaris-go/pkg/plugin/localregistry" -) - -// NewHealthCheckCallBack 创建健康检查的回调 -func NewHealthCheckCallBack(cfg config.Configuration, supplier plugin.Supplier) (*HealthCheckCallBack, error) { - var err error - callback := &HealthCheckCallBack{ - mutex: &sync.Mutex{}, - } - if callback.healthCheckers, err = data.GetHealthCheckers(cfg, supplier); err != nil { - return nil, err - } - if callback.registry, err = data.GetRegistry(cfg, supplier); err != nil { - return nil, err - } - callback.healthCheckConfig = cfg.GetConsumer().GetHealthCheck() - return callback, nil -} - -// HealthCheckCallBack 健康探测回调任务 -type HealthCheckCallBack struct { - // 探活器 - healthCheckers []healthcheck.HealthChecker - // 本地缓存 - registry localregistry.LocalRegistry - // 健康检查配置 - healthCheckConfig config.HealthCheckConfig - // 修改pendingInstances的锁 - mutex *sync.Mutex - // 正在执行健康检查的实例 - pendingInstances model.HashSet - // 任务取消函数 - taskWorkerCancel context.CancelFunc - // 任务队列 - taskChannels []chan model.Instance - // 任务下标 - taskIndex int64 -} - -const channelBuffer = 100 - -// Process 执行任务 -func (c *HealthCheckCallBack) Process( - taskKey interface{}, taskValue interface{}, lastProcessTime time.Time) model.TaskResult { - svc := taskKey.(model.ServiceKey) - svcInstances := c.registry.GetInstances(&svc, false, true) - if !svcInstances.IsInitialized() || len(svcInstances.GetInstances()) == 0 { - return model.CONTINUE - } - err := c.doHealthCheckService(svcInstances) - if err != nil { - log.GetDetectLogger().Errorf("fail to update instances for %v, error: %v", svc, err) - return model.CONTINUE - } - return model.CONTINUE -} - -// OnTaskEvent 任务事件回调 -func (c *HealthCheckCallBack) OnTaskEvent(event model.TaskEvent) { - switch event { - case model.EventStart: - c.mutex.Lock() - c.pendingInstances = model.HashSet{} - c.taskIndex = 0 - c.mutex.Unlock() - var taskWorkerCtx context.Context - c.taskChannels = make([]chan model.Instance, 0, c.healthCheckConfig.GetConcurrency()) - taskWorkerCtx, c.taskWorkerCancel = context.WithCancel(context.Background()) - for i := 0; i < c.healthCheckConfig.GetConcurrency(); i++ { - taskChan := make(chan model.Instance, channelBuffer) - c.taskChannels = append(c.taskChannels, taskChan) - go c.healthCheckLoop(taskChan, taskWorkerCtx) - } - case model.EventStop: - c.taskWorkerCancel() - } -} - -func (c *HealthCheckCallBack) healthCheckLoop(taskChannel chan model.Instance, taskWorkerCtx context.Context) { - for { - select { - case <-taskWorkerCtx.Done(): - log.GetDetectLogger().Infof("[HealthCheck] detect task has stopped") - return - case instance := <-taskChannel: - err := c.processHealthCheck(&model.ServiceKey{ - Namespace: instance.GetNamespace(), - Service: instance.GetService(), - }, instance) - if err != nil { - log.GetDetectLogger().Infof("[HealthCheck] fail to do health check, err is %v", err) - } - } - } -} - -func (c *HealthCheckCallBack) processHealthCheck(svc *model.ServiceKey, instance model.Instance) error { - if !instance.IsHealthy() { - // 不健康的实例,不进行探活 - return nil - } - when := c.healthCheckConfig.GetWhen() - cbStatus := instance.GetCircuitBreakerStatus() - if (cbStatus == nil || cbStatus.GetStatus() != model.Open) && when != config.HealthCheckAlways { - return nil - } - success, curTime := c.doConcurrentHealthCheck(instance) - var status model.HealthCheckStatus - if success { - status = model.Healthy - } else { - status = model.Dead - } - // 构造请求,更新探测结果 - updateRequest := &localregistry.ServiceUpdateRequest{ - ServiceKey: *svc, - Properties: []localregistry.InstanceProperties{ - { - ID: instance.GetId(), - Service: svc, - Properties: map[string]interface{}{localregistry.PropertyHealthCheckStatus: &healthCheckingStatus{ - status: status, - startTime: curTime, - }}, - }, - }, - } - log.GetDetectLogger().Infof("[HealthCheck] detect UpdateRequest, request is %s", updateRequest) - return c.registry.UpdateInstances(updateRequest) -} - -func (c *HealthCheckCallBack) doConcurrentHealthCheck(instance model.Instance) (bool, time.Time) { - curTime := time.Now() - for _, checker := range c.healthCheckers { - result, err := checker.DetectInstance(instance) - if err != nil { - log.GetDetectLogger().Errorf("[HealthCheck] timing_flow healthCheck Err:%s", err.Error()) - continue - } - if result == nil { - continue - } - // all success = success - if !result.IsSuccess() { - return false, result.GetDetectTime() - } - curTime = result.GetDetectTime() - } - return true, curTime -} - -// doHealthCheckService 对一组服务进行探活逻辑 -func (c *HealthCheckCallBack) doHealthCheckService(svcInstances model.ServiceInstances) error { - if len(c.healthCheckers) == 0 || len(svcInstances.GetInstances()) == 0 { - return nil - } - // 保存探活的instance - configWhen := c.healthCheckConfig.GetWhen() - for _, oneInstance := range svcInstances.GetInstances() { - if !oneInstance.IsHealthy() || oneInstance.IsIsolated() || oneInstance.GetWeight() == 0 { - // 不健康, 隔离以及权重为0的实例,不进行探活 - continue - } - cbStatus := oneInstance.GetCircuitBreakerStatus() - if configWhen == config.HealthCheckOnRecover && (cbStatus == nil || cbStatus.GetStatus() != model.Open) { - // detect instance when failure - continue - } - var instanceLocalValue local.InstanceLocalValue - instanceLocalValue, ok := oneInstance.(local.InstanceLocalValue) - if !ok { - continue - } - activeDetectStatus := instanceLocalValue.GetActiveDetectStatus() - if nil != activeDetectStatus && time.Since(activeDetectStatus.GetStartTime()) < c.healthCheckConfig.GetInterval() { - continue - } - for i := 0; i < 5; i++ { - var success bool - nextIdx := atomic.AddInt64(&c.taskIndex, 1) - select { - case c.taskChannels[int(nextIdx)%c.healthCheckConfig.GetConcurrency()] <- oneInstance: - success = true - default: - break - } - if success { - break - } - } - } - return nil -} diff --git a/pkg/flow/impl.go b/pkg/flow/impl.go index 61b10be4..f0aeef5d 100644 --- a/pkg/flow/impl.go +++ b/pkg/flow/impl.go @@ -19,13 +19,13 @@ package flow import ( "errors" + "fmt" "sync" "time" "github.com/modern-go/reflect2" "github.com/polarismesh/polaris-go/pkg/config" - "github.com/polarismesh/polaris-go/pkg/flow/cbcheck" "github.com/polarismesh/polaris-go/pkg/flow/configuration" "github.com/polarismesh/polaris-go/pkg/flow/data" "github.com/polarismesh/polaris-go/pkg/flow/quota" @@ -35,7 +35,6 @@ import ( "github.com/polarismesh/polaris-go/pkg/model" "github.com/polarismesh/polaris-go/pkg/model/pb" "github.com/polarismesh/polaris-go/pkg/plugin" - "github.com/polarismesh/polaris-go/pkg/plugin/circuitbreaker" "github.com/polarismesh/polaris-go/pkg/plugin/common" "github.com/polarismesh/polaris-go/pkg/plugin/configconnector" "github.com/polarismesh/polaris-go/pkg/plugin/configfilter" @@ -75,19 +74,15 @@ type Engine struct { plugins plugin.Supplier // 任务调度协程 taskRoutines []schedule.TaskRoutine - // 实时熔断任务队列 - rtCircuitBreakChan chan<- *model.PriorityTask - // 实时熔断公共任务信息 - circuitBreakTask *cbcheck.CircuitBreakCallBack - // 熔断插件链 - circuitBreakerChain []circuitbreaker.InstanceCircuitBreaker + // 熔断引擎 + circuitBreakerFlow *CircuitBreakerFlow // 修改消息订阅插件链 subscribe *subscribeChannel // 配置中心门面类 configFlow *configuration.ConfigFlow // 注册状态管理器 registerStates *registerstate.RegisterStateManager - + // watchEngine . watchEngine *WatchEngine // 配置过滤链 configFilterChain configfilter.Chain @@ -146,25 +141,16 @@ func InitFlowEngine(flowEngine *Engine, initContext plugin.InitContext) error { if err = flowEngine.flowQuotaAssistant.Init(flowEngine, flowEngine.configuration, flowEngine.plugins); err != nil { return err } - // 启动健康探测 - when := cfg.GetConsumer().GetHealthCheck().GetWhen() - disableHealthCheck := when == config.HealthCheckNever - if !disableHealthCheck { - if err = flowEngine.addHealthCheckTask(); err != nil { - return err - } - } // 加载熔断器插件 - enable := cfg.GetConsumer().GetCircuitBreaker().IsEnable() - if enable { - flowEngine.circuitBreakerChain, err = data.GetCircuitBreakers(cfg, plugins) + if enable := cfg.GetConsumer().GetCircuitBreaker().IsEnable(); enable { + breakers, err := data.GetCircuitBreakers(cfg, flowEngine.plugins) if err != nil { return err } - flowEngine.rtCircuitBreakChan, flowEngine.circuitBreakTask, err = flowEngine.addPeriodicCircuitBreakTask() - if err != nil { - return err + if len(breakers) == 0 { + return fmt.Errorf("consumer.circuitBreaker.chain not set") } + flowEngine.circuitBreakerFlow = newCircuitBreakerFlow(flowEngine, breakers[0]) } flowEngine.watchEngine = NewWatchEngine(flowEngine.registry) flowEngine.subscribe = &subscribeChannel{ @@ -260,6 +246,10 @@ func (e *Engine) GetContext() model.ValueContext { return e.globalCtx } +func (e *Engine) CircuitBreakerFlow() *CircuitBreakerFlow { + return e.circuitBreakerFlow +} + // ServiceEventCallback serviceUpdate消息订阅回调 func (e *Engine) ServiceEventCallback(event *common.PluginEvent) error { if e.subscribe != nil { diff --git a/pkg/flow/sync_flow.go b/pkg/flow/sync_flow.go index fe2082c0..4edee974 100644 --- a/pkg/flow/sync_flow.go +++ b/pkg/flow/sync_flow.go @@ -22,7 +22,6 @@ import ( "time" "github.com/polarismesh/polaris-go/pkg/config" - "github.com/polarismesh/polaris-go/pkg/flow/cbcheck" "github.com/polarismesh/polaris-go/pkg/flow/data" "github.com/polarismesh/polaris-go/pkg/flow/registerstate" "github.com/polarismesh/polaris-go/pkg/log" @@ -331,21 +330,18 @@ func (e *Engine) doSyncGetInstances(commonRequest *data.CommonInstancesRequest) commonRequest.DstService, targetCls, instances, totalWeight, commonRequest.DstInstances), nil } -// SyncRegisterV2 async-regis -func (e *Engine) SyncRegisterV2(request *model.InstanceRegisterRequest) (*model.InstanceRegisterResponse, error) { - request.SetDefaultTTL() - - resp, err := e.doSyncRegister(request, registerstate.CreateRegisterV2Header()) - if err != nil { - return nil, err - } - - e.registerStates.PutRegister(request, e.doSyncRegister, e.SyncHeartbeat) - return resp, nil -} - // SyncRegister 同步进行服务注册 func (e *Engine) SyncRegister(instance *model.InstanceRegisterRequest) (*model.InstanceRegisterResponse, error) { + if instance.AutoHeartbeat { + instance.SetDefaultTTL() + resp, err := e.doSyncRegister(instance, registerstate.CreateRegisterV2Header()) + if err != nil { + return nil, err + } + + e.registerStates.PutRegister(instance, e.doSyncRegister, e.SyncHeartbeat) + return resp, nil + } return e.doSyncRegister(instance, nil) } @@ -468,37 +464,7 @@ func (e *Engine) realSyncUpdateServiceCallResult(result *model.ServiceCallResult if err := e.reportSvcStat(result); err != nil { return err } - if nil == e.rtCircuitBreakChan || len(e.circuitBreakerChain) == 0 { - return nil - } - var rtTask *cbcheck.RealTimeLimitTask - for _, cbreaker := range e.circuitBreakerChain { - cbName := cbreaker.Name() - rtLimit, err := cbreaker.Stat(result) - if err != nil { - return model.NewSDKError(model.ErrCodeCircuitBreakerError, err, - "fail to do real time circuitBreak in %s", cbName) - } - if rtLimit && nil == rtTask { - rtTask = &cbcheck.RealTimeLimitTask{ - SvcKey: model.ServiceKey{ - Namespace: result.GetNamespace(), - Service: result.GetService()}, - InstID: result.GetID(), - Host: result.GetHost(), - Port: result.GetPort(), - CbName: cbName} - } - } - if nil == rtTask { - return nil - } - rtCircuitBreakTask := &model.PriorityTask{ - Name: fmt.Sprintf("real-time-cb-%s", result.GetID()), - CallBack: cbcheck.NewCircuitBreakRealTimeCallBack(e.circuitBreakTask, rtTask), - } - log.GetDetectLogger().Debugf("realTime circuit break task %s for %s generated", rtCircuitBreakTask.Name, rtTask.SvcKey) - e.rtCircuitBreakChan <- rtCircuitBreakTask + // TODO 用新的熔断实现进行适配 return nil } @@ -543,13 +509,13 @@ func (e *Engine) doSyncGetServiceRule(commonRequest *data.CommonRuleRequest) (*m apiStartTime := e.globalCtx.Now() for retryTimes < maxRetryTimes { startTime := e.globalCtx.Now() - svcRule := e.registry.GetServiceRouteRule(&commonRequest.DstService.ServiceKey, false) + svcRule := e.registry.GetServiceRule(&commonRequest.DstService, false) if svcRule.IsInitialized() { commonRequest.CallResult.SetSuccess(e.globalCtx.Since(startTime)) return commonRequest.BuildServiceRuleResponse(svcRule), nil } var notifier *common.Notifier - if notifier, err = e.registry.LoadServiceRouteRule(&commonRequest.DstService.ServiceKey); err != nil { + if notifier, err = e.registry.LoadServiceRule(&commonRequest.DstService); err != nil { (&commonRequest.CallResult).SetFail( model.GetErrorCodeFromError(err), e.globalCtx.Since(apiStartTime)) return nil, err diff --git a/pkg/flow/timing_flow.go b/pkg/flow/timing_flow.go index 7df3dd7f..f7bf94e4 100644 --- a/pkg/flow/timing_flow.go +++ b/pkg/flow/timing_flow.go @@ -19,12 +19,9 @@ package flow import ( "github.com/polarismesh/polaris-go/pkg/config" - "github.com/polarismesh/polaris-go/pkg/flow/cbcheck" - "github.com/polarismesh/polaris-go/pkg/flow/detect" "github.com/polarismesh/polaris-go/pkg/flow/schedule" "github.com/polarismesh/polaris-go/pkg/flow/startup" "github.com/polarismesh/polaris-go/pkg/model" - "github.com/polarismesh/polaris-go/pkg/plugin/common" ) const ( @@ -42,28 +39,6 @@ func (e *Engine) ScheduleTask(task *model.PeriodicTask) (chan<- *model.PriorityT return routine.Schedule() } -// addPeriodicCircuitBreakTask 添加定时熔断任务 -func (e *Engine) addPeriodicCircuitBreakTask() (chan<- *model.PriorityTask, *cbcheck.CircuitBreakCallBack, error) { - callback, err := cbcheck.NewCircuitBreakCallBack(e.configuration, e.plugins) - if err != nil { - return nil, nil, err - } - rtChan, taskValues := e.ScheduleTask(&model.PeriodicTask{ - Name: taskCircuitBreak, - CallBack: callback, - TakePriority: true, - LongRun: false, - Period: e.configuration.GetConsumer().GetCircuitBreaker().GetCheckPeriod() / 2, - }) - svcEventHandler := &schedule.ServiceEventHandler{TaskValues: taskValues} - // 注入服务回调函数 - e.plugins.RegisterEventSubscriber(common.OnServiceAdded, common.PluginEventHandler{ - Callback: svcEventHandler.OnServiceAdded}) - e.plugins.RegisterEventSubscriber(common.OnServiceDeleted, common.PluginEventHandler{ - Callback: svcEventHandler.OnServiceDeleted}) - return rtChan, callback, nil -} - // addClientReportTask 添加客户端定期上报任务 func (e *Engine) addClientReportTask() (model.TaskValues, error) { callback, err := startup.NewReportClientCallBack(e.configuration, e.plugins, e.globalCtx) @@ -110,25 +85,3 @@ func (e *Engine) addLoadServerServiceTask() (model.TaskValues, error) { }) return taskValues, nil } - -// addHealthCheckTask 添加客户端主动健康检查任务 -func (e *Engine) addHealthCheckTask() error { - callback, err := detect.NewHealthCheckCallBack(e.configuration, e.plugins) - if err != nil { - return err - } - _, taskValues := e.ScheduleTask(&model.PeriodicTask{ - Name: taskHealthCheck, - CallBack: callback, - TakePriority: false, - LongRun: false, - Period: e.configuration.GetConsumer().GetHealthCheck().GetInterval() / 2, - }) - svcEventHandler := &schedule.ServiceEventHandler{TaskValues: taskValues} - // 注入服务回调函数 - e.plugins.RegisterEventSubscriber(common.OnServiceAdded, common.PluginEventHandler{ - Callback: svcEventHandler.OnServiceAdded}) - e.plugins.RegisterEventSubscriber(common.OnServiceDeleted, common.PluginEventHandler{ - Callback: svcEventHandler.OnServiceDeleted}) - return nil -} diff --git a/pkg/log/log.go b/pkg/log/log.go index 056a12b8..222b1020 100644 --- a/pkg/log/log.go +++ b/pkg/log/log.go @@ -260,6 +260,11 @@ func GetStatLogger() Logger { return logContainer.GetStatLogger() } +// GetCircuitBreakerEventLogger 获取全局统计日志对象 +func GetCircuitBreakerEventLogger() Logger { + return logContainer.GetStatLogger() +} + // GetStatReportLogger 获取统计上报日志对象 func GetStatReportLogger() Logger { return logContainer.GetStatReportLogger() diff --git a/pkg/model/circuitbreaker.go b/pkg/model/circuitbreaker.go new file mode 100644 index 00000000..33abcde0 --- /dev/null +++ b/pkg/model/circuitbreaker.go @@ -0,0 +1,483 @@ +/** + * 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 model + +import ( + "context" + "errors" + "fmt" + "strconv" + "sync" + "sync/atomic" + "time" + + "github.com/polarismesh/specification/source/go/api/v1/fault_tolerance" +) + +type ResourceStat struct { + Resource Resource + RetCode string + Delay time.Duration + RetStatus RetStatus +} + +type Node struct { + Host string + Port uint32 +} + +func (n Node) String() string { + return n.Host + ":" + strconv.FormatUint(uint64(n.Port), 10) +} + +// Resource +type Resource interface { + fmt.Stringer + // GetLevel + GetLevel() fault_tolerance.Level + // GetService + GetService() *ServiceKey + // GetCallerService + GetCallerService() *ServiceKey +} + +type ServiceResource struct { + *abstractResource +} + +func (r *ServiceResource) String() string { + callerSvc := r.callerService + if callerSvc == nil { + callerSvc = EmptyServiceKey + } + return fmt.Sprintf("level=%s|service=%s|caller=%s", r.level.String(), r.service.String(), + callerSvc.String()) +} + +func NewServiceResource(svc, caller *ServiceKey) (*ServiceResource, error) { + abstractRes, err := newAbstractResource(svc, caller) + if err != nil { + return nil, err + } + abstractRes.level = fault_tolerance.Level_SERVICE + res := &ServiceResource{ + abstractResource: abstractRes, + } + return res, nil +} + +type MethodResource struct { + *abstractResource + Method string +} + +func (r *MethodResource) String() string { + callerSvc := r.callerService + if callerSvc == nil { + callerSvc = EmptyServiceKey + } + return fmt.Sprintf("level=%s|method=%s|service=%s|caller=%s", r.level.String(), r.Method, + r.service.String(), callerSvc.String()) +} + +func NewMethodResource(svc, caller *ServiceKey, method string) (*MethodResource, error) { + if method == "" { + return nil, errors.New("method can not be empty") + } + abstractRes, err := newAbstractResource(svc, caller) + if err != nil { + return nil, err + } + abstractRes.level = fault_tolerance.Level_METHOD + res := &MethodResource{ + abstractResource: abstractRes, + Method: method, + } + return res, nil +} + +type InstanceResource struct { + *abstractResource + protocol string + node Node +} + +func (r *InstanceResource) GetProtocol() string { + return r.protocol +} + +func (r *InstanceResource) GetNode() Node { + return r.node +} + +func (r *InstanceResource) String() string { + callerSvc := r.callerService + if callerSvc == nil { + callerSvc = EmptyServiceKey + } + return fmt.Sprintf("level=%s|instance=%s|service=%s|caller=%s", r.level.String(), r.node.String(), + r.service.String(), callerSvc.String()) +} + +func NewInstanceResource(svc, caller *ServiceKey, protocol, host string, port uint32) (*InstanceResource, error) { + if host == "" { + return nil, errors.New("host can not be empty") + } + abstractRes, err := newAbstractResource(svc, caller) + if err != nil { + return nil, err + } + abstractRes.level = fault_tolerance.Level_INSTANCE + res := &InstanceResource{ + abstractResource: abstractRes, + protocol: protocol, + node: Node{Host: host, Port: port}, + } + return res, nil +} + +type abstractResource struct { + level fault_tolerance.Level + service *ServiceKey + callerService *ServiceKey +} + +func newAbstractResource(service, caller *ServiceKey) (*abstractResource, error) { + if service == nil { + return nil, errors.New("service can not be empty") + } + if service.Namespace == "" { + return nil, errors.New("namespace can not be blank") + } + if service.Service == "" { + return nil, errors.New("service can not be blank") + } + return &abstractResource{ + service: service, + callerService: caller, + }, nil +} + +func (ar *abstractResource) GetLevel() fault_tolerance.Level { + return ar.level +} + +func (ar *abstractResource) GetService() *ServiceKey { + return ar.service +} + +func (ar *abstractResource) GetCallerService() *ServiceKey { + return ar.callerService +} + +// CircuitBreakerStatus 熔断器状态管理器 +type CircuitBreakerStatus interface { + // GetCircuitBreaker 标识被哪个熔断器熔断 + GetCircuitBreaker() string + // GetStatus 熔断状态 + GetStatus() Status + // GetStartTime 状态转换的时间 + GetStartTime() time.Time + // GetFallbackInfo 获取熔断器的降级信息 + GetFallbackInfo() *FallbackInfo + // SetFallbackInfo 获取熔断器的降级信息 + SetFallbackInfo(*FallbackInfo) +} + +// Status 断路器状态 +type Status int + +const ( + // Open 断路器已打开,代表节点已经被熔断 + Open Status = 1 + // HalfOpen 断路器半开,节点处于刚熔断恢复,只允许少量请求通过 + HalfOpen Status = 2 + // Close 断路器关闭,节点处于正常工作状态 + Close Status = 3 +) + +// String toString method +func (s Status) String() string { + switch s { + case Open: + return "open" + case HalfOpen: + return "half-open" + case Close: + return "close" + } + return "unknown" +} + +// HealthCheckStatus 健康探测状态 +type HealthCheckStatus int + +const ( + // Healthy 节点探测结果已经恢复健康, 代表可以放开一部分流量 + Healthy HealthCheckStatus = 1 + // Dead 节点仍然不可用 + Dead HealthCheckStatus = 2 +) + +// ActiveDetectStatus 健康探测管理器 +type ActiveDetectStatus interface { + // GetStatus 健康探测结果状态 + GetStatus() HealthCheckStatus + // GetStartTime 状态转换的时间 + GetStartTime() time.Time +} + +// CircuitBreakerStatusImpl 熔断状态模型,实现了CircuitBreakerStatus接口 +type CircuitBreakerStatusImpl struct { + CircuitBreaker string + Status Status + StartTime time.Time + fallbackInfo *FallbackInfo +} + +// GetCircuitBreaker 标识被哪个熔断器熔断 +func (c *CircuitBreakerStatusImpl) GetCircuitBreaker() string { + return c.CircuitBreaker +} + +// GetStatus 熔断状态 +func (c *CircuitBreakerStatusImpl) GetStatus() Status { + return c.Status +} + +// GetStartTime 状态转换的时间 +func (c *CircuitBreakerStatusImpl) GetStartTime() time.Time { + return c.StartTime +} + +// String 打印熔断状态数据 +func (c CircuitBreakerStatusImpl) String() string { + return fmt.Sprintf("{circuitBreaker:%s, status:%v, startTime:%v}", + c.CircuitBreaker, c.Status, c.StartTime) +} + +type CustomerFunction func(ctx context.Context, args interface{}) (interface{}, error) + +type DecoratorFunction func(ctx context.Context, args interface{}) (interface{}, *CallAborted, error) + +type FallbackInfo struct { + Code int + Headers map[string]string + Body string +} + +type ResultToErrorCode interface { + // OnSuccess + OnSuccess(val interface{}) string + // OnError + OnError(err error) string +} + +type CheckResult struct { + Pass bool + RuleName string + FallbackInfo *FallbackInfo +} + +type RequestContext struct { + Caller *ServiceKey + Callee *ServiceKey + Method string + CodeConvert ResultToErrorCode +} + +type ResponseContext struct { + Duration time.Duration + Result interface{} + Err error +} + +// InvokeHandler . +type InvokeHandler interface { + // AcquirePermission . + AcquirePermission() (bool, *CallAborted, error) + // OnSuccess . + OnSuccess(*ResponseContext) + // OnError . + OnError(*ResponseContext) +} + +func NewCallAborted(err error, + rule string, + fallback *FallbackInfo) *CallAborted { + return &CallAborted{ + err: err, + rule: rule, + fallback: fallback, + } +} + +type CallAborted struct { + err error + rule string + fallback *FallbackInfo +} + +func (c *CallAborted) HasFallback() bool { + return c.fallback != nil +} + +func (c *CallAborted) GetFallbackCode() int { + if !c.HasFallback() { + return 0 + } + return c.fallback.Code +} + +func (c *CallAborted) GetFallbackBody() string { + if !c.HasFallback() { + return "" + } + return c.fallback.Body +} + +func (c *CallAborted) GetFallbackHeaders() map[string]string { + if !c.HasFallback() { + return map[string]string{} + } + return c.fallback.Headers +} + +func (c *CallAborted) GetError() error { + return c.err +} + +type InitCircuitBreakerStatus func(CircuitBreakerStatus) + +func NewCircuitBreakerStatus(name string, status Status, startTime time.Time, + options ...InitCircuitBreakerStatus) CircuitBreakerStatus { + impl := &BaseCircuitBreakerStatus{ + name: name, + status: status, + startTime: startTime, + } + for i := range options { + options[i](impl) + } + return impl +} + +type BaseCircuitBreakerStatus struct { + name string + status Status + startTime time.Time + fallbackInfo *FallbackInfo +} + +// GetCircuitBreaker 标识被哪个熔断器熔断 +func (c *BaseCircuitBreakerStatus) GetCircuitBreaker() string { + return c.name +} + +// GetStatus 熔断状态 +func (c *BaseCircuitBreakerStatus) GetStatus() Status { + return c.status +} + +// GetStartTime 状态转换的时间 +func (c *BaseCircuitBreakerStatus) GetStartTime() time.Time { + return c.startTime +} + +func (c *BaseCircuitBreakerStatus) GetFallbackInfo() *FallbackInfo { + return c.fallbackInfo +} + +func (c *BaseCircuitBreakerStatus) SetFallbackInfo(info *FallbackInfo) { + c.fallbackInfo = info +} + +func (c *BaseCircuitBreakerStatus) IsAvailable() bool { + if c.status == Close { + return true + } + if c.status == Open { + return false + } + return true +} + +type HalfOpenStatus struct { + BaseCircuitBreakerStatus + maxRequest int + scheduled int32 + calledResult []bool + triggered bool + lock sync.Mutex +} + +func NewHalfOpenStatus(name string, start time.Time, maxRequest int) CircuitBreakerStatus { + return &HalfOpenStatus{ + BaseCircuitBreakerStatus: BaseCircuitBreakerStatus{ + name: name, + status: HalfOpen, + startTime: start, + }, + maxRequest: maxRequest, + } +} + +func (c *HalfOpenStatus) Report(success bool) bool { + c.lock.Lock() + defer c.lock.Unlock() + + c.calledResult = append(c.calledResult, success) + needTrigger := !success || (len(c.calledResult) >= c.maxRequest) + if needTrigger && !c.triggered { + c.triggered = true + return true + } + return false +} + +func (c *HalfOpenStatus) Schedule() bool { + return atomic.CompareAndSwapInt32(&c.scheduled, 0, 1) +} + +func (c *HalfOpenStatus) CalNextStatus() Status { + c.lock.Lock() + defer c.lock.Unlock() + + if !c.triggered { + return HalfOpen + } + + for _, ret := range c.calledResult { + if !ret { + return Open + } + } + return Close +} + +func (c *HalfOpenStatus) IsAvailable() bool { + if c.status == Close { + return true + } + if c.status == Open { + return false + } + return true +} + +var ErrorCallAborted = errors.New("call aborted") diff --git a/pkg/model/core.go b/pkg/model/core.go index 51343338..f766fd37 100644 --- a/pkg/model/core.go +++ b/pkg/model/core.go @@ -35,6 +35,10 @@ const ( EventRateLimiting EventType = 0x2003 // EventServices 批量服务 EventServices EventType = 0x2005 + // EventCircuitBreaker 熔断规则 + EventCircuitBreaker EventType = 0x2006 + // EventFaultDetect 探测规则 + EventFaultDetect EventType = 0x2007 ) // RegistryValue 存储于sdk缓存中的对象,包括服务实例和服务路由 @@ -62,17 +66,21 @@ func (e EventType) String() string { var ( // 路由规则到日志回显 eventTypeToPresent = map[EventType]string{ - EventInstances: "instance", - EventRouting: "routing", - EventRateLimiting: "rate_limiting", - EventServices: "services", + EventInstances: "instance", + EventRouting: "routing", + EventRateLimiting: "rate_limiting", + EventServices: "services", + EventCircuitBreaker: "circuit_breaker", + EventFaultDetect: "fault_detect", } presentToEventType = map[string]EventType{ - "instance": EventInstances, - "routing": EventRouting, - "rate_limiting": EventRateLimiting, - "services": EventServices, + "instance": EventInstances, + "routing": EventRouting, + "rate_limiting": EventRateLimiting, + "services": EventServices, + "circuit_breaker": EventCircuitBreaker, + "fault_detect": EventFaultDetect, } ) @@ -84,6 +92,8 @@ func ToEventType(value string) EventType { return EventUnknown } +var EmptyServiceKey = &ServiceKey{} + // ServiceKey 服务的唯一标识KEY type ServiceKey struct { // 命名空间 diff --git a/pkg/model/engine.go b/pkg/model/engine.go index b3c2c0e9..7e083a44 100644 --- a/pkg/model/engine.go +++ b/pkg/model/engine.go @@ -82,8 +82,6 @@ type Engine interface { SyncGetInstances(req *GetInstancesRequest) (*InstancesResponse, error) // SyncGetAllInstances 同步获取全量服务实例 SyncGetAllInstances(req *GetAllInstancesRequest) (*InstancesResponse, error) - // SyncRegisterV2 同步进行服务注册,并且会自动进行心跳上报动作 - SyncRegisterV2(Instance *InstanceRegisterRequest) (*InstanceRegisterResponse, error) // SyncRegister 同步进行服务注册 SyncRegister(instance *InstanceRegisterRequest) (*InstanceRegisterResponse, error) // SyncDeregister 同步进行服务反注册 @@ -128,4 +126,12 @@ type Engine interface { WatchAllInstances(request *WatchAllInstancesRequest) (*WatchAllInstancesResponse, error) // WatchAllServices 监听服务列表变更事件 WatchAllServices(request *WatchAllServicesRequest) (*WatchAllServicesResponse, error) + // Check + Check(Resource) (*CheckResult, error) + // Report + Report(*ResourceStat) error + // MakeFunctionDecorator + MakeFunctionDecorator(CustomerFunction, *RequestContext) DecoratorFunction + // MakeInvokeHandler + MakeInvokeHandler(*RequestContext) InvokeHandler } diff --git a/pkg/model/pb/circuitbreaker.go b/pkg/model/pb/circuitbreaker.go new file mode 100644 index 00000000..08ce9380 --- /dev/null +++ b/pkg/model/pb/circuitbreaker.go @@ -0,0 +1,73 @@ +/** + * 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 pb + +import ( + "github.com/golang/protobuf/proto" + "github.com/polarismesh/specification/source/go/api/v1/service_manage" + + "github.com/polarismesh/polaris-go/pkg/model" +) + +type CircuitBreakAssistant struct { +} + +// ParseRuleValue 解析出具体的规则值 +func (a *CircuitBreakAssistant) ParseRuleValue(resp *service_manage.DiscoverResponse) (proto.Message, string) { + var revision string + circuitBreakerValue := resp.CircuitBreaker + if nil == circuitBreakerValue { + return circuitBreakerValue, revision + } + revision = circuitBreakerValue.GetRevision().GetValue() + return circuitBreakerValue, revision +} + +// SetDefault 设置默认值 +func (a *CircuitBreakAssistant) SetDefault(message proto.Message) { + +} + +// Validate 规则校验 +func (a *CircuitBreakAssistant) Validate(message proto.Message, cache model.RuleCache) error { + return nil +} + +type FaultDetectAssistant struct { +} + +// ParseRuleValue 解析出具体的规则值 +func (a *FaultDetectAssistant) ParseRuleValue(resp *service_manage.DiscoverResponse) (proto.Message, string) { + var revision string + faultDetectValue := resp.FaultDetector + if nil == faultDetectValue { + return faultDetectValue, revision + } + revision = faultDetectValue.GetRevision() + return faultDetectValue, revision +} + +// SetDefault 设置默认值 +func (a *FaultDetectAssistant) SetDefault(message proto.Message) { + +} + +// Validate 规则校验 +func (a *FaultDetectAssistant) Validate(message proto.Message, cache model.RuleCache) error { + return nil +} diff --git a/pkg/model/pb/rule.go b/pkg/model/pb/rule.go index 02449ba3..610e47ec 100644 --- a/pkg/model/pb/rule.go +++ b/pkg/model/pb/rule.go @@ -39,8 +39,10 @@ type ServiceRuleAssistant interface { } var eventTypeToAssistant = map[model.EventType]ServiceRuleAssistant{ - model.EventRouting: &RoutingAssistant{}, - model.EventRateLimiting: &RateLimitingAssistant{}, + model.EventRouting: &RoutingAssistant{}, + model.EventRateLimiting: &RateLimitingAssistant{}, + model.EventCircuitBreaker: &CircuitBreakAssistant{}, + model.EventFaultDetect: &FaultDetectAssistant{}, } // ServiceRuleInProto 路由规则配置对象. diff --git a/pkg/model/pb/service.go b/pkg/model/pb/service.go index ac01ab87..791b7659 100644 --- a/pkg/model/pb/service.go +++ b/pkg/model/pb/service.go @@ -22,6 +22,7 @@ import ( "fmt" "io" "sort" + "strconv" "sync/atomic" apimodel "github.com/polarismesh/specification/source/go/api/v1/model" @@ -44,10 +45,13 @@ type SvcPluginValues struct { // ServiceInstancesInProto 通用的应答. type ServiceInstancesInProto struct { - service *apiservice.Service - notExists bool - instances []model.Instance - instancesMap map[string]model.Instance + service *apiservice.Service + notExists bool + instances []model.Instance + // instancesMap instanceId -> model.Instance + instancesMap map[string]model.Instance + // endpointMapping host:port -> instanceId + endpointMapping map[string]string initialized bool svcIDSet model.HashSet totalWeight int @@ -91,6 +95,7 @@ func NewServiceInstancesInProto(resp *apiservice.DiscoverResponse, createLocalVa service: resp.Service, instances: make([]model.Instance, 0, len(resp.Instances)), instancesMap: make(map[string]model.Instance, len(resp.Instances)), + endpointMapping: make(map[string]string, len(resp.Instances)), initialized: true, svcIDSet: model.HashSet{}, svcPluginValues: pluginValues, @@ -125,6 +130,8 @@ func NewServiceInstancesInProto(resp *apiservice.DiscoverResponse, createLocalVa instancesInProto.instances = append(instancesInProto.instances, instanceInProto) instancesInProto.svcIDSet.Add(instId) instancesInProto.instancesMap[instId] = instanceInProto + endpoint := instanceInProto.GetHost() + ":" + strconv.FormatUint(uint64(instanceInProto.GetPort()), 10) + instancesInProto.endpointMapping[endpoint] = instId clusterCache.AddInstance(instanceInProto) } } @@ -244,6 +251,19 @@ func (s *ServiceInstancesInProto) GetInstanceLocalValue(instId string) local.Ins return nil } +// GetInstanceLocalValueByEndpoint . +func (s *ServiceInstancesInProto) GetInstanceLocalValueByEndpoint(host string, port uint32) local.InstanceLocalValue { + endpoint := host + ":" + strconv.Itoa(int(port)) + instId, ok := s.endpointMapping[endpoint] + if !ok { + return nil + } + if inst, ok := s.instancesMap[instId]; ok { + return inst.(*InstanceInProto).GetInstanceLocalValue() + } + return nil +} + // GetInstance 通过实例ID获取实例. func (s *ServiceInstancesInProto) GetInstance(instId string) model.Instance { return s.instancesMap[instId] diff --git a/pkg/model/pb/validate.go b/pkg/model/pb/validate.go index 46234618..76897dd5 100644 --- a/pkg/model/pb/validate.go +++ b/pkg/model/pb/validate.go @@ -29,17 +29,21 @@ import ( var ( eventTypeToProtoRequestType = map[model.EventType]apiservice.DiscoverRequest_DiscoverRequestType{ - model.EventInstances: apiservice.DiscoverRequest_INSTANCE, - model.EventRouting: apiservice.DiscoverRequest_ROUTING, - model.EventRateLimiting: apiservice.DiscoverRequest_RATE_LIMIT, - model.EventServices: apiservice.DiscoverRequest_SERVICES, + model.EventInstances: apiservice.DiscoverRequest_INSTANCE, + model.EventRouting: apiservice.DiscoverRequest_ROUTING, + model.EventRateLimiting: apiservice.DiscoverRequest_RATE_LIMIT, + model.EventServices: apiservice.DiscoverRequest_SERVICES, + model.EventCircuitBreaker: apiservice.DiscoverRequest_CIRCUIT_BREAKER, + model.EventFaultDetect: apiservice.DiscoverRequest_FAULT_DETECTOR, } protoRespTypeToEventType = map[apiservice.DiscoverResponse_DiscoverResponseType]model.EventType{ - apiservice.DiscoverResponse_INSTANCE: model.EventInstances, - apiservice.DiscoverResponse_ROUTING: model.EventRouting, - apiservice.DiscoverResponse_RATE_LIMIT: model.EventRateLimiting, - apiservice.DiscoverResponse_SERVICES: model.EventServices, + apiservice.DiscoverResponse_INSTANCE: model.EventInstances, + apiservice.DiscoverResponse_ROUTING: model.EventRouting, + apiservice.DiscoverResponse_RATE_LIMIT: model.EventRateLimiting, + apiservice.DiscoverResponse_SERVICES: model.EventServices, + apiservice.DiscoverResponse_CIRCUIT_BREAKER: model.EventCircuitBreaker, + apiservice.DiscoverResponse_FAULT_DETECTOR: model.EventFaultDetect, } ) diff --git a/pkg/model/service.go b/pkg/model/service.go index 29578426..81ca1a08 100644 --- a/pkg/model/service.go +++ b/pkg/model/service.go @@ -78,75 +78,6 @@ type ServiceInstances interface { IsCacheLoaded() bool } -// Status 断路器状态 -type Status int - -const ( - // Open 断路器已打开,代表节点已经被熔断 - Open Status = 1 - // HalfOpen 断路器半开,节点处于刚熔断恢复,只允许少量请求通过 - HalfOpen Status = 2 - // Close 断路器关闭,节点处于正常工作状态 - Close Status = 3 -) - -// String toString method -func (s Status) String() string { - switch s { - case Open: - return "open" - case HalfOpen: - return "half-open" - case Close: - return "close" - } - return "unknown" -} - -// HealthCheckStatus 健康探测状态 -type HealthCheckStatus int - -const ( - // Healthy 节点探测结果已经恢复健康, 代表可以放开一部分流量 - Healthy HealthCheckStatus = 1 - // Dead 节点仍然不可用 - Dead HealthCheckStatus = 2 -) - -// CircuitBreakerStatus 熔断器状态管理器 -type CircuitBreakerStatus interface { - // GetCircuitBreaker 标识被哪个熔断器熔断 - GetCircuitBreaker() string - // GetStatus 熔断状态 - GetStatus() Status - // GetStartTime 状态转换的时间 - GetStartTime() time.Time - // IsAvailable 是否可以分配请求 - IsAvailable() bool - // Allocate 执行请求分配 - Allocate() bool - // GetRequestsAfterHalfOpen 获取进入半开状态之后分配的请求数 - GetRequestsAfterHalfOpen() int32 - // GetFailRequestsAfterHalfOpen 获取进入半开状态之后的失败请求数 - GetFailRequestsAfterHalfOpen() int32 - // AddRequestCountAfterHalfOpen 添加半开状态下面的请求数 - AddRequestCountAfterHalfOpen(n int32, success bool) int32 - // GetFinalAllocateTimeInt64 获取分配了最后配额的时间 - GetFinalAllocateTimeInt64() int64 - // AcquireStatusLock 获取状态转换锁,主要是避免状态重复发生转变,如多个协程上报调用失败时,每个stat方法都返回需要转化为熔断状态 - AcquireStatusLock() bool - // AllocatedRequestsAfterHalfOpen 获取在半开之后,分配出去的请求数,即getOneInstance接口返回这个实例的次数 - AllocatedRequestsAfterHalfOpen() int32 -} - -// ActiveDetectStatus 健康探测管理器 -type ActiveDetectStatus interface { - // GetStatus 健康探测结果状态 - GetStatus() HealthCheckStatus - // GetStartTime 状态转换的时间 - GetStartTime() time.Time -} - // Instance 服务实例信息 type Instance interface { // GetInstanceKey 获取实例四元组标识 @@ -269,6 +200,8 @@ type GetOneInstanceRequest struct { LbPolicy string // 金丝雀 Canary string + // 可选,是否包含被熔断的服务实例,默认false + IncludeCircuitBreakInstances bool } // SetTimeout 设置超时时间 @@ -1233,8 +1166,6 @@ const ( type InstanceRegisterRequest struct { // 必选,服务名 Service string - // 必选,服务访问Token - ServiceToken string // 必选,命名空间 Namespace string // 必选,服务监听host,支持IPv6地址 @@ -1243,6 +1174,8 @@ type InstanceRegisterRequest struct { Port int // 以下字段可选,默认nil表示客户端不配置,使用服务端配置 + // 可选,服务访问Token + ServiceToken string // 服务协议 Protocol *string // 服务权重,默认100,范围0-10000 @@ -1269,6 +1202,8 @@ type InstanceRegisterRequest struct { RetryCount *int // 可选,指定实例id InstanceId string + // 可选, 是否将心跳上报交由 SDK 内部定时任务进行处理 + AutoHeartbeat bool } // String 打印消息内容 diff --git a/pkg/model/util.go b/pkg/model/util.go index a297f3e9..6da1b968 100644 --- a/pkg/model/util.go +++ b/pkg/model/util.go @@ -260,18 +260,6 @@ func GetErrorCodeFromError(e error) ErrCode { return sdkErr.ErrorCode() } -// IsInstanceAvailable 服务实例是否可用 -func IsInstanceAvailable(instance Instance) bool { - if !instance.IsHealthy() { - return false - } - cbStatus := instance.GetCircuitBreakerStatus() - if nil != cbStatus && !cbStatus.IsAvailable() { - return false - } - return true -} - // SortMap 对map进行排序, keys的长度必须等于map的长度 // 返回已经排序的key,以及map中总字符串长度 func SortMap(values map[string]string, keys []string) ([]string, int) { diff --git a/pkg/network/impl.go b/pkg/network/impl.go index e2759d9b..d4aad074 100644 --- a/pkg/network/impl.go +++ b/pkg/network/impl.go @@ -206,7 +206,7 @@ func (s *ServerAddressList) ConnectServerByAddrOnly(addr string, timeout time.Du func (s *ServerAddressList) tryGetConnection(timeout time.Duration, hashKey []byte) (*Connection, error) { curConnValue := s.loadCurrentConnection() if IsAvailableConnection(curConnValue) { - // log.GetBaseLogger().Debugf("[CheckConnection]traceCheck IsAvailableConnection") + // log.GetNetworkLogger().Debugf("[CheckConnection]traceCheck IsAvailableConnection") return curConnValue, nil } s.connectMutex.Lock() @@ -406,7 +406,7 @@ func (c *connectionManager) ConnectByAddr(clusterType config.ClusterType, addr s // ReportSuccess 上报服务成功 func (c *connectionManager) ReportSuccess(connID ConnID, retCode int32, timeout time.Duration) { - log.GetBaseLogger().Debugf("service %s: reported success", connID.Service) + log.GetNetworkLogger().Debugf("service %s: reported success", connID.Service) var err error if !reflect2.IsNil(connID.instance) { engineValue, ok := c.valueCtx.GetValue(model.ContextKeyEngine) @@ -421,14 +421,14 @@ func (c *connectionManager) ReportSuccess(connID ConnID, retCode int32, timeout } } if err != nil { - log.GetBaseLogger().Errorf( + log.GetNetworkLogger().Errorf( "error to update success call result for connection %s, %s", connID.String(), err) } } // ReportFail 上报服务失败 func (c *connectionManager) ReportFail(connID ConnID, retCode int32, timeout time.Duration) { - log.GetBaseLogger().Warnf("connection %s: reported fail", connID) + log.GetNetworkLogger().Warnf("connection %s: reported fail", connID) var err error if !reflect2.IsNil(connID.instance) && connID.Service.ClusterType != config.BuiltinCluster { engineValue, ok := c.valueCtx.GetValue(model.ContextKeyEngine) @@ -443,23 +443,23 @@ func (c *connectionManager) ReportFail(connID ConnID, retCode int32, timeout tim } } if err != nil { - log.GetBaseLogger().Errorf( + log.GetNetworkLogger().Errorf( "error to update fail call result for connection %s, %s", connID.String(), err) } } // ReportConnectionDown 报告连接故障 func (c *connectionManager) ReportConnectionDown(connID ConnID) { - log.GetBaseLogger().Tracef("connection %s: reported down", connID) + log.GetNetworkLogger().Tracef("connection %s: reported down", connID) var svc = connID.Service var serverList *ServerAddressList var ok bool serverList, ok = c.serverServices[svc.ClusterType] if !ok { - log.GetBaseLogger().Warnf("connection %s down received from unknown service %s", connID, svc) + log.GetNetworkLogger().Warnf("connection %s down received from unknown service %s", connID, svc) return } - log.GetBaseLogger().Infof("connection %s down received from service %s", connID, svc.String()) + log.GetNetworkLogger().Infof("connection %s down received from service %s", connID, svc.String()) curConn := serverList.loadCurrentConnection() if nil != curConn && connID.ID != curConn.ConnID.ID { // 已经切换新连接,忽略 @@ -492,16 +492,13 @@ func (c *connectionManager) doSwitchRoutine() { for { select { case <-c.ctx.Done(): - log.GetBaseLogger().Infof("doSwitchRoutine of connection manager has been terminated") + log.GetNetworkLogger().Infof("doSwitchRoutine of connection manager has been terminated") return - case <-buildInCloseTicker.C: - serverList := c.serverServices[config.BuiltinCluster] - serverList.closeCurrentConnection(false) + // case <-buildInCloseTicker.C: + // serverList := c.serverServices[config.BuiltinCluster] + // serverList.closeCurrentConnection(false) case <-switchTicker.C: for clusterType, serverList := range c.serverServices { - if clusterType == config.BuiltinCluster { - continue - } if ctrl, ok := DefaultServerServiceToConnectionControl[clusterType]; ok && ctrl == ConnectionLong { // 只有长连接模式才切换server curConn := serverList.loadCurrentConnection() @@ -529,7 +526,7 @@ func (c *connectionManager) UpdateServers(svcEventKey model.ServiceEventKey) { return } value := atomic.AddUint32(&c.ready, 1) - log.GetBaseLogger().Infof("discover server updated to ready %v, event is %s", value, svcEventKey) + log.GetNetworkLogger().Infof("discover server updated to ready %v, event is %s", value, svcEventKey) } } diff --git a/pkg/plugin/circuitbreaker/circuitbreaker.go b/pkg/plugin/circuitbreaker/circuitbreaker.go index 535c861b..a56ba814 100644 --- a/pkg/plugin/circuitbreaker/circuitbreaker.go +++ b/pkg/plugin/circuitbreaker/circuitbreaker.go @@ -25,14 +25,13 @@ import ( "github.com/polarismesh/polaris-go/pkg/plugin/common" ) -// InstanceCircuitBreaker 【扩展点接口】节点熔断 -type InstanceCircuitBreaker interface { +// CircuitBreaker 【扩展点接口】资源熔断(实例熔断、方法熔断、服务熔断) +type CircuitBreaker interface { plugin.Plugin - // Stat 进行调用统计,返回当前实例是否需要进行立即熔断 - Stat(model.InstanceGauge) (bool, error) - // CircuitBreak 进行熔断计算,返回需要进行状态转换的实例ID - // 入参包括全量服务实例,以及当前周期的健康探测结果 - CircuitBreak(instances []model.Instance) (*Result, error) + // CheckResource get the resource circuitbreaker status + CheckResource(model.Resource) model.CircuitBreakerStatus + // Report report resource invoke result stat + Report(*model.ResourceStat) error } // Result 熔断结算结果 @@ -83,5 +82,5 @@ func (r *Result) IsEmpty() bool { // 初始化 func init() { - plugin.RegisterPluginInterface(common.TypeCircuitBreaker, new(InstanceCircuitBreaker)) + plugin.RegisterPluginInterface(common.TypeCircuitBreaker, new(CircuitBreaker)) } diff --git a/pkg/plugin/circuitbreaker/proxy.go b/pkg/plugin/circuitbreaker/proxy.go index acfecf89..31e6348d 100644 --- a/pkg/plugin/circuitbreaker/proxy.go +++ b/pkg/plugin/circuitbreaker/proxy.go @@ -25,25 +25,23 @@ import ( // Proxy .proxy of InstanceCircuitBreaker type Proxy struct { - InstanceCircuitBreaker + CircuitBreaker engine model.Engine } // Stat proxy InstanceCircuitBreaker stat -func (p *Proxy) Stat(gauge model.InstanceGauge) (bool, error) { - toCb, err := p.InstanceCircuitBreaker.Stat(gauge) - return toCb, err +func (p *Proxy) CheckResource(res model.Resource) model.CircuitBreakerStatus { + return p.CircuitBreaker.CheckResource(res) } // CircuitBreak proxy InstanceCircuitBreaker CircuitBreak -func (p *Proxy) CircuitBreak(instances []model.Instance) (*Result, error) { - cbResult, err := p.InstanceCircuitBreaker.CircuitBreak(instances) - return cbResult, err +func (p *Proxy) Report(stat *model.ResourceStat) error { + return p.CircuitBreaker.Report(stat) } // SetRealPlugin 设置 func (p *Proxy) SetRealPlugin(plug plugin.Plugin, engine model.Engine) { - p.InstanceCircuitBreaker = plug.(InstanceCircuitBreaker) + p.CircuitBreaker = plug.(CircuitBreaker) p.engine = engine } diff --git a/pkg/plugin/configconnector/config_file.go b/pkg/plugin/configconnector/config_file.go index d4428bfb..0e726309 100644 --- a/pkg/plugin/configconnector/config_file.go +++ b/pkg/plugin/configconnector/config_file.go @@ -23,9 +23,10 @@ import ( "encoding/json" "strconv" - "github.com/polarismesh/polaris-go/pkg/model" "github.com/polarismesh/specification/source/go/api/v1/config_manage" "google.golang.org/protobuf/types/known/wrapperspb" + + "github.com/polarismesh/polaris-go/pkg/model" ) const ( diff --git a/pkg/plugin/configconnector/config_file_response.go b/pkg/plugin/configconnector/config_file_response.go index 4a9b5b98..f4f8911a 100644 --- a/pkg/plugin/configconnector/config_file_response.go +++ b/pkg/plugin/configconnector/config_file_response.go @@ -21,8 +21,9 @@ package configconnector import ( "time" - "github.com/polarismesh/polaris-go/pkg/model" "github.com/polarismesh/specification/source/go/api/v1/config_manage" + + "github.com/polarismesh/polaris-go/pkg/model" ) // ConfigFileResponse 配置文件响应体 diff --git a/pkg/plugin/healthcheck/healthcheck.go b/pkg/plugin/healthcheck/healthcheck.go index 138fa0ac..21967c57 100644 --- a/pkg/plugin/healthcheck/healthcheck.go +++ b/pkg/plugin/healthcheck/healthcheck.go @@ -20,6 +20,8 @@ package healthcheck import ( "time" + "github.com/polarismesh/specification/source/go/api/v1/fault_tolerance" + "github.com/polarismesh/polaris-go/pkg/model" "github.com/polarismesh/polaris-go/pkg/plugin" "github.com/polarismesh/polaris-go/pkg/plugin/common" @@ -28,9 +30,10 @@ import ( // HealthChecker 【扩展点接口】主动健康探测策略 type HealthChecker interface { plugin.Plugin - // DetectInstance 对单个实例进行探测,返回探测结果 - // DetectInstance 每个探测方法自己去判断当前周期是否需要探测,如果无需探测,则返回nil + // DetectInstance 对单个实例进行探测,返回探测结果, 每个探测方法自己去判断当前周期是否需要探测,如果无需探测,则返回nil DetectInstance(model.Instance) (DetectResult, error) + // Protocol . + Protocol() fault_tolerance.FaultDetectRule_Protocol } // DetectResult 健康探测结果 @@ -41,6 +44,12 @@ type DetectResult interface { GetDetectTime() time.Time // GetDetectInstance 探测是实例 GetDetectInstance() model.Instance + // GetCode() return code + GetCode() string + // GetDelay + GetDelay() time.Duration + // GetRetStatus + GetRetStatus() model.RetStatus } // DetectResultImp 探活返回的结果,plugin.DetectResult的实现 @@ -48,6 +57,9 @@ type DetectResultImp struct { Success bool DetectTime time.Time // 探测时间 DetectInstance model.Instance // 探测的实例 + delay time.Duration + code string + status model.RetStatus } // IsSuccess 探测类型,与探测插件名相同 @@ -65,6 +77,20 @@ func (r *DetectResultImp) GetDetectInstance() model.Instance { return r.DetectInstance } +// GetCode() return code +func (r *DetectResultImp) GetCode() string { + return r.code +} + +// GetDelay +func (r *DetectResultImp) GetDelay() time.Duration { + return r.delay +} + +func (r *DetectResultImp) GetRetStatus() model.RetStatus { + return r.status +} + // init 初始化 func init() { plugin.RegisterPluginInterface(common.TypeHealthCheck, new(HealthChecker)) diff --git a/pkg/plugin/healthcheck/proxy.go b/pkg/plugin/healthcheck/proxy.go index b8db5281..f2816cfa 100644 --- a/pkg/plugin/healthcheck/proxy.go +++ b/pkg/plugin/healthcheck/proxy.go @@ -18,6 +18,8 @@ package healthcheck import ( + "github.com/polarismesh/specification/source/go/api/v1/fault_tolerance" + "github.com/polarismesh/polaris-go/pkg/model" "github.com/polarismesh/polaris-go/pkg/plugin" "github.com/polarismesh/polaris-go/pkg/plugin/common" @@ -41,6 +43,11 @@ func (p *Proxy) DetectInstance(inst model.Instance) (DetectResult, error) { return result, err } +// Protocol . +func (p *Proxy) Protocol() fault_tolerance.FaultDetectRule_Protocol { + return p.HealthChecker.Protocol() +} + // init 注册proxy func init() { plugin.RegisterPluginProxy(common.TypeHealthCheck, &Proxy{}) diff --git a/pkg/plugin/loadbalancer/proxy.go b/pkg/plugin/loadbalancer/proxy.go index eaac1669..5da24d26 100644 --- a/pkg/plugin/loadbalancer/proxy.go +++ b/pkg/plugin/loadbalancer/proxy.go @@ -44,29 +44,8 @@ type SelectStatus struct { // ChooseInstance proxy LoadBalancer ChooseInstance func (p *Proxy) ChooseInstance(criteria *Criteria, instances model.ServiceInstances) (model.Instance, error) { // 第一次进行负载均衡,包括半开实例 - criteria.Cluster.IncludeHalfOpen = true - firstResult, firstErr := p.LoadBalancer.ChooseInstance(criteria, instances) - // 如果出错了,直接返回错误 - if firstErr != nil { - return firstResult, firstErr - } - // 熔断状态分配流量成功,返回结果 - cbStatus := firstResult.GetCircuitBreakerStatus() - if cbStatus == nil || cbStatus.Allocate() { - return firstResult, nil - } - - // 第一次因为熔断状态分配流量不成功,进行第二次负载均衡,这一次不包括半开实例 - criteria.Cluster.IncludeHalfOpen = false - secondResult, secondErr := p.LoadBalancer.ChooseInstance(criteria, instances) - // 如果没有出现错误,那么直接返回第二次的结果 - if secondErr == nil { - return secondResult, secondErr - } - // 否则,直接返回第一次的结果 - // 目前可能的情况是,所有实例都是半开,所以第二次负载均衡会返回实例权重为0的错误,第一次返回了一个半开实例; - // 在这种情况下,选择返回第一次的结果,即一个配额用完的半开实例 - return firstResult, nil + result, err := p.LoadBalancer.ChooseInstance(criteria, instances) + return result, err } // init 注册proxy diff --git a/pkg/plugin/localregistry/localregistry.go b/pkg/plugin/localregistry/localregistry.go index 045b6c95..840d06ae 100644 --- a/pkg/plugin/localregistry/localregistry.go +++ b/pkg/plugin/localregistry/localregistry.go @@ -39,6 +39,8 @@ const ( type InstanceProperties struct { Service *model.ServiceKey ID string + Host string + Port uint32 Properties map[string]interface{} } @@ -116,6 +118,10 @@ type RuleFilter struct { // RuleRegistry ConfigRegistry 配置缓存 type RuleRegistry interface { + // GetServiceRule 非阻塞获取配置信息 + GetServiceRule(key *model.ServiceEventKey, includeCache bool) model.ServiceRule + // LoadServiceRule 非阻塞发起配置加载 + LoadServiceRule(key *model.ServiceEventKey) (*common.Notifier, error) // GetServiceRouteRule 非阻塞获取配置信息 GetServiceRouteRule(key *model.ServiceKey, includeCache bool) model.ServiceRule // LoadServiceRouteRule 非阻塞发起配置加载 diff --git a/pkg/plugin/manage.go b/pkg/plugin/manage.go index 98cb13b8..1a17c3c9 100644 --- a/pkg/plugin/manage.go +++ b/pkg/plugin/manage.go @@ -66,6 +66,8 @@ func IsPluginRegistered(typ common.Type, name string) bool { type Supplier interface { // GetPlugin 获取插件实例 GetPlugin(typ common.Type, name string) (Plugin, error) + // GetPlugins 获取插件实例列表 + GetPlugins(typ common.Type) ([]Plugin, error) // GetPluginById 通过id获取插件实例 GetPluginById(id int32) (Plugin, error) // GetPluginsByType 获取一个类型的加载了的插件名字 @@ -320,6 +322,20 @@ func (m *manager) GetPlugin(typ common.Type, name string) (Plugin, error) { return plug.instance, nil } +// GetPlugins . +func (m *manager) GetPlugins(typ common.Type) ([]Plugin, error) { + plugins, exists := m.plugins[typ] + if !exists { + return nil, model.NewSDKError(model.ErrCodePluginError, nil, + "GetPlugin: invalid plugin type %v", typ) + } + ret := make([]Plugin, 0, len(plugins)) + for _, i := range plugins { + ret = append(ret, i.instance) + } + return ret, nil +} + // GetPluginsByType 获取一个类型的加载的插件名字 func (m *manager) GetPluginsByType(typ common.Type) []string { var res []string diff --git a/pkg/plugin/register/plugins.go b/pkg/plugin/register/plugins.go index 071c6bbc..cc969e37 100644 --- a/pkg/plugin/register/plugins.go +++ b/pkg/plugin/register/plugins.go @@ -30,9 +30,7 @@ import ( _ "github.com/polarismesh/polaris-go/pkg/plugin/serverconnector" _ "github.com/polarismesh/polaris-go/pkg/plugin/servicerouter" _ "github.com/polarismesh/polaris-go/pkg/plugin/weightadjuster" - _ "github.com/polarismesh/polaris-go/plugin/circuitbreaker/errorcheck" - _ "github.com/polarismesh/polaris-go/plugin/circuitbreaker/errorcount" - _ "github.com/polarismesh/polaris-go/plugin/circuitbreaker/errorrate" + _ "github.com/polarismesh/polaris-go/plugin/circuitbreaker/composite" _ "github.com/polarismesh/polaris-go/plugin/configconnector/polaris" _ "github.com/polarismesh/polaris-go/plugin/configfilter/crypto" _ "github.com/polarismesh/polaris-go/plugin/configfilter/crypto/aes" diff --git a/pkg/plugin/servicerouter/servicerouter.go b/pkg/plugin/servicerouter/servicerouter.go index b4f91c48..949f6fa4 100644 --- a/pkg/plugin/servicerouter/servicerouter.go +++ b/pkg/plugin/servicerouter/servicerouter.go @@ -27,6 +27,14 @@ import ( "github.com/polarismesh/polaris-go/pkg/plugin/common" ) +type FailOverType int32 + +const ( + _ FailOverType = iota + FailOverAll + FailOverNone +) + // RouteInfo 路由信息 type RouteInfo struct { // 源服务信息 @@ -46,6 +54,8 @@ type RouteInfo struct { // 对于全死全活插件,以及就近路由插件等,已经做了最小实例数检查,则可以设置该属性为true // 需要插件内部进行设置 ignoreFilterOnlyOnEndChain bool + // includeCircuitBreakInstances + IncludeCircuitBreakInstances bool // 可动态调整路由插件是否启用,不存在或者为true代表启用 // key为路由插件的id chainEnables map[int32]bool @@ -57,6 +67,8 @@ type RouteInfo struct { Canary string // 进行匹配的规则类型,如规则路由有入规则和出规则之分 MatchRuleType RuleType + // 规则路由失败降级类型 + FailOverType *FailOverType } // Init 初始化map diff --git a/plugin.cfg b/plugin.cfg index 0ca1f0c8..45026952 100644 --- a/plugin.cfg +++ b/plugin.cfg @@ -29,9 +29,7 @@ hash : loadbalancer/hash maglev : loadbalancer/maglev tcp : healthcheck/tcp http : healthcheck/http -errorRate : circuitbreaker/errorrate -errorCount : circuitbreaker/errorcount -errorCheck : circuitbreaker/errorcheck +composite : circuitbreaker/composite stat2file : statreporter/monitor serviceCache : statreporter/serviceinfo rateDelayAdjuster : weightadjuster/ratedelay diff --git a/plugin/circuitbreaker/common/half_open.go b/plugin/circuitbreaker/common/half_open.go deleted file mode 100644 index 54366a83..00000000 --- a/plugin/circuitbreaker/common/half_open.go +++ /dev/null @@ -1,214 +0,0 @@ -/** - * 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 common - -import ( - "math" - "time" - - "github.com/golang/protobuf/proto" - - "github.com/polarismesh/polaris-go/pkg/clock" - "github.com/polarismesh/polaris-go/pkg/config" - "github.com/polarismesh/polaris-go/pkg/log" - "github.com/polarismesh/polaris-go/pkg/metric" - "github.com/polarismesh/polaris-go/pkg/model" - "github.com/polarismesh/polaris-go/pkg/model/local" -) - -// 半开调用结果统计维度 -const ( - // KeyHalfOpenSuccessCount 半开成功数 - KeyHalfOpenSuccessCount int = iota - // KeyHalfOpenRequestCount 半开总请求数 - KeyHalfOpenRequestCount - // MaxHalfOpenDimension 半开错误统计维度总数 - MaxHalfOpenDimension -) - -// forceIsolate 是否强制隔离,当节点状态为不健康或者隔离时,则不会转换熔断半开状态 -func forceIsolate(instance model.Instance) bool { - return !instance.IsHealthy() || instance.IsIsolated() -} - -// HalfOpenConversionHandler 半开状态变更处理器 -type HalfOpenConversionHandler struct { - // 熔断器全局配置 - cbCfg config.CircuitBreakerConfig - // 健康检查判断器 - enableHealthCheck bool -} - -// NewHalfOpenConversionHandler 创建半开熔断器 -func NewHalfOpenConversionHandler(cfg config.Configuration) *HalfOpenConversionHandler { - handler := &HalfOpenConversionHandler{ - cbCfg: cfg.GetConsumer().GetCircuitBreaker(), - enableHealthCheck: cfg.GetConsumer().GetHealthCheck().GetWhen() != config.HealthCheckNever, - } - return handler -} - -// GetRecoverBucketInterval 获取恢复滑桶间隔 -func (h *HalfOpenConversionHandler) GetRecoverBucketInterval() time.Duration { - bucketSize := math.Ceil(float64(h.cbCfg.GetRecoverWindow()) / float64(h.cbCfg.GetRecoverNumBuckets())) - return time.Duration(bucketSize) -} - -// CreateHalfOpenMetricWindow 创建半开的统计窗口 -func (h *HalfOpenConversionHandler) CreateHalfOpenMetricWindow(name string) *metric.SliceWindow { - return metric.NewSliceWindow(name, h.cbCfg.GetRecoverNumBuckets(), - h.GetRecoverBucketInterval(), MaxHalfOpenDimension, clock.GetClock().Now().UnixNano()) -} - -// halfOpenByCheck 打开了探测,通过探测结果来判断半开 -func (h *HalfOpenConversionHandler) halfOpenByCheck(instance model.Instance, startTime time.Time) *bool { - if !h.enableHealthCheck { - return nil - } - var instanceLocalValue local.InstanceLocalValue - var ok bool - instanceLocalValue, ok = instance.(local.InstanceLocalValue) - if !ok { - return nil - } - odStatus := instanceLocalValue.GetActiveDetectStatus() - if odStatus != nil && odStatus.GetStatus() == model.Healthy && - odStatus.GetStartTime().After(startTime) { - return proto.Bool(true) - } - if odStatus != nil && odStatus.GetStatus() == model.Dead && - odStatus.GetStartTime().After(startTime) { - return proto.Bool(false) - } - return nil -} - -// OpenToHalfOpen 熔断器从打开到半开 -func (h *HalfOpenConversionHandler) OpenToHalfOpen(instance model.Instance, now time.Time, cbName string) bool { - cbStatus := instance.GetCircuitBreakerStatus() - if nil == cbStatus || cbStatus.GetCircuitBreaker() != cbName || cbStatus.GetStatus() != model.Open { - // 判断状态以及是否当前熔断器 - return false - } - if forceIsolate(instance) { - return false - } - // 增加探测结果恢复判断 - startTime := cbStatus.GetStartTime() - result := h.halfOpenByCheck(instance, startTime) - if nil != result { - return *result - } - return halfOpenByTimeout(now, startTime, h.cbCfg.GetSleepWindow()) -} - -// halfOpenByTimeout 打开了探测,通过探测结果来判断半开 -func halfOpenByTimeout(now time.Time, startTime time.Time, sleepWindow time.Duration) bool { - // 时间窗已经过去, 则恢复半开 - return now.Sub(startTime) >= sleepWindow -} - -// 半开状态日志打印间隔 -const halfOpenLogInterval = 30 * time.Second - -// GetRequestCountAfterHalfOpen 统计半开后的请求分配次数 -func GetRequestCountAfterHalfOpen(halfOpenWindow *metric.SliceWindow, timeRange *metric.TimeRange) int64 { - return halfOpenWindow.CalcMetrics(KeyHalfOpenRequestCount, timeRange) -} - -const ( - ToClose = iota - ToOpen - NoChange -) - -// halfOpenConversion 半开状态转换判断逻辑 -func (h *HalfOpenConversionHandler) halfOpenConversion(now time.Time, instance model.Instance, cbName string) int { - cbStatus := instance.GetCircuitBreakerStatus() - if nil == cbStatus || cbStatus.GetCircuitBreaker() != cbName || - cbStatus.GetStatus() != model.HalfOpen { - // 判断状态以及是否当前熔断器,以及是否已经分配完所有的探测配额 - return NoChange - } - if forceIsolate(instance) { - // 健康检查失败,直接重新熔断 - return ToOpen - } - if cbStatus.GetFailRequestsAfterHalfOpen() > 0 { - return ToOpen - } - - // 如果还有配额,那么保持状态不变 - // 这里不考虑有失败调用的情况,因为出现失败调用的时候,在调用stat接口的时候,该实例就已经转化为熔断了 - if cbStatus.IsAvailable() { - if now.Sub(cbStatus.GetStartTime()) >= halfOpenLogInterval { - // 半开太久的节点,需要打印日志 - log.GetDetectLogger().Infof("HalfOpenToOpen: instance(id=%s, host=%s, port=%d) halfOpen exceed %v, "+ - "startTime is %v, allocated reqCountAfterHalfOpen %d, reported reqCountAfterHalfOpen %d", - instance.GetId(), instance.GetHost(), instance.GetPort(), halfOpenLogInterval, - cbStatus.GetStartTime(), cbStatus.AllocatedRequestsAfterHalfOpen(), cbStatus.GetRequestsAfterHalfOpen()) - } - // 如果还有配额可以分配,那么保持状态 - return NoChange - } - - allocatedRequests, reportedRequests := cbStatus.AllocatedRequestsAfterHalfOpen(), cbStatus.GetRequestsAfterHalfOpen() - // 在已经无法分配配额的情况下,首先判断分配配额数和上报请求数的数量 - if allocatedRequests > reportedRequests { - finalAllocTime := cbStatus.GetFinalAllocateTimeInt64() - // 如果在最后的配额分配完了,过了半开周期还没有上报调用,那么认为这个调用失败了,进入熔断状态 - if finalAllocTime != 0 && now.Sub(time.Unix(0, finalAllocTime)) >= h.cbCfg.GetSleepWindow() { - log.GetDetectLogger().Infof("HalfOpenToOpen: instance(id=%s, host=%s, port=%d) quota not available exceed %v, "+ - "startTime is %v, allocated reqCountAfterHalfOpen %d, reported reqCountAfterHalfOpen %d", - instance.GetId(), instance.GetHost(), instance.GetPort(), h.cbCfg.GetSleepWindow(), - cbStatus.GetStartTime(), allocatedRequests, reportedRequests) - return ToOpen - } - // 如果分配配额数量没有达到请求数,那么不要改变状态 - return NoChange - } - - // 如果没有失败调用,并且分配请求和上报请求数相同,说明所有调用都成功了,转换为正常状态 - return ToClose -} - -// HalfOpenConversion 熔断器的半开状态转换 -func (h *HalfOpenConversionHandler) HalfOpenConversion(now time.Time, instance model.Instance, cbName string) int { - return h.halfOpenConversion(now, instance, cbName) -} - -// GetRequestCountAfterHalfOpen 获取半开后分配的请求数 -func (h *HalfOpenConversionHandler) GetRequestCountAfterHalfOpen() int { - return h.cbCfg.GetRequestCountAfterHalfOpen() -} - -// StatHalfOpenCalls 统计半开状态的调用量以及成功失败数 -// 当达到半开次数阈值时,返回true,代表立刻进行状态判断 -func (h *HalfOpenConversionHandler) StatHalfOpenCalls(cbStatus model.CircuitBreakerStatus, gauge model.InstanceGauge) bool { - reqCountAfterHalfOpen := cbStatus.AddRequestCountAfterHalfOpen(1, gauge.GetRetStatus() == model.RetSuccess) - // 如果调用失败,直接恢复成熔断状态 - if gauge.GetRetStatus() != model.RetSuccess && cbStatus.AcquireStatusLock() { - calledInstance := gauge.GetCalledInstance() - log.GetBaseLogger().Infof("instance(service=%s, namespace=%s, host=%s, port=%d, instanceId=%s) "+ - "stat trigger halfOpen maxReqCount limit for reqCount %v", - gauge.GetService(), gauge.GetNamespace(), calledInstance.GetHost(), calledInstance.GetPort(), - calledInstance.GetId(), reqCountAfterHalfOpen) - return true - } - return false -} diff --git a/plugin/circuitbreaker/composite/breaker.go b/plugin/circuitbreaker/composite/breaker.go new file mode 100644 index 00000000..cc6bbc05 --- /dev/null +++ b/plugin/circuitbreaker/composite/breaker.go @@ -0,0 +1,398 @@ +/** + * 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 ( + "context" + "sync" + "sync/atomic" + "time" + + regexp "github.com/dlclark/regexp2" + "github.com/polarismesh/specification/source/go/api/v1/fault_tolerance" + + "github.com/polarismesh/polaris-go/pkg/log" + "github.com/polarismesh/polaris-go/pkg/model" + "github.com/polarismesh/polaris-go/pkg/plugin" + "github.com/polarismesh/polaris-go/pkg/plugin/common" + "github.com/polarismesh/polaris-go/pkg/plugin/healthcheck" + "github.com/polarismesh/polaris-go/pkg/plugin/localregistry" +) + +const ( + defaultCheckPeriod = 60 * time.Second + defaultCheckPeriodMultiple = 20 +) + +type CompositeCircuitBreaker struct { + *plugin.PluginBase + // pluginCtx + pluginCtx *plugin.InitContext + // countersCache + countersCache map[fault_tolerance.Level]*CountersBucket + // healthCheckers . + healthCheckers map[fault_tolerance.FaultDetectRule_Protocol]healthcheck.HealthChecker + // healthCheckCache map[model.Resource]*ResourceHealthChecker + healthCheckCache *sync.Map + // serviceHealthCheckCache map[model.ServiceKey]map[model.Resource]*ResourceHealthChecker + serviceHealthCheckCache *sync.Map + // containers model.Resource -> *RuleContainer + containers *sync.Map + // engineFlow + engineFlow model.Engine + // regexpCache regexp -> *regexp.Regexp + rlock sync.RWMutex + // regexpCache + regexpCache map[string]*regexp.Regexp + // checkPeriod + checkPeriod time.Duration + // healthCheckInstanceExpireInterval + healthCheckInstanceExpireInterval time.Duration + // localCache + localCache localregistry.LocalRegistry + // log . + log log.Logger + // start + start int32 + // destroy . + destroy int32 + // cancel + cancel context.CancelFunc + // taskCtx + taskCtx context.Context + // executor + executor *TaskExecutor +} + +// Init 初始化插件 +func (c *CompositeCircuitBreaker) Init(ctx *plugin.InitContext) error { + c.PluginBase = plugin.NewPluginBase(ctx) + c.pluginCtx = ctx + // 监听规则 + callbackHandler := common.PluginEventHandler{ + Callback: c.OnEvent, + } + c.pluginCtx.Plugins.RegisterEventSubscriber(common.OnServiceAdded, callbackHandler) + c.pluginCtx.Plugins.RegisterEventSubscriber(common.OnServiceUpdated, callbackHandler) + c.pluginCtx.Plugins.RegisterEventSubscriber(common.OnServiceDeleted, callbackHandler) + return nil +} + +// Start 启动插件,对于需要依赖外部资源,以及启动协程的操作,在Start方法里面做 +func (c *CompositeCircuitBreaker) Start() error { + c.taskCtx, c.cancel = context.WithCancel(context.Background()) + c.countersCache = make(map[fault_tolerance.Level]*CountersBucket) + c.healthCheckers = make(map[fault_tolerance.FaultDetectRule_Protocol]healthcheck.HealthChecker) + c.healthCheckCache = &sync.Map{} + c.serviceHealthCheckCache = &sync.Map{} + c.containers = &sync.Map{} + c.regexpCache = make(map[string]*regexp.Regexp) + c.executor = newTaskExecutor(8) + c.checkPeriod = c.pluginCtx.Config.GetConsumer().GetCircuitBreaker().GetCheckPeriod() + if c.checkPeriod == 0 { + c.checkPeriod = defaultCheckPeriod + } + c.healthCheckInstanceExpireInterval = c.checkPeriod * defaultCheckPeriodMultiple + c.engineFlow = c.pluginCtx.ValueCtx.GetEngine() + c.start = 1 + + c.countersCache[fault_tolerance.Level_SERVICE] = newCountersBucket() + c.countersCache[fault_tolerance.Level_METHOD] = newCountersBucket() + c.countersCache[fault_tolerance.Level_INSTANCE] = newCountersBucket() + c.countersCache[fault_tolerance.Level_GROUP] = newCountersBucket() + + plugins, err := c.pluginCtx.Plugins.GetPlugins(common.TypeHealthCheck) + if err != nil { + return err + } + for i := range plugins { + item := plugins[i] + checker := item.(healthcheck.HealthChecker) + c.healthCheckers[checker.Protocol()] = checker + } + registryPlugin, err := c.pluginCtx.Plugins.GetPlugin(common.TypeLocalRegistry, c.pluginCtx.Config.GetConsumer().GetLocalCache().GetType()) + if err != nil { + return err + } + c.localCache = registryPlugin.(localregistry.LocalRegistry) + c.log = log.GetBaseLogger() + return nil +} + +// Destroy 销毁插件,可用于释放资源 +func (c *CompositeCircuitBreaker) Destroy() error { + if atomic.CompareAndSwapInt32(&c.destroy, 0, 1) { + return nil + } + c.cancel() + c.healthCheckCache.Range(func(key, value interface{}) bool { + checker, ok := value.(*ResourceHealthChecker) + if !ok { + return true + } + checker.stop() + return true + }) + return nil +} + +// CheckResource get the resource circuitbreaker status +func (c *CompositeCircuitBreaker) CheckResource(res model.Resource) model.CircuitBreakerStatus { + counters, exist := c.getResourceCounters(res) + if !exist { + return nil + } + return counters.CurrentCircuitBreakerStatus() +} + +// Report report resource invoke result stat +func (c *CompositeCircuitBreaker) Report(stat *model.ResourceStat) error { + return c.doReport(stat, true) +} + +func (c *CompositeCircuitBreaker) doReport(stat *model.ResourceStat, record bool) error { + resource := stat.Resource + if resource.GetLevel() == fault_tolerance.Level_UNKNOWN { + return nil + } + retStatus := stat.RetStatus + // 因为限流、熔断被拒绝的请求,不需要进入熔断数据上报 + if retStatus == model.RetReject || retStatus == model.RetFlowControl { + return nil + } + counters, exist := c.getResourceCounters(resource) + if !exist { + c.containers.LoadOrStore(resource.String(), newRuleContainer(c.taskCtx, resource, c)) + } else { + counters.Report(stat) + } + c.addInstanceForHealthCheck(resource, record) + return nil +} + +func (c *CompositeCircuitBreaker) addInstanceForHealthCheck(res model.Resource, record bool) { + insRes, ok := res.(*model.InstanceResource) + if !ok { + return + } + checkers, exist := c.loadServiceHealthCheck(*insRes.GetService()) + if !exist { + return + } + checkers.foreach(func(rhc *ResourceHealthChecker) { + rhc.addInstance(insRes, true) + }) +} + +func (c *CompositeCircuitBreaker) loadOrStoreCompiledRegex(s string) *regexp.Regexp { + c.rlock.Lock() + defer c.rlock.Unlock() + + if val, ok := c.regexpCache[s]; ok { + return val + } + + val := regexp.MustCompile(s, regexp.RE2) + c.regexpCache[s] = val + return val +} + +func (c *CompositeCircuitBreaker) getResourceCounters(res model.Resource) (*ResourceCounters, bool) { + levelCache := c.getLevelResourceCounters(res.GetLevel()) + return levelCache.get(res) +} + +func (c *CompositeCircuitBreaker) getLevelResourceCounters(level fault_tolerance.Level) *CountersBucket { + return c.countersCache[level] +} + +// Type 插件类型 +func (c *CompositeCircuitBreaker) Type() common.Type { + return common.TypeCircuitBreaker +} + +// Name 插件名,一个类型下插件名唯一 +func (c *CompositeCircuitBreaker) Name() string { + return "composite" +} + +func (c *CompositeCircuitBreaker) OnEvent(event *common.PluginEvent) error { + if c.isDestroyed() || c.start == 0 { + return nil + } + + var ( + eventObject *common.ServiceEventObject + ok bool + ) + if eventObject, ok = event.EventObject.(*common.ServiceEventObject); !ok { + return nil + } + if eventObject.SvcEventKey.Type != model.EventCircuitBreaker && eventObject.SvcEventKey.Type != model.EventFaultDetect { + return nil + } + c.doSchedule(eventObject.SvcEventKey) + return nil +} + +func (c *CompositeCircuitBreaker) doSchedule(expectKey model.ServiceEventKey) { + 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 { + switch expectKey.Type { + case model.EventCircuitBreaker: + ruleC.scheduleCircuitBreaker() + case model.EventFaultDetect: + ruleC.scheduleHealthCheck() + } + } + return true + }) +} + +func (c *CompositeCircuitBreaker) loadServiceHealthCheck(key model.ServiceKey) (*HealthCheckersBucket, bool) { + val, ok := c.serviceHealthCheckCache.Load(key) + if !ok { + return nil, false + } + return val.(*HealthCheckersBucket), true +} + +func (c *CompositeCircuitBreaker) loadOrStoreServiceHealthCheck(key model.ServiceKey) *HealthCheckersBucket { + c.serviceHealthCheckCache.LoadOrStore(key, newHealthCheckersBucket()) + val, _ := c.serviceHealthCheckCache.Load(key) + return val.(*HealthCheckersBucket) +} + +func (c *CompositeCircuitBreaker) delServiceHealthCheck(key model.ServiceKey) { + c.serviceHealthCheckCache.LoadAndDelete(key) +} + +func (c *CompositeCircuitBreaker) getResourceHealthChecker(res model.Resource) (*ResourceHealthChecker, bool) { + v, ok := c.healthCheckCache.Load(res) + if !ok { + return nil, false + } + return v.(*ResourceHealthChecker), true +} + +func (c *CompositeCircuitBreaker) delResourceHealthChecker(res model.Resource) (*ResourceHealthChecker, bool) { + v, ok := c.healthCheckCache.LoadAndDelete(res) + if !ok { + return nil, false + } + return v.(*ResourceHealthChecker), true +} + +func (c *CompositeCircuitBreaker) setResourceHealthChecker(res model.Resource, checker *ResourceHealthChecker) { + c.healthCheckCache.Store(res, checker) +} + +func (c *CompositeCircuitBreaker) isDestroyed() bool { + return atomic.LoadInt32(&c.destroy) == 1 +} + +// init 注册插件信息. +func init() { + plugin.RegisterConfigurablePlugin(&CompositeCircuitBreaker{}, &circuitbreakConfig{}) +} + +func newCountersBucket() *CountersBucket { + return &CountersBucket{m: make(map[string]*ResourceCounters)} +} + +type CountersBucket struct { + lock sync.RWMutex + m map[string]*ResourceCounters +} + +func (c *CountersBucket) get(key model.Resource) (*ResourceCounters, bool) { + c.lock.RLock() + defer c.lock.RUnlock() + + v, ok := c.m[key.String()] + return v, ok +} + +func (c *CountersBucket) put(key model.Resource, counter *ResourceCounters) { + c.lock.Lock() + defer c.lock.Unlock() + + c.m[key.String()] = counter +} + +func (c *CountersBucket) remove(key model.Resource) (*ResourceCounters, bool) { + c.lock.Lock() + defer c.lock.Unlock() + + v, ok := c.m[key.String()] + delete(c.m, key.String()) + return v, ok +} + +func newHealthCheckersBucket() *HealthCheckersBucket { + return &HealthCheckersBucket{m: make(map[string]*ResourceHealthChecker)} +} + +type HealthCheckersBucket struct { + lock sync.RWMutex + m map[string]*ResourceHealthChecker +} + +func (c *HealthCheckersBucket) get(key model.Resource) (*ResourceHealthChecker, bool) { + c.lock.RLock() + defer c.lock.RUnlock() + + v, ok := c.m[key.String()] + return v, ok +} + +func (c *HealthCheckersBucket) put(key model.Resource, counter *ResourceHealthChecker) { + c.lock.Lock() + defer c.lock.Unlock() + + c.m[key.String()] = counter +} + +func (c *HealthCheckersBucket) remove(key model.Resource) (*ResourceHealthChecker, bool) { + c.lock.Lock() + defer c.lock.Unlock() + + v, ok := c.m[key.String()] + delete(c.m, key.String()) + return v, ok +} + +func (c *HealthCheckersBucket) foreach(f func(*ResourceHealthChecker)) { + c.lock.RLock() + defer c.lock.RUnlock() + + for _, v := range c.m { + f(v) + } +} + +func (c *HealthCheckersBucket) isEmpty() bool { + c.lock.Lock() + defer c.lock.Unlock() + + return len(c.m) == 0 +} diff --git a/plugin/circuitbreaker/composite/checker.go b/plugin/circuitbreaker/composite/checker.go new file mode 100644 index 00000000..c8cf9edf --- /dev/null +++ b/plugin/circuitbreaker/composite/checker.go @@ -0,0 +1,308 @@ +/** + * 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 ( + "strings" + "sync" + "sync/atomic" + "time" + + regexp "github.com/dlclark/regexp2" + "github.com/polarismesh/specification/source/go/api/v1/fault_tolerance" + apimodel "github.com/polarismesh/specification/source/go/api/v1/model" + "github.com/polarismesh/specification/source/go/api/v1/service_manage" + "google.golang.org/protobuf/types/known/wrapperspb" + + "github.com/polarismesh/polaris-go/pkg/algorithm/match" + "github.com/polarismesh/polaris-go/pkg/clock" + "github.com/polarismesh/polaris-go/pkg/log" + "github.com/polarismesh/polaris-go/pkg/model" + pb "github.com/polarismesh/polaris-go/pkg/model/pb" + "github.com/polarismesh/polaris-go/pkg/plugin/healthcheck" +) + +const ( + defaultCheckInterval = 10 * time.Second +) + +type ResourceHealthChecker struct { + resource model.Resource + faultDetector *fault_tolerance.FaultDetector + stopped int32 + healthCheckers map[fault_tolerance.FaultDetectRule_Protocol]healthcheck.HealthChecker + circuitBreaker *CompositeCircuitBreaker + // regexFunction + regexFunction func(string) *regexp.Regexp + // lock + lock sync.RWMutex + // instances + instances map[string]*ProtocolInstance + // instanceExpireIntervalMill . + instanceExpireIntervalMill int64 + // executor + executor *TaskExecutor + // log + log log.Logger +} + +func NewResourceHealthChecker(res model.Resource, faultDetector *fault_tolerance.FaultDetector, + breaker *CompositeCircuitBreaker) *ResourceHealthChecker { + checker := &ResourceHealthChecker{ + resource: res, + faultDetector: faultDetector, + circuitBreaker: breaker, + regexFunction: func(s string) *regexp.Regexp { + return breaker.loadOrStoreCompiledRegex(s) + }, + healthCheckers: breaker.healthCheckers, + instances: make(map[string]*ProtocolInstance, 16), + } + if insRes, ok := res.(*model.InstanceResource); ok { + checker.addInstance(insRes, false) + } + checker.start() + return checker +} + +func (c *ResourceHealthChecker) start() { + protocol2Rules := c.selectFaultDetectRules(c.resource, c.faultDetector) + for protocol, rule := range protocol2Rules { + checkFunc := c.createCheckJob(protocol, rule) + interval := defaultCheckInterval + if rule.GetInterval() > 0 { + interval = time.Duration(rule.GetInterval()) * time.Second + } + c.log.Infof("[CircuitBreaker] schedule task: resource=%s, protocol=%s, interval=%+v, rule=%s", + c.resource.String(), protocol, interval, rule.GetName()) + c.executor.IntervalExecute(interval, checkFunc) + } + if c.resource.GetLevel() != fault_tolerance.Level_INSTANCE { + checkPeriod := c.circuitBreaker.checkPeriod + c.log.Infof("[CircuitBreaker] schedule expire task: resource=%s, interval=%+v", c.resource.String(), checkPeriod) + c.executor.IntervalExecute(checkPeriod, c.cleanInstances) + } +} + +func (c *ResourceHealthChecker) stop() { + c.log.Infof("[CircuitBreaker] health checker for resource=%s has stopped", c.resource.String()) + atomic.StoreInt32(&c.stopped, 1) +} + +func (c *ResourceHealthChecker) isStopped() bool { + return atomic.LoadInt32(&c.stopped) == 1 +} + +func (c *ResourceHealthChecker) cleanInstances() { + curTimeMill := clock.CurrentMillis() + expireIntervalMill := c.instanceExpireIntervalMill + + waitDel := make([]string, 0, 4) + func() { + c.lock.RLock() + defer c.lock.RUnlock() + + for k, v := range c.instances { + if v.isCheckSuccess() { + continue + } + lastReportMilli := v.getLastReportMilli() + if curTimeMill-lastReportMilli >= expireIntervalMill { + waitDel = append(waitDel, k) + c.log.Infof("[CircuitBreaker] clean instance from health check tasks, resource=%s, expired node=%s, lastReportMilli=%d", + c.resource.String(), k, lastReportMilli) + } + } + }() + + c.lock.Lock() + defer c.lock.Unlock() + for k := range waitDel { + delete(c.instances, waitDel[k]) + } +} + +func (c *ResourceHealthChecker) createCheckJob(protocol string, rule *fault_tolerance.FaultDetectRule) func() { + return func() { + if c.isStopped() { + return + } + name := fault_tolerance.FaultDetectRule_Protocol_value[protocol] + c.checkResource(fault_tolerance.FaultDetectRule_Protocol(name), rule) + } +} + +func (c *ResourceHealthChecker) checkResource(protocol fault_tolerance.FaultDetectRule_Protocol, rule *fault_tolerance.FaultDetectRule) { + port := rule.GetPort() + if port > 0 { + hosts := map[string]struct{}{} + c.lock.RLock() + defer c.lock.RUnlock() + for k, v := range c.instances { + if _, ok := hosts[k]; ok { + continue + } + hosts[k] = struct{}{} + ins := pb.NewInstanceInProto(&service_manage.Instance{ + Host: wrapperspb.String(v.insRes.GetNode().Host), + Port: wrapperspb.UInt32(v.insRes.GetNode().Port), + }, defaultServiceKey(v.insRes.GetService()), nil) + isSuccess := c.doCheck(ins, v.protocol, rule) + v.setCheckResult(isSuccess) + } + return + } + c.lock.RLock() + defer c.lock.RUnlock() + for _, v := range c.instances { + curProtocol := v.protocol + if !(curProtocol == fault_tolerance.FaultDetectRule_UNKNOWN || curProtocol == protocol) { + continue + } + ins := pb.NewInstanceInProto(&service_manage.Instance{ + Host: wrapperspb.String(v.insRes.GetNode().Host), + Port: wrapperspb.UInt32(v.insRes.GetNode().Port), + }, defaultServiceKey(v.insRes.GetService()), nil) + isSuccess := c.doCheck(ins, v.protocol, rule) + v.setCheckResult(isSuccess) + } +} + +func (c *ResourceHealthChecker) doCheck(ins model.Instance, protocol fault_tolerance.FaultDetectRule_Protocol, + rule *fault_tolerance.FaultDetectRule) bool { + checker, ok := c.healthCheckers[protocol] + if !ok { + c.log.Infof("plugin not found, skip health check for instance=%s:%d, resource=%s, protocol=%s", + ins.GetHost(), ins.GetPort(), c.resource.String(), protocol.String()) + return false + } + ret, err := checker.DetectInstance(ins) + if err != nil { + return false + } + stat := &model.ResourceStat{ + Resource: c.resource, + RetCode: ret.GetCode(), + Delay: ret.GetDelay(), + RetStatus: ret.GetRetStatus(), + } + if err := c.circuitBreaker.Report(stat); err != nil { + c.log.Errorf("[CircuitBreaker] report resource stat error, resource=%s, err=%s", c.resource.String(), err.Error()) + } + return stat.RetStatus == model.RetSuccess +} + +func (c *ResourceHealthChecker) addInstance(res *model.InstanceResource, record bool) { + c.lock.Lock() + defer c.lock.Unlock() + saveIns, ok := c.instances[res.GetNode().String()] + if !ok { + c.instances[res.GetNode().String()] = &ProtocolInstance{ + protocol: parseProtocol(res.GetProtocol()), + insRes: res, + lastReportMilli: clock.CurrentMillis(), + } + return + } + if record { + saveIns.doReport() + } +} + +func (c *ResourceHealthChecker) selectFaultDetectRules(res model.Resource, + faultDetector *fault_tolerance.FaultDetector) map[string]*fault_tolerance.FaultDetectRule { + sortedRules := sortFaultDetectRules(faultDetector.GetRules()) + matchRule := map[string]*fault_tolerance.FaultDetectRule{} + + for i := range sortedRules { + rule := sortedRules[i] + targetService := rule.GetTargetService() + if !match.MatchService(res.GetService(), targetService.Namespace, targetService.Service) { + continue + } + if res.GetLevel() == fault_tolerance.Level_METHOD { + if !matchMethod(res, targetService.GetMethod(), c.regexFunction) { + continue + } + } else { + if !match.IsMatchAll(targetService.GetMethod().GetValue().Value) { + continue + } + } + if _, ok := matchRule[rule.GetProtocol().String()]; !ok { + matchRule[rule.GetProtocol().String()] = rule + } + } + return matchRule +} + +func matchMethod(res model.Resource, val *apimodel.MatchString, regexFunc func(string) *regexp.Regexp) bool { + if res.GetLevel() != fault_tolerance.Level_METHOD { + return true + } + methodRes := res.(*model.MethodResource) + return match.MatchString(methodRes.Method, val, regexFunc) +} + +type ProtocolInstance struct { + protocol fault_tolerance.FaultDetectRule_Protocol + insRes *model.InstanceResource + lastReportMilli int64 + checkSuccess int32 +} + +func (p *ProtocolInstance) getLastReportMilli() int64 { + return atomic.LoadInt64(&p.lastReportMilli) +} + +func (p *ProtocolInstance) isCheckSuccess() bool { + return atomic.LoadInt32(&p.checkSuccess) == 1 +} + +func (p *ProtocolInstance) setCheckResult(v bool) { + if v { + atomic.StoreInt32(&p.checkSuccess, 1) + } else { + atomic.StoreInt32(&p.checkSuccess, 0) + } +} + +func (p *ProtocolInstance) doReport() { + atomic.StoreInt64(&p.lastReportMilli, clock.CurrentMillis()) +} + +func parseProtocol(s string) fault_tolerance.FaultDetectRule_Protocol { + s = strings.ToLower(s) + if s == "http" || strings.HasPrefix(s, "http/") || strings.HasSuffix(s, "/http") { + return fault_tolerance.FaultDetectRule_HTTP + } + if s == "udp" || strings.HasPrefix(s, "udp/") || strings.HasSuffix(s, "/udp") { + return fault_tolerance.FaultDetectRule_UDP + } + if s == "tcp" || strings.HasPrefix(s, "tcp/") || strings.HasSuffix(s, "/tcp") { + return fault_tolerance.FaultDetectRule_TCP + } + return fault_tolerance.FaultDetectRule_UNKNOWN +} + +func defaultServiceKey(v *model.ServiceKey) *model.ServiceKey { + if v == nil { + return &model.ServiceKey{} + } + return v +} diff --git a/pkg/flow/detect/object.go b/plugin/circuitbreaker/composite/config.go similarity index 57% rename from pkg/flow/detect/object.go rename to plugin/circuitbreaker/composite/config.go index c8bc0c00..c0e9d7cf 100644 --- a/pkg/flow/detect/object.go +++ b/plugin/circuitbreaker/composite/config.go @@ -15,28 +15,17 @@ * specific language governing permissions and limitations under the License. */ -package detect +package composite -import ( - "time" - - "github.com/polarismesh/polaris-go/pkg/model" -) - -// healthCheckingStatus 实现接口HealthCheckingStatus -type healthCheckingStatus struct { - // status 探测结果状态 - status model.HealthCheckStatus - // startTime 开始时间 - startTime time.Time +type circuitbreakConfig struct { } -// GetStatus 获取健康探测的状态结果 -func (od *healthCheckingStatus) GetStatus() model.HealthCheckStatus { - return od.status +// Verify 校验配置是否OK +func (c *circuitbreakConfig) Verify() error { + return nil } -// GetStartTime 获取健康探测的时间 -func (od *healthCheckingStatus) GetStartTime() time.Time { - return od.startTime +// SetDefault 对关键值设置默认值 +func (c *circuitbreakConfig) SetDefault() { + } diff --git a/plugin/circuitbreaker/composite/counter.go b/plugin/circuitbreaker/composite/counter.go new file mode 100644 index 00000000..7fa3df18 --- /dev/null +++ b/plugin/circuitbreaker/composite/counter.go @@ -0,0 +1,303 @@ +/** + * 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 ( + "strconv" + "sync" + "sync/atomic" + "time" + + regexp "github.com/dlclark/regexp2" + "github.com/polarismesh/specification/source/go/api/v1/fault_tolerance" + + "github.com/polarismesh/polaris-go/pkg/algorithm/match" + "github.com/polarismesh/polaris-go/pkg/log" + "github.com/polarismesh/polaris-go/pkg/model" + "github.com/polarismesh/polaris-go/pkg/plugin/localregistry" + "github.com/polarismesh/polaris-go/plugin/circuitbreaker/composite/trigger" +) + +const ( + _stateCloseToOpen = iota + _stateOpenToHalfOpen + _stateHalfOpenToOpen + _stateHalfOpenToClose +) + +// ResourceCounters . +type ResourceCounters struct { + circuitBreaker *CompositeCircuitBreaker + lock sync.RWMutex + // activeRuleRef + activeRule *fault_tolerance.CircuitBreakerRule + // counters + counters []trigger.TriggerCounter + // resource + resource model.Resource + // statusRef + statusRef atomic.Value + // fallbackInfo + fallbackInfo *model.FallbackInfo + // regexFunction + regexFunction func(string) *regexp.Regexp + // engineFlow + engineFlow model.Engine + // log + log log.Logger + // isInsRes + isInsRes bool + // + executor *TaskExecutor +} + +func newResourceCounters(res model.Resource, activeRule *fault_tolerance.CircuitBreakerRule, + circuitBreaker *CompositeCircuitBreaker) (*ResourceCounters, error) { + _, isInsRes := res.(*model.InstanceResource) + counters := &ResourceCounters{ + activeRule: activeRule, + resource: res, + regexFunction: func(s string) *regexp.Regexp { + if circuitBreaker == nil { + return regexp.MustCompile(s, regexp.RE2) + } + return circuitBreaker.loadOrStoreCompiledRegex(s) + }, + circuitBreaker: circuitBreaker, + statusRef: atomic.Value{}, + fallbackInfo: buildFallbackInfo(activeRule), + log: log.GetCircuitBreakerEventLogger(), + isInsRes: isInsRes, + executor: circuitBreaker.executor, + } + counters.updateCircuitBreakerStatus(model.NewCircuitBreakerStatus(activeRule.Name, model.Close, time.Now())) + if circuitBreaker != nil { + counters.engineFlow = circuitBreaker.engineFlow + } + if err := counters.init(); err != nil { + return nil, err + } + return counters, nil +} + +func (rc *ResourceCounters) init() error { + conditions := rc.activeRule.GetTriggerCondition() + for i := range conditions { + condition := conditions[i] + opt := trigger.Options{ + Resource: rc.resource, + Condition: condition, + StatusHandler: rc, + DelayExecutor: rc.executor.DelayExecute, + Log: rc.log, + } + + switch condition.GetTriggerType() { + case fault_tolerance.TriggerCondition_CONSECUTIVE_ERROR: + rc.counters = append(rc.counters, trigger.NewConsecutiveCounter(rc.activeRule.Name, &opt)) + case fault_tolerance.TriggerCondition_ERROR_RATE: + rc.counters = append(rc.counters, trigger.NewErrRateCounter(rc.activeRule.Name, &opt)) + } + } + return nil +} + +func (rc *ResourceCounters) CurrentActiveRule() *fault_tolerance.CircuitBreakerRule { + return rc.activeRule +} + +func (rc *ResourceCounters) updateCircuitBreakerStatus(status model.CircuitBreakerStatus) { + rc.statusRef.Store(status) +} + +func (rc *ResourceCounters) CurrentCircuitBreakerStatus() model.CircuitBreakerStatus { + val := rc.statusRef.Load() + if val == nil { + return nil + } + return val.(model.CircuitBreakerStatus) +} + +func (rc *ResourceCounters) CloseToOpen(breaker string) { + rc.lock.Lock() + defer rc.lock.Unlock() + + status := rc.CurrentCircuitBreakerStatus() + if status.GetStatus() == model.Close { + rc.toOpen(status, breaker) + } +} + +func (rc *ResourceCounters) toOpen(before model.CircuitBreakerStatus, name string) { + newStatus := model.NewCircuitBreakerStatus(name, model.Open, time.Now(), + func(cbs model.CircuitBreakerStatus) { + cbs.SetFallbackInfo(rc.fallbackInfo) + }) + rc.updateCircuitBreakerStatus(newStatus) + rc.reportCircuitStatus(newStatus) + rc.log.Infof("previous status %s, current status %s, resource %s, rule %s", before.GetStatus(), + newStatus.GetStatus(), rc.resource.String(), before.GetCircuitBreaker()) + sleepWindow := rc.activeRule.GetRecoverCondition().GetSleepWindow() + delay := time.Duration(sleepWindow) * time.Second + + rc.executor.AffinityDelayExecute(rc.activeRule.Id, delay, rc.OpenToHalfOpen) +} + +func (rc *ResourceCounters) OpenToHalfOpen() { + rc.lock.Lock() + defer rc.lock.Unlock() + + status := rc.CurrentCircuitBreakerStatus() + if status.GetStatus() != model.Open { + return + } + consecutiveSuccess := rc.activeRule.GetRecoverCondition().ConsecutiveSuccess + halfOpenStatus := model.NewHalfOpenStatus(status.GetCircuitBreaker(), time.Now(), int(consecutiveSuccess)) + rc.log.Infof("previous status %s, current status %s, resource %s, rule %s", status.GetStatus(), + halfOpenStatus.GetStatus(), rc.resource.String(), status.GetCircuitBreaker()) + rc.updateCircuitBreakerStatus(halfOpenStatus) + rc.reportCircuitStatus(halfOpenStatus) +} + +func (rc *ResourceCounters) HalfOpenToClose() { + rc.lock.Lock() + defer rc.lock.Unlock() + + status := rc.CurrentCircuitBreakerStatus() + if status.GetStatus() != model.HalfOpen { + return + } + newStatus := model.NewCircuitBreakerStatus(status.GetCircuitBreaker(), model.Close, time.Now()) + rc.updateCircuitBreakerStatus(newStatus) + rc.log.Infof("previous status %s, current status %s, resource %s, rule %s", status.GetStatus(), + newStatus.GetStatus(), rc.resource.String(), status.GetCircuitBreaker()) + rc.reportCircuitStatus(newStatus) +} + +func (rc *ResourceCounters) HalfOpenToOpen() { + rc.lock.Lock() + defer rc.lock.Unlock() + + status := rc.CurrentCircuitBreakerStatus() + if status.GetStatus() == model.HalfOpen { + rc.toOpen(status, status.GetCircuitBreaker()) + } +} + +func (rc *ResourceCounters) Report(stat *model.ResourceStat) { + retStatus := rc.parseRetStatus(stat) + isSuccess := retStatus != model.RetFail && retStatus != model.RetTimeout + curStatus := rc.CurrentCircuitBreakerStatus() + if curStatus != nil && curStatus.GetStatus() == model.HalfOpen { + halfOpenStatus := curStatus.(*model.HalfOpenStatus) + checked := halfOpenStatus.Report(isSuccess) + if !checked { + return + } + nextStatus := halfOpenStatus.CalNextStatus() + switch nextStatus { + case model.Close: + rc.executor.AffinityExecute(rc.activeRule.Id, rc.HalfOpenToClose) + case model.Open: + rc.executor.AffinityExecute(rc.activeRule.Id, rc.HalfOpenToOpen) + } + } else { + log.GetBaseLogger().Debugf("[CircuitBreaker] report resource stat to counter %s", stat.Resource.String()) + for _, counter := range rc.counters { + counter.Report(isSuccess) + } + } +} + +func (rc *ResourceCounters) parseRetStatus(stat *model.ResourceStat) model.RetStatus { + errConditions := rc.activeRule.GetErrorConditions() + if len(errConditions) == 0 { + return stat.RetStatus + } + for i := range errConditions { + errCondition := errConditions[i] + condition := errCondition.GetCondition() + switch errCondition.GetInputType() { + case fault_tolerance.ErrorCondition_RET_CODE: + codeMatched := match.MatchString(stat.RetCode, condition, rc.regexFunction) + if codeMatched { + return model.RetFail + } + case fault_tolerance.ErrorCondition_DELAY: + delayVal, err := strconv.ParseInt(condition.GetValue().GetValue(), 10, 64) + if err == nil { + if stat.Delay.Milliseconds() > delayVal { + return model.RetTimeout + } + } + } + } + return model.RetSuccess +} + +func (rc *ResourceCounters) reportCircuitStatus(newStatus model.CircuitBreakerStatus) { + if !rc.isInsRes { + return + } + insRes := rc.resource.(*model.InstanceResource) + // 构造请求,更新探测结果 + updateRequest := &localregistry.ServiceUpdateRequest{ + ServiceKey: *insRes.GetService(), + Properties: []localregistry.InstanceProperties{ + { + Host: insRes.GetNode().Host, + Port: insRes.GetNode().Port, + Service: insRes.GetService(), + Properties: map[string]interface{}{localregistry.PropertyCircuitBreakerStatus: newStatus}, + }, + }, + } + // 调用 localCache + if err := rc.circuitBreaker.localCache.UpdateInstances(updateRequest); err != nil { + log.GetBaseLogger().Errorf("update instance circuitbreaker status fail, resource %s, rule %s, err %s", + insRes.String(), rc.activeRule.Id, err.Error()) + } +} + +func buildFallbackInfo(rule *fault_tolerance.CircuitBreakerRule) *model.FallbackInfo { + if rule == nil { + return nil + } + if rule.GetLevel() != fault_tolerance.Level_METHOD && rule.GetLevel() != fault_tolerance.Level_SERVICE { + return nil + } + fallbackInfo := rule.GetFallbackConfig() + if fallbackInfo == nil { + return nil + } + if fallbackInfo.GetResponse() == nil { + return nil + } + ret := &model.FallbackInfo{ + Code: int(fallbackInfo.GetResponse().GetCode()), + Body: fallbackInfo.GetResponse().GetBody(), + Headers: map[string]string{}, + } + + headers := fallbackInfo.GetResponse().GetHeaders() + for i := range headers { + header := headers[i] + ret.Headers[header.Key] = header.Value + } + return ret +} diff --git a/plugin/circuitbreaker/composite/rule.go b/plugin/circuitbreaker/composite/rule.go new file mode 100644 index 00000000..1c0b0abf --- /dev/null +++ b/plugin/circuitbreaker/composite/rule.go @@ -0,0 +1,298 @@ +/** + * 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 ( + "context" + "sort" + "strings" + "time" + + regexp "github.com/dlclark/regexp2" + "github.com/polarismesh/specification/source/go/api/v1/fault_tolerance" + + "github.com/polarismesh/polaris-go/pkg/algorithm/match" + "github.com/polarismesh/polaris-go/pkg/log" + "github.com/polarismesh/polaris-go/pkg/model" +) + +const ( + _triggerCircuitBreaker = 1 + _triggerFaultDetect = 0 +) + +// RuleContainer +type RuleContainer struct { + // res + res model.Resource + // breaker + breaker *CompositeCircuitBreaker + // regexFunction + regexFunction func(string) *regexp.Regexp + // engineFlow + engineFlow model.Engine + // log + log log.Logger + // + executor *TaskExecutor +} + +func newRuleContainer(ctx context.Context, res model.Resource, breaker *CompositeCircuitBreaker) *RuleContainer { + c := &RuleContainer{ + res: res, + breaker: breaker, + regexFunction: func(s string) *regexp.Regexp { + return breaker.loadOrStoreCompiledRegex(s) + }, + engineFlow: breaker.engineFlow, + log: breaker.log, + executor: breaker.executor, + } + c.scheduleCircuitBreaker() + return c +} + +func (c *RuleContainer) scheduleCircuitBreaker() { + c.executor.AffinityExecute(c.res.String(), c.realRefreshCircuitBreaker) +} + +func (c *RuleContainer) scheduleHealthCheck() { + c.executor.AffinityExecute(c.res.String(), c.realRefreshHealthCheck) +} + +func (c *RuleContainer) realRefreshCircuitBreaker() { + engineFlow := c.engineFlow + resp, err := engineFlow.SyncGetServiceRule(model.EventCircuitBreaker, &model.GetServiceRuleRequest{ + Namespace: c.res.GetService().Namespace, + Service: c.res.GetService().Service, + }) + if err != nil { + c.log.Errorf("[CircuitBreaker] get %s rule fail: %+v", c.res.GetService().String(), err) + return + } + resourceCounters := c.breaker.getLevelResourceCounters(c.res.GetLevel()) + cbRule := selectCircuitBreakerRule(c.res, resp, c.regexFunction) + if cbRule == nil { + if _, exist := resourceCounters.remove(c.res); exist { + c.scheduleHealthCheck() + } + return + } + counters, exist := resourceCounters.get(c.res) + if exist { + activeRule := counters.CurrentActiveRule() + if activeRule.Id == cbRule.Id && activeRule.Revision == cbRule.Revision { + return + } + } + counters, err = newResourceCounters(c.res, cbRule, c.breaker) + if err != nil { + c.log.Errorf("[CircuitBreaker] new resource counters fail: %+v", err) + return + } + resourceCounters.put(c.res, counters) + c.scheduleHealthCheck() +} + +func (c *RuleContainer) realRefreshHealthCheck() { + c.log.Infof("[FaultDetect] start to pull fault detect rule for resource=%s", c.res.String()) + counters, exist := c.breaker.getLevelResourceCounters(c.res.GetLevel()).get(c.res) + faultDetectEnabled := false + var currentActiveRule *fault_tolerance.CircuitBreakerRule + if exist { + currentActiveRule = counters.CurrentActiveRule() + } + if currentActiveRule != nil && currentActiveRule.Enable && currentActiveRule.GetFallbackConfig().Enable { + engineFlow := c.engineFlow + resp, err := engineFlow.SyncGetServiceRule(model.EventFaultDetect, &model.GetServiceRuleRequest{ + Namespace: c.res.GetService().Namespace, + Service: c.res.GetService().Service, + }) + if err != nil { + c.log.Errorf("[FaultDetect] get %s rule fail: %+v", c.res.GetService().String(), err) + c.executor.AffinityDelayExecute(c.res.String(), 5*time.Second, c.realRefreshHealthCheck) + return + } + if faultDetector := selectFaultDetector(c.res, resp, c.regexFunction); faultDetector != nil { + if curChecker, ok := c.breaker.getResourceHealthChecker(c.res); ok { + curRule := curChecker.faultDetector + if curRule.Revision == faultDetector.Revision { + return + } + curChecker.stop() + } + checker := NewResourceHealthChecker(c.res, faultDetector, c.breaker) + c.breaker.setResourceHealthChecker(c.res, checker) + if c.res.GetLevel() != fault_tolerance.Level_INSTANCE { + svcKey := c.res.GetService() + resourceHealthCheckerMap := c.breaker.loadOrStoreServiceHealthCheck(*svcKey) + resourceHealthCheckerMap.put(c.res, checker) + } + faultDetectEnabled = true + } + } + if !faultDetectEnabled { + c.log.Infof("[FaultDetect] health check for resource=%s is disabled, now stop the previous checker", c.res.String()) + if checker, ok := c.breaker.delResourceHealthChecker(c.res); ok { + checker.stop() + } + if c.res.GetLevel() != fault_tolerance.Level_INSTANCE { + svcKey := c.res.GetService() + resourceHealthCheckerMap := c.breaker.loadOrStoreServiceHealthCheck(*svcKey) + resourceHealthCheckerMap.remove(c.res) + if resourceHealthCheckerMap.isEmpty() { + c.breaker.delServiceHealthCheck(*svcKey) + } + } + } +} + +func selectCircuitBreakerRule(res model.Resource, object *model.ServiceRuleResponse, regexFunc func(string) *regexp.Regexp) *fault_tolerance.CircuitBreakerRule { + if object == nil { + return nil + } + if object.Value == nil { + return nil + } + circuitBreaker := object.Value.(*fault_tolerance.CircuitBreaker) + rules := circuitBreaker.Rules + if len(rules) == 0 { + return nil + } + sortedRules := sortCircuitBreakerRules(rules) + for i := range sortedRules { + cbRule := sortedRules[i] + if !cbRule.Enable { + continue + } + if cbRule.Level != res.GetLevel() { + continue + } + ruleMatcher := cbRule.RuleMatcher + destination := ruleMatcher.Destination + if !match.MatchService(res.GetService(), destination.Namespace, destination.Service) { + continue + } + source := ruleMatcher.Source + if !match.MatchService(res.GetCallerService(), source.Namespace, source.Service) { + continue + } + if ok := matchMethod(res, destination.GetMethod(), regexFunc); !ok { + continue + } + return cbRule + } + return nil +} + +func sortCircuitBreakerRules(rules []*fault_tolerance.CircuitBreakerRule) []*fault_tolerance.CircuitBreakerRule { + ret := make([]*fault_tolerance.CircuitBreakerRule, 0, len(rules)) + ret = append(ret, rules...) + sort.Slice(ret, func(i, j int) bool { + rule1 := ret[i] + rule2 := ret[j] + + // 1. compare destination service + destNamespace1 := rule1.RuleMatcher.Destination.Namespace + destService1 := rule1.RuleMatcher.Destination.Service + destMethod1 := rule1.RuleMatcher.Destination.Method.Value.Value + + destNamespace2 := rule2.RuleMatcher.Destination.Namespace + destService2 := rule2.RuleMatcher.Destination.Service + destMethod2 := rule2.RuleMatcher.Destination.Method.Value.Value + + svcResult := compareService(destNamespace1, destService1, destNamespace2, destService2) + if svcResult != 0 { + return svcResult < 0 + } + if rule1.Level == rule2.Level { + if rule1.Level == fault_tolerance.Level_METHOD { + methodResult := compareStringValue(destMethod1, destMethod2) + if methodResult != 0 { + return methodResult < 0 + } + } + } + + // 2. compare source service + srcNamespace1 := rule1.RuleMatcher.Source.Namespace + srcService1 := rule1.RuleMatcher.Source.Service + srcNamespace2 := rule2.RuleMatcher.Source.Namespace + srcService2 := rule2.RuleMatcher.Source.Service + return compareService(srcNamespace1, srcService1, srcNamespace2, srcService2) < 0 + }) + return ret +} + +func selectFaultDetector(res model.Resource, object *model.ServiceRuleResponse, regexFunc func(string) *regexp.Regexp) *fault_tolerance.FaultDetector { + if object == nil { + return nil + } + if object.Value == nil { + return nil + } + return object.Value.(*fault_tolerance.FaultDetector) +} + +func sortFaultDetectRules(srcRules []*fault_tolerance.FaultDetectRule) []*fault_tolerance.FaultDetectRule { + rules := make([]*fault_tolerance.FaultDetectRule, 0, len(srcRules)) + copy(rules, srcRules) + sort.Slice(rules, func(i, j int) bool { + rule1 := rules[i] + rule2 := rules[j] + + targetSvc1 := rule1.GetTargetService() + destNamespace1 := targetSvc1.GetNamespace() + destService1 := targetSvc1.GetService() + destMethod1 := targetSvc1.GetMethod().GetValue().GetValue() + + targetSvc2 := rule2.GetTargetService() + destNamespace2 := targetSvc2.GetNamespace() + destService2 := targetSvc2.GetService() + destMethod2 := targetSvc2.GetMethod().GetValue().GetValue() + + if v := compareService(destNamespace1, destService1, destNamespace2, destService2); v != 0 { + return v < 0 + } + return compareStringValue(destMethod1, destMethod2) < 0 + }) + return rules +} + +func compareService(ns1, svc1, ns2, svc2 string) int { + if v := compareStringValue(ns1, ns2); v != 0 { + return v + } + return compareStringValue(svc1, svc2) +} + +func compareStringValue(v1, v2 string) int { + isMatchAllV1 := match.IsMatchAll(v1) + isMatchAllV2 := match.IsMatchAll(v2) + + if isMatchAllV1 && isMatchAllV2 { + return 0 + } + if isMatchAllV1 { + return 1 + } + if isMatchAllV2 { + return -1 + } + return strings.Compare(v1, v2) +} diff --git a/plugin/circuitbreaker/composite/task_pool.go b/plugin/circuitbreaker/composite/task_pool.go new file mode 100644 index 00000000..4dd83a36 --- /dev/null +++ b/plugin/circuitbreaker/composite/task_pool.go @@ -0,0 +1,225 @@ +/** + * 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 ( + "context" + "hash/fnv" + "math/rand" + "sync" + "sync/atomic" + "time" +) + +func newTaskExecutor(size int) *TaskExecutor { + workers := make([]*worker, 0, size) + for i := 0; i < size; i++ { + workers = append(workers, &worker{ + close: 0, + queue: make(chan func(), 128), + delayQueue: sync.Map{}, + }) + } + + ctx, cancel := context.WithCancel(context.Background()) + + e := &TaskExecutor{ + cancel: cancel, + workers: workers, + r: rand.New(rand.NewSource(time.Now().Unix())), + } + + for i := range workers { + workers[i].mainLoop(ctx) + } + return e +} + +type TaskExecutor struct { + cancel context.CancelFunc + workers []*worker + r *rand.Rand +} + +func (e *TaskExecutor) Stop() { + e.cancel() +} + +func (e *TaskExecutor) Execute(f func()) { + e.workers[e.randIndex()].add(f) +} + +func (e *TaskExecutor) IntervalExecute(interval time.Duration, f func()) { + e.workers[e.randIndex()].addDelay(interval, f, true) +} + +func (e *TaskExecutor) DelayExecute(delay time.Duration, f func()) { + e.workers[e.randIndex()].addDelay(delay, f, false) +} + +func (e *TaskExecutor) AffinityExecute(key string, f func()) { + h := fnv.New64a() + h.Write([]byte(key)) + ret := h.Sum64() + + index := int(ret % uint64(len(e.workers)-1)) + e.workers[index].add(f) +} + +func (e *TaskExecutor) AffinityDelayExecute(key string, delay time.Duration, f func()) { + h := fnv.New64a() + h.Write([]byte(key)) + ret := h.Sum64() + + index := int(ret % uint64(len(e.workers)-1)) + e.workers[index].addDelay(delay, f, false) +} + +func (e *TaskExecutor) randIndex() int32 { + return e.r.Int31n(int32(len(e.workers))) +} + +type worker struct { + lock sync.RWMutex + close int8 + queue chan func() + id int64 + delayQueue sync.Map +} + +func (w *worker) add(f func()) { + w.lock.RLock() + defer w.lock.RUnlock() + + if w.close == 1 { + return + } + + w.queue <- func() { + defer func() { + if err := recover(); err != nil { + panic(err) + } + }() + f() + } +} + +func (w *worker) addDelay(delay time.Duration, f func(), isInterval bool) { + w.lock.RLock() + defer w.lock.RUnlock() + + if w.close == 1 { + return + } + + wf := func() { + defer func() { + if err := recover(); err != nil { + // do nothing + } + }() + f() + } + + id := atomic.AddInt64(&w.id, 1) + if isInterval { + w.delayQueue.Store(id, &tickTask{ + ticker: time.NewTicker(delay), + f: wf, + }) + } else { + w.delayQueue.Store(id, &delayTask{ + timer: time.NewTimer(delay), + f: wf, + }) + } +} + +func (w *worker) mainLoop(ctx context.Context) { + go func() { + for { + select { + case <-ctx.Done(): + w.lock.Lock() + w.close = 1 + close(w.queue) + w.lock.Unlock() + return + case f := <-w.queue: + f() + } + } + }() + go func() { + ticker := time.NewTicker(100 * time.Millisecond) + for { + select { + case <-ctx.Done(): + ticker.Stop() + w.delayQueue.Range(func(key, value interface{}) bool { + switch t := value.(type) { + case *delayTask: + t.timer.Stop() + case *tickTask: + t.ticker.Stop() + } + return true + }) + return + case <-ticker.C: + waitDel := make([]interface{}, 0, 8) + w.delayQueue.Range(func(key, value interface{}) bool { + switch t := value.(type) { + case *delayTask: + select { + case <-t.timer.C: + t.timer.Stop() + t.f() + waitDel = append(waitDel, key) + default: + } + case *tickTask: + select { + case <-t.ticker.C: + t.ticker.Stop() + t.f() + waitDel = append(waitDel, key) + default: + } + } + return true + }) + + for i := range waitDel { + w.delayQueue.Delete(waitDel[i]) + } + } + } + }() +} + +type delayTask struct { + timer *time.Timer + f func() +} + +type tickTask struct { + ticker *time.Ticker + f func() +} diff --git a/plugin/circuitbreaker/composite/trigger/consecutive.go b/plugin/circuitbreaker/composite/trigger/consecutive.go new file mode 100644 index 00000000..9225763d --- /dev/null +++ b/plugin/circuitbreaker/composite/trigger/consecutive.go @@ -0,0 +1,57 @@ +/** + * 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 trigger + +import ( + "sync/atomic" +) + +type ConsecutiveCounter struct { + *baseCounter + maxCount int64 + consecutiveErrors int32 +} + +func NewConsecutiveCounter(name string, opt *Options) *ConsecutiveCounter { + c := &ConsecutiveCounter{ + baseCounter: newBaseCounter(name, opt), + } + c.init() + return c +} + +func (c *ConsecutiveCounter) init() { + c.log.Infof("[CircuitBreaker][Counter] consecutiveCounter(%s) initialized, resource(%s)", c.ruleName, c.res.String()) + c.maxCount = int64(c.triggerCondition.GetErrorCount()) +} + +func (c *ConsecutiveCounter) Report(success bool) { + if c.isSuspend() { + return + } + if !success { + currentSum := atomic.AddInt32(&c.consecutiveErrors, 1) + if currentSum == int32(c.maxCount) { + c.suspend() + atomic.StoreInt32(&c.consecutiveErrors, 0) + c.handler.CloseToOpen(c.ruleName) + } + } else { + atomic.StoreInt32(&c.consecutiveErrors, 0) + } +} diff --git a/plugin/circuitbreaker/composite/trigger/counter.go b/plugin/circuitbreaker/composite/trigger/counter.go new file mode 100644 index 00000000..ccab4d93 --- /dev/null +++ b/plugin/circuitbreaker/composite/trigger/counter.go @@ -0,0 +1,89 @@ +/** + * 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 trigger + +import ( + "sync/atomic" + "time" + + "github.com/polarismesh/specification/source/go/api/v1/fault_tolerance" + + "github.com/polarismesh/polaris-go/pkg/log" + "github.com/polarismesh/polaris-go/pkg/model" +) + +// StatusChangeHandler +type StatusChangeHandler interface { + // CloseToOpen + CloseToOpen(breaker string) + // OpenToHalfOpen + OpenToHalfOpen() + // HalfOpenToClose + HalfOpenToClose() + // HalfOpenToOpen + HalfOpenToOpen() +} + +// Options +type Options struct { + Resource model.Resource + Condition *fault_tolerance.TriggerCondition + StatusHandler StatusChangeHandler + Log log.Logger + DelayExecutor func(delay time.Duration, f func()) +} + +// TriggerCounter . +type TriggerCounter interface { + // Report . + Report(success bool) +} + +func newBaseCounter(rule string, opt *Options) *baseCounter { + return &baseCounter{ + ruleName: rule, + triggerCondition: opt.Condition, + res: opt.Resource, + handler: opt.StatusHandler, + suspended: 0, + log: opt.Log, + delayExecutor: opt.DelayExecutor, + } +} + +type baseCounter struct { + ruleName string + triggerCondition *fault_tolerance.TriggerCondition + res model.Resource + handler StatusChangeHandler + suspended int32 + log log.Logger + delayExecutor func(delay time.Duration, f func()) +} + +func (bc *baseCounter) isSuspend() bool { + return atomic.LoadInt32(&bc.suspended) == 1 +} + +func (bc *baseCounter) suspend() { + atomic.StoreInt32(&bc.suspended, 1) +} + +func (bc *baseCounter) resume() { + atomic.StoreInt32(&bc.suspended, 0) +} diff --git a/plugin/circuitbreaker/composite/trigger/err_rate.go b/plugin/circuitbreaker/composite/trigger/err_rate.go new file mode 100644 index 00000000..b1257fec --- /dev/null +++ b/plugin/circuitbreaker/composite/trigger/err_rate.go @@ -0,0 +1,135 @@ +/** + * 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 trigger + +import ( + "math" + "sync/atomic" + "time" + + "github.com/polarismesh/polaris-go/pkg/clock" + "github.com/polarismesh/polaris-go/pkg/metric" + "github.com/polarismesh/polaris-go/pkg/model" +) + +const ( + bucketCount = 10 +) + +const ( + // 错误率统计窗口下标 + metricIdxErrRate = iota + // 半开统计窗口下标 + metricIdxHalfOpen + // 最大窗口下标 + metricIdxMax +) + +// 统计维度 +const ( + // 总请求数 + keyRequestCount = iota + // 错误数 + keyFailCount + // 总统计维度 + maxDimension +) + +type ErrRateCounter struct { + *baseCounter + sliceWindow *metric.SliceWindow + metricWindow time.Duration + minimumRequest int32 + errorPercent int + scheduled int32 +} + +func NewErrRateCounter(name string, opt *Options) *ErrRateCounter { + c := &ErrRateCounter{ + baseCounter: newBaseCounter(name, opt), + } + c.init() + return c +} + +func (c *ErrRateCounter) init() { + c.log.Infof("[CircuitBreaker][Counter] errRateCounter(%s) initialized, 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()) +} + +func (c *ErrRateCounter) Report(success bool) { + if c.isSuspend() { + c.log.Debugf("[CircuitBreaker][Counter] errRateCounter(%s) suspended, skip report", c.ruleName) + return + } + c.log.Debugf("[CircuitBreaker][Counter] errRateCounter(%s): add requestCount 1, success(%+v)", c.ruleName, success) + + retStatus := model.RetSuccess + if !success { + retStatus = model.RetFail + } + + c.sliceWindow.AddGauge(&model.ServiceCallResult{ + RetStatus: retStatus, + }, func(gauge model.InstanceGauge, bucket *metric.Bucket) int64 { + ret := gauge.GetRetStatus() + if ret == model.RetFail { + bucket.AddMetric(keyFailCount, 1) + } + bucket.AddMetric(keyRequestCount, 1) + 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.delayExecutor(c.metricWindow, func() { + currentTime := time.Now() + timeRange := &metric.TimeRange{ + Start: currentTime.Add(-1 * c.metricWindow), + End: currentTime, + } + 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) { + atomic.StoreInt32(&c.scheduled, 0) + 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) + } + atomic.StoreInt32(&c.scheduled, 0) + }) + } +} + +func getBucketInterval(interval time.Duration) time.Duration { + bucketSize := math.Ceil(float64(interval) / float64(bucketCount)) + return time.Duration(bucketSize) +} + +// ToErrorRateThreshold 转换成熔断错误率阈值 +func ToErrorRateThreshold(errorRatePercent int) float64 { + return float64(errorRatePercent) / 100 +} diff --git a/plugin/circuitbreaker/errorcheck/err_check.go b/plugin/circuitbreaker/errorcheck/err_check.go deleted file mode 100644 index f0004280..00000000 --- a/plugin/circuitbreaker/errorcheck/err_check.go +++ /dev/null @@ -1,143 +0,0 @@ -/** - * 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 errorcheck - -import ( - "time" - - "github.com/polarismesh/polaris-go/pkg/clock" - "github.com/polarismesh/polaris-go/pkg/config" - "github.com/polarismesh/polaris-go/pkg/log" - "github.com/polarismesh/polaris-go/pkg/model" - "github.com/polarismesh/polaris-go/pkg/model/local" - "github.com/polarismesh/polaris-go/pkg/plugin" - "github.com/polarismesh/polaris-go/pkg/plugin/circuitbreaker" - common2 "github.com/polarismesh/polaris-go/pkg/plugin/common" - "github.com/polarismesh/polaris-go/plugin/circuitbreaker/common" -) - -// CircuitBreaker 通过定时探测进行熔断的熔断器 -type CircuitBreaker struct { - *plugin.PluginBase - healthCheckCfg config.HealthCheckConfig - halfOpenHandler *common.HalfOpenConversionHandler -} - -// Type 插件类型 -func (g *CircuitBreaker) Type() common2.Type { - return common2.TypeCircuitBreaker -} - -// Name 插件名,一个类型下插件名唯一 -func (g *CircuitBreaker) Name() string { - return config.DefaultCircuitBreakerErrCheck -} - -// Init 初始化插件 -func (g *CircuitBreaker) Init(ctx *plugin.InitContext) error { - g.PluginBase = plugin.NewPluginBase(ctx) - g.healthCheckCfg = ctx.Config.GetConsumer().GetHealthCheck() - g.halfOpenHandler = common.NewHalfOpenConversionHandler(ctx.Config) - return nil -} - -// Destroy 销毁插件,可用于释放资源 -func (g *CircuitBreaker) Destroy() error { - return nil -} - -// IsEnable 插件是否启用 -func (g *CircuitBreaker) IsEnable(cfg config.Configuration) bool { - return true -} - -// Stat 进行调用统计,返回当前实例是否需要进行立即熔断 -func (g *CircuitBreaker) Stat(model.InstanceGauge) (bool, error) { - return false, nil -} - -// CircuitBreak 进行熔断计算,返回需要进行状态转换的实例ID -// 入参包括全量服务实例,以及当前周期的健康探测结果 -func (g *CircuitBreaker) CircuitBreak(instances []model.Instance) (*circuitbreaker.Result, error) { - if g.healthCheckCfg.GetWhen() != config.HealthCheckAlways { - return nil, nil - } - result := circuitbreaker.NewCircuitBreakerResult(clock.GetClock().Now()) - for _, instance := range instances { - log.GetBaseLogger().Tracef("invoke %s circuitBreaker for instance %s", g.Name(), instance.GetId()) - if g.closeToOpen(instance, result.Now) { - log.GetBaseLogger().Warnf("ErrCheck circuitbreaker: close to open, instance:"+ - " Id: %s, Namespace: %s, Service: %s, Host: %s, Port: %v\n", - instance.GetId(), instance.GetNamespace(), instance.GetService(), instance.GetHost(), instance.GetPort()) - result.InstancesToOpen.Add(instance.GetId()) - continue - } - if g.halfOpenHandler.OpenToHalfOpen(instance, result.Now, g.Name()) { - log.GetBaseLogger().Infof("ErrCheck circuitbreaker: open to halfopen, instance:"+ - " Id: %s, Namespace: %s, Service: %s, Host: %s, Port: %v\n", - instance.GetId(), instance.GetNamespace(), instance.GetService(), instance.GetHost(), instance.GetPort()) - result.InstancesToHalfOpen.Add(instance.GetId()) - continue - } - - halfOpenChange := g.halfOpenHandler.HalfOpenConversion(result.Now, instance, g.Name()) - switch halfOpenChange { - case common.ToOpen: - log.GetBaseLogger().Warnf("ErrCheck circuitbreaker: halfopen to open, instance:"+ - " Id: %s, Namespace: %s, Service: %s, Host: %s, Port: %v\n", - instance.GetId(), instance.GetNamespace(), instance.GetService(), instance.GetHost(), instance.GetPort()) - result.InstancesToOpen.Add(instance.GetId()) - case common.ToClose: - log.GetBaseLogger().Infof("ErrCheck circuitbreaker: halfopen to close, instance:"+ - " Id: %s, Namespace: %s, Service: %s, Host: %s, Port: %v\n", - instance.GetId(), instance.GetNamespace(), instance.GetService(), instance.GetHost(), instance.GetPort()) - result.InstancesToClose.Add(instance.GetId()) - } - } - if result.IsEmpty() { - return nil, nil - } - result.RequestCountAfterHalfOpen = g.halfOpenHandler.GetRequestCountAfterHalfOpen() - return result, nil -} - -// closeToOpen 打开了探测,通过探测结果来判断熔断器开启 -func (g *CircuitBreaker) closeToOpen(instance model.Instance, now time.Time) bool { - cbStatus := instance.GetCircuitBreakerStatus() - if nil != cbStatus && cbStatus.GetStatus() != model.Close { - return false - } - var instanceLocalValue local.InstanceLocalValue - var ok bool - instanceLocalValue, ok = instance.(local.InstanceLocalValue) - if !ok { - return false - } - odStatus := instanceLocalValue.GetActiveDetectStatus() - // log.GetBaseLogger().Infof("health check status is %v, name %s, instance %s:%d", odStatus, g.Name(), instance.GetHost(), instance.GetPort()) - if odStatus != nil && odStatus.GetStatus() == model.Dead && - now.Sub(odStatus.GetStartTime()) <= g.healthCheckCfg.GetInterval() { - return true - } - return false -} - -// init 插件注册 -func init() { - plugin.RegisterConfigurablePlugin(&CircuitBreaker{}, nil) -} diff --git a/plugin/circuitbreaker/errorcount/config.go b/plugin/circuitbreaker/errorcount/config.go deleted file mode 100644 index 87aaad54..00000000 --- a/plugin/circuitbreaker/errorcount/config.go +++ /dev/null @@ -1,112 +0,0 @@ -/** - * 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 errorcount - -import ( - "fmt" - "math" - "time" - - "github.com/hashicorp/go-multierror" - - "github.com/polarismesh/polaris-go/pkg/model" -) - -// 定义连续错误熔断配置的默认值 -const ( - // DefaultContinuousErrorThreshold 默认可触发熔断的连续错误数,默认10 - DefaultContinuousErrorThreshold = 10 - // DefaultMetricStatTimeWindow 连续错误数统计时间窗口,默认1分钟 - DefaultMetricStatTimeWindow = 1 * time.Minute - // MinMetricStatTimeWindow 最小连续错误数统计时间窗口 - MinMetricStatTimeWindow = 1 * time.Second - // DefaultErrCountMetricBucketCount 连续错误数统计滑桶数量 - DefaultErrCountMetricBucketCount = 10 -) - -// Config 连续错误熔断的配置对象 -type Config struct { - // 连续错误数阈值 - ContinuousErrorThreshold int `yaml:"continuousErrorThreshold" json:"continuousErrorThreshold"` - // 连续错误数统计时间窗口 - MetricStatTimeWindow *time.Duration `yaml:"metricStatTimeWindow" json:"metricStatTimeWindow"` - // 连续错误数统计滑桶数量 - MetricNumBuckets int `yaml:"metricNumBuckets" json:"metricNumBuckets"` -} - -// Verify 检验连续错误熔断配置 -func (r *Config) Verify() error { - var errs error - if r.ContinuousErrorThreshold <= 0 { - errs = multierror.Append(errs, fmt.Errorf("errorCount.continuousErrorThreshold must be greater than 0")) - } - if nil != r.MetricStatTimeWindow && *r.MetricStatTimeWindow < MinMetricStatTimeWindow { - errs = multierror.Append(errs, - fmt.Errorf("errorCount.metricStatTimeWindow must be greater than %v", MinMetricStatTimeWindow)) - } - return errs -} - -// SetDefault 设置连续错误熔断配置默认值 -func (r *Config) SetDefault() { - if r.ContinuousErrorThreshold == 0 { - r.ContinuousErrorThreshold = DefaultContinuousErrorThreshold - } - if nil == r.MetricStatTimeWindow { - r.MetricStatTimeWindow = model.ToDurationPtr(DefaultMetricStatTimeWindow) - } - if r.MetricNumBuckets == 0 { - r.MetricNumBuckets = DefaultErrCountMetricBucketCount - } -} - -// GetBucketInterval 获取滑桶时间间隔 -func (r *Config) GetBucketInterval() time.Duration { - bucketSize := math.Ceil(float64(*r.MetricStatTimeWindow) / float64(r.MetricNumBuckets)) - return time.Duration(bucketSize) -} - -// GetContinuousErrorThreshold 连续错误数阈值 -func (r *Config) GetContinuousErrorThreshold() int { - return r.ContinuousErrorThreshold -} - -// SetContinuousErrorThreshold 设置连续错误数阈值 -func (r *Config) SetContinuousErrorThreshold(value int) { - r.ContinuousErrorThreshold = value -} - -// GetMetricStatTimeWindow 连续错误数统计时间窗口 -func (r *Config) GetMetricStatTimeWindow() time.Duration { - return *r.MetricStatTimeWindow -} - -// SetMetricStatTimeWindow 设置连续错误数统计时间窗口 -func (r *Config) SetMetricStatTimeWindow(value time.Duration) { - r.MetricStatTimeWindow = &value -} - -// GetMetricNumBuckets 连续错误数统计滑桶数量 -func (r *Config) GetMetricNumBuckets() int { - return r.MetricNumBuckets -} - -// SetMetricNumBuckets 设置连续错误数统计滑桶数量 -func (r *Config) SetMetricNumBuckets(value int) { - r.MetricNumBuckets = value -} diff --git a/plugin/circuitbreaker/errorcount/err_count.go b/plugin/circuitbreaker/errorcount/err_count.go deleted file mode 100644 index 18c4c128..00000000 --- a/plugin/circuitbreaker/errorcount/err_count.go +++ /dev/null @@ -1,237 +0,0 @@ -/** - * 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 errorcount - -import ( - "time" - - "github.com/polarismesh/polaris-go/pkg/clock" - "github.com/polarismesh/polaris-go/pkg/config" - "github.com/polarismesh/polaris-go/pkg/log" - "github.com/polarismesh/polaris-go/pkg/metric" - "github.com/polarismesh/polaris-go/pkg/model" - "github.com/polarismesh/polaris-go/pkg/model/local" - "github.com/polarismesh/polaris-go/pkg/model/pb" - "github.com/polarismesh/polaris-go/pkg/plugin" - "github.com/polarismesh/polaris-go/pkg/plugin/circuitbreaker" - common2 "github.com/polarismesh/polaris-go/pkg/plugin/common" - "github.com/polarismesh/polaris-go/plugin/circuitbreaker/common" -) - -// CircuitBreaker 熔断器 -type CircuitBreaker struct { - *plugin.PluginBase - wholeCfg config.Configuration - cfg config.ErrorCountConfig - halfOpenHandler *common.HalfOpenConversionHandler -} - -// Type 插件类型 -func (g *CircuitBreaker) Type() common2.Type { - return common2.TypeCircuitBreaker -} - -// Name 插件名,一个类型下插件名唯一 -func (g *CircuitBreaker) Name() string { - return config.DefaultCircuitBreakerErrCount -} - -// Init 初始化插件 -func (g *CircuitBreaker) Init(ctx *plugin.InitContext) error { - g.PluginBase = plugin.NewPluginBase(ctx) - g.wholeCfg = ctx.Config - g.cfg = ctx.Config.GetConsumer().GetCircuitBreaker().GetErrorCountConfig() - ctx.Plugins.RegisterEventSubscriber(common2.OnInstanceLocalValueCreated, common2.PluginEventHandler{ - Callback: g.generateSliceWindow, - }) - g.halfOpenHandler = common.NewHalfOpenConversionHandler(ctx.Config) - return nil -} - -// 获取实例的滑窗 -func (g *CircuitBreaker) getSliceWindows(instance model.Instance) []*metric.SliceWindow { - instanceInProto := instance.(*pb.InstanceInProto) - return instanceInProto.GetSliceWindows(g.ID()) -} - -// Destroy 销毁插件,可用于释放资源 -func (g *CircuitBreaker) Destroy() error { - return nil -} - -// IsEnable enable -func (g *CircuitBreaker) IsEnable(cfg config.Configuration) bool { - return cfg.GetGlobal().GetSystem().GetMode() != model.ModeWithAgent -} - -const ( - // 错误数统计窗口下标 - metricIdxErrCount = iota - // 最大窗口下标 - metricIdxMax -) - -// 统计维度 -const ( - // 连续错误数 - keyContinuousFailCount = iota + common.MaxHalfOpenDimension - // 总统计维度 - maxDimension -) - -var ( - addMetricWindow = func(gauge model.InstanceGauge, bucket *metric.Bucket) int64 { - var failCount int64 - if gauge.GetRetStatus() == model.RetFail { - failCount = bucket.AddMetric(keyContinuousFailCount, 1) - } else { - // 一次成功则重置连续失败次数为0 - bucket.SetMetric(keyContinuousFailCount, 0) - } - return failCount - } -) - -// 正常状态下的统计 -func (g *CircuitBreaker) regularStat(gauge model.InstanceGauge, metricWindow *metric.SliceWindow) bool { - failCount := metricWindow.AddGauge(gauge, addMetricWindow) - cfg := g.GetErrorCountConfig(gauge.GetNamespace(), gauge.GetService()) - // 只有相同才发,避免发送多次实时任务 - if failCount == int64(cfg.GetContinuousErrorThreshold()) { - calledInstance := gauge.GetCalledInstance() - log.GetBaseLogger().Infof("instance(service=%s, namespace=%s, host=%s, port=%d, instanceId=%s) "+ - "stat trigger errCount limit for failCount %v equals to %v", - gauge.GetService(), gauge.GetNamespace(), calledInstance.GetHost(), calledInstance.GetPort(), - calledInstance.GetId(), failCount, cfg.GetContinuousErrorThreshold()) - return true - } - return false -} - -// Stat 实时上报健康状态并进行连续失败熔断判断,返回当前实例是否需要进行立即熔断 -func (g *CircuitBreaker) Stat(gauge model.InstanceGauge) (bool, error) { - instance := gauge.GetCalledInstance() - cbStatus := instance.GetCircuitBreakerStatus() - if nil != cbStatus && cbStatus.GetStatus() == model.Open { - // 熔断状态不进行统计 - return false, nil - } - metricWindows := g.getSliceWindows(gauge.GetCalledInstance()) - if nil != cbStatus && cbStatus.GetStatus() == model.HalfOpen && cbStatus.GetCircuitBreaker() == g.Name() { - return g.halfOpenHandler.StatHalfOpenCalls(cbStatus, gauge), nil - } - return g.regularStat(gauge, metricWindows[metricIdxErrCount]), nil -} - -// GetErrorCountConfig .获取错误的连续错误数熔断配置 -func (g *CircuitBreaker) GetErrorCountConfig(namespace string, service string) config.ErrorCountConfig { - cfg := g.cfg - serviceSp := g.wholeCfg.GetConsumer().GetServiceSpecific(namespace, service) - if serviceSp != nil { - cfg = serviceSp.GetServiceCircuitBreaker().GetErrorCountConfig() - } - return cfg -} - -// 熔断器从关闭到打开 -func (g *CircuitBreaker) closeToOpen(instance model.Instance, metricWindow *metric.SliceWindow, now time.Time) bool { - cbStatus := instance.GetCircuitBreakerStatus() - if nil != cbStatus && cbStatus.GetStatus() != model.Close { - return false - } - // 统计错误率 - cfg := g.GetErrorCountConfig(instance.GetNamespace(), instance.GetService()) - timeRange := &metric.TimeRange{ - Start: now.Add(0 - cfg.GetMetricStatTimeWindow()), - End: now.Add(metricWindow.GetBucketInterval()), - } - failCount := metricWindow.CalcMetrics(keyContinuousFailCount, timeRange) - if log.GetBaseLogger().IsLevelEnabled(log.TraceLog) { - log.GetBaseLogger().Tracef( - "failCount to calc closeToOpen is %d for instance %s", failCount, instance.GetId()) - } - if failCount >= int64(cfg.GetContinuousErrorThreshold()) { - // 达到阈值可进行熔断 - log.GetDetectLogger().Infof( - "closeToOpen %s: instance(id=%s, address=%s:%d) match condition for failCount=%d(threshold=%d)", - g.Name(), instance.GetId(), instance.GetHost(), instance.GetPort(), - failCount, cfg.GetContinuousErrorThreshold()) - return true - } - return false -} - -// CircuitBreak 熔断计算 -// 定期或触发式进行熔断计算,返回需要进行状态转换的实例ID -// 入参包括全量服务实例,以及当前周期的健康探测结果 -func (g *CircuitBreaker) CircuitBreak(instances []model.Instance) (*circuitbreaker.Result, error) { - result := circuitbreaker.NewCircuitBreakerResult(clock.GetClock().Now()) - for _, instance := range instances { - log.GetBaseLogger().Tracef("invoke %s circuitBreaker for instance %s", g.Name(), instance.GetId()) - metricWindows := g.getSliceWindows(instance) - if g.closeToOpen(instance, metricWindows[metricIdxErrCount], result.Now) { - log.GetDetectLogger().Warnf("ErrCount circuitbreaker: close to open, instance:"+ - " Id: %s, Namespace: %s, Service: %s, Host: %s, Port: %v", - instance.GetId(), instance.GetNamespace(), instance.GetService(), instance.GetHost(), instance.GetPort()) - result.InstancesToOpen.Add(instance.GetId()) - continue - } - if g.halfOpenHandler.OpenToHalfOpen(instance, result.Now, g.Name()) { - log.GetDetectLogger().Infof("ErrCount circuitbreaker: open to halfopen, instance:"+ - " Id: %s, Namespace: %s, Service: %s, Host: %s, Port: %v", - instance.GetId(), instance.GetNamespace(), instance.GetService(), instance.GetHost(), instance.GetPort()) - result.InstancesToHalfOpen.Add(instance.GetId()) - continue - } - - halfOpenChange := g.halfOpenHandler.HalfOpenConversion(result.Now, instance, g.Name()) - switch halfOpenChange { - case common.ToOpen: - log.GetDetectLogger().Warnf("ErrCount circuitbreaker: halfopen to open, instance:"+ - " Id: %s, Namespace: %s, Service: %s, Host: %s, Port: %v", - instance.GetId(), instance.GetNamespace(), instance.GetService(), instance.GetHost(), instance.GetPort()) - result.InstancesToOpen.Add(instance.GetId()) - case common.ToClose: - log.GetDetectLogger().Infof("ErrCount circuitbreaker: halfopen to close, instance:"+ - " Id: %s, Namespace: %s, Service: %s, Host: %s, Port: %v", - instance.GetId(), instance.GetNamespace(), instance.GetService(), instance.GetHost(), instance.GetPort()) - result.InstancesToClose.Add(instance.GetId()) - } - } - if result.IsEmpty() { - return nil, nil - } - result.RequestCountAfterHalfOpen = g.halfOpenHandler.GetRequestCountAfterHalfOpen() - return result, nil -} - -// 生成滑窗 -func (g *CircuitBreaker) generateSliceWindow(event *common2.PluginEvent) error { - localValue := event.EventObject.(*local.DefaultInstanceLocalValue) - metricWindows := make([]*metric.SliceWindow, metricIdxMax) - metricWindows[metricIdxErrCount] = metric.NewSliceWindow(g.Name(), - g.cfg.GetMetricNumBuckets(), g.cfg.GetBucketInterval(), maxDimension, clock.GetClock().Now().UnixNano()) - // metricWindows[metricIdxHalfOpen] = g.halfOpenHandler.CreateHalfOpenMetricWindow(g.Name()) - localValue.SetSliceWindows(g.ID(), metricWindows) - return nil -} - -// init 插件注册 -func init() { - plugin.RegisterConfigurablePlugin(&CircuitBreaker{}, &Config{}) -} diff --git a/plugin/circuitbreaker/errorrate/config.go b/plugin/circuitbreaker/errorrate/config.go deleted file mode 100644 index d7c4e914..00000000 --- a/plugin/circuitbreaker/errorrate/config.go +++ /dev/null @@ -1,149 +0,0 @@ -/** - * 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 errorrate - -import ( - "fmt" - "math" - "time" - - "github.com/hashicorp/go-multierror" - - "github.com/polarismesh/polaris-go/pkg/model" -) - -// 定义错误率熔断配置的默认值 -const ( - // DefaultRequestVolumeThreshold 只有请求数达到某个阈值才执行熔断计算,默认10 - DefaultRequestVolumeThreshold = 10 - // DefaultErrorRateThreshold 触发熔断的错误率阈值,默认0.5 - DefaultErrorRateThreshold float64 = 0.5 - // DefaultErrorRatePercent 默认错误率百分比 - DefaultErrorRatePercent int = 50 - // MaxErrorRatePercent 最大错误率百分比 - MaxErrorRatePercent int = 100 - // DefaultMetricStatTimeWindow 错误率统计时间窗口,默认1分钟 - DefaultMetricStatTimeWindow = 60 * time.Second - // MinMetricStatTimeWindow 最小错误率统计时间窗口,1s - MinMetricStatTimeWindow = 1 * time.Second - // DefaultMetricNumBuckets 统计窗口细分的桶数量,默认10 - DefaultMetricNumBuckets = 5 - // MinMetricStatBucketSize 最小的滑窗时间片,1ms - MinMetricStatBucketSize = 1 * time.Millisecond -) - -// Config 基于错误率熔断器的配置结构 -type Config struct { - RequestVolumeThreshold int `yaml:"requestVolumeThreshold" json:"requestVolumeThreshold"` - ErrorRatePercent int `yaml:"errorRatePercent" json:"errorRatePercent"` - // Deprecated: 请使用ErrorRatePercent - ErrorRateThreshold float64 `yaml:"errorRateThreshold" json:"errorRateThreshold"` - MetricStatTimeWindow *time.Duration `yaml:"metricStatTimeWindow" json:"metricStatTimeWindow"` - MetricNumBuckets int `yaml:"metricNumBuckets" json:"metricNumBuckets"` -} - -// Verify 检验错误率熔断配置 -func (r *Config) Verify() error { - var errs error - if r.RequestVolumeThreshold <= 0 { - errs = multierror.Append(errs, fmt.Errorf("errRate.requestVolumeThreshold must be greater than 0")) - } - if r.ErrorRatePercent <= 0 || r.ErrorRatePercent > MaxErrorRatePercent { - errs = multierror.Append(errs, fmt.Errorf( - "errRate.errorRatePercent must be greater than 0 and lower than %d", MaxErrorRatePercent)) - } - if nil != r.MetricStatTimeWindow && *r.MetricStatTimeWindow < MinMetricStatTimeWindow { - errs = multierror.Append(errs, - fmt.Errorf("errRate.metricStatTimeWindow must be greater than %v", MinMetricStatTimeWindow)) - } - if r.MetricNumBuckets <= 0 { - errs = multierror.Append(errs, fmt.Errorf("errRate.metricNumBuckets must be greater than 0")) - } - if r.GetBucketInterval() < MinMetricStatBucketSize { - errs = multierror.Append(errs, - fmt.Errorf("bucketSize(metricStatTimeWindow/metricNumBuckets) must be greater than %v", - MinMetricStatBucketSize)) - } - return errs -} - -// GetBucketInterval 获取滑桶时间间隔 -func (r *Config) GetBucketInterval() time.Duration { - bucketSize := math.Ceil(float64(*r.MetricStatTimeWindow) / float64(r.MetricNumBuckets)) - return time.Duration(bucketSize) -} - -// SetDefault 设置错误率熔断配置的默认值 -func (r *Config) SetDefault() { - if r.RequestVolumeThreshold == 0 { - r.RequestVolumeThreshold = DefaultRequestVolumeThreshold - } - // 兼容原有配置项 - if r.ErrorRateThreshold > 0 && r.ErrorRatePercent == 0 { - r.ErrorRatePercent = int(r.ErrorRateThreshold * 100) - } - if r.ErrorRatePercent == 0 { - r.ErrorRatePercent = DefaultErrorRatePercent - } - if nil == r.MetricStatTimeWindow { - r.MetricStatTimeWindow = model.ToDurationPtr(DefaultMetricStatTimeWindow) - } - if r.MetricNumBuckets == 0 { - r.MetricNumBuckets = DefaultMetricNumBuckets - } -} - -// GetRequestVolumeThreshold 触发错误率熔断的请求量阈值 -func (r *Config) GetRequestVolumeThreshold() int { - return r.RequestVolumeThreshold -} - -// SetRequestVolumeThreshold 设置触发错误率熔断的请求量阈值 -func (r *Config) SetRequestVolumeThreshold(value int) { - r.RequestVolumeThreshold = value -} - -// GetErrorRatePercent 触发熔断的错误率阈值,取值范围(0, 100] -func (r *Config) GetErrorRatePercent() int { - return r.ErrorRatePercent -} - -// SetErrorRatePercent 设置错误率阈值 -func (r *Config) SetErrorRatePercent(value int) { - r.ErrorRatePercent = value -} - -// GetMetricStatTimeWindow 错误率统计时间窗口 -func (r *Config) GetMetricStatTimeWindow() time.Duration { - return *r.MetricStatTimeWindow -} - -// SetMetricStatTimeWindow 设置错误率统计时间窗口 -func (r *Config) SetMetricStatTimeWindow(value time.Duration) { - r.MetricStatTimeWindow = &value -} - -// GetMetricNumBuckets 统计窗口细分的桶数量 -func (r *Config) GetMetricNumBuckets() int { - return r.MetricNumBuckets -} - -// SetMetricNumBuckets 设置统计窗口细分的桶数量 -func (r *Config) SetMetricNumBuckets(value int) { - r.MetricNumBuckets = value -} diff --git a/plugin/circuitbreaker/errorrate/err_rate.go b/plugin/circuitbreaker/errorrate/err_rate.go deleted file mode 100644 index 4945ae30..00000000 --- a/plugin/circuitbreaker/errorrate/err_rate.go +++ /dev/null @@ -1,222 +0,0 @@ -/** - * 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 errorrate - -import ( - "time" - - "github.com/polarismesh/polaris-go/pkg/clock" - "github.com/polarismesh/polaris-go/pkg/config" - "github.com/polarismesh/polaris-go/pkg/log" - "github.com/polarismesh/polaris-go/pkg/metric" - "github.com/polarismesh/polaris-go/pkg/model" - "github.com/polarismesh/polaris-go/pkg/model/local" - "github.com/polarismesh/polaris-go/pkg/model/pb" - "github.com/polarismesh/polaris-go/pkg/plugin" - "github.com/polarismesh/polaris-go/pkg/plugin/circuitbreaker" - common2 "github.com/polarismesh/polaris-go/pkg/plugin/common" - "github.com/polarismesh/polaris-go/plugin/circuitbreaker/common" -) - -// CircuitBreaker 基于错误率的默认熔断规则 -type CircuitBreaker struct { - *plugin.PluginBase - cfg config.ErrorRateConfig - halfOpenHandler *common.HalfOpenConversionHandler -} - -// Type 插件类型 -func (g *CircuitBreaker) Type() common2.Type { - return common2.TypeCircuitBreaker -} - -// Name 插件名,一个类型下插件名唯一 -func (g *CircuitBreaker) Name() string { - return config.DefaultCircuitBreakerErrRate -} - -// Init 初始化插件 -func (g *CircuitBreaker) Init(ctx *plugin.InitContext) error { - g.PluginBase = plugin.NewPluginBase(ctx) - g.cfg = ctx.Config.GetConsumer().GetCircuitBreaker().GetErrorRateConfig() - ctx.Plugins.RegisterEventSubscriber(common2.OnInstanceLocalValueCreated, common2.PluginEventHandler{ - Callback: g.generateSliceWindow, - }) - g.halfOpenHandler = common.NewHalfOpenConversionHandler(ctx.Config) - return nil -} - -// Destroy 销毁插件,可用于释放资源 -func (g *CircuitBreaker) Destroy() error { - return nil -} - -// IsEnable enable -func (g *CircuitBreaker) IsEnable(cfg config.Configuration) bool { - return cfg.GetGlobal().GetSystem().GetMode() != model.ModeWithAgent -} - -const ( - // 错误率统计窗口下标 - metricIdxErrRate = iota - // 半开统计窗口下标 - metricIdxHalfOpen - // 最大窗口下标 - metricIdxMax -) - -// 统计维度 -const ( - // 总请求数 - keyRequestCount = iota - // 错误数 - keyFailCount - // 总统计维度 - maxDimension -) - -var ( - addMetricWindow = func(gauge model.InstanceGauge, bucket *metric.Bucket) int64 { - bucket.AddMetric(keyRequestCount, 1) - if gauge.GetRetStatus() == model.RetFail { - bucket.AddMetric(keyFailCount, 1) - } - return 0 - } -) - -// 正常状态下的统计 -func (g *CircuitBreaker) regularStat(gauge model.InstanceGauge, metricWindow *metric.SliceWindow) { - metricWindow.AddGauge(gauge, addMetricWindow) -} - -// 获取实例的滑窗 -func (g *CircuitBreaker) getSliceWindows(instance model.Instance) []*metric.SliceWindow { - instanceInProto := instance.(*pb.InstanceInProto) - return instanceInProto.GetSliceWindows(g.ID()) -} - -// Stat 实时上报健康状态并进行失败率统计 -func (g *CircuitBreaker) Stat(gauge model.InstanceGauge) (bool, error) { - instance := gauge.GetCalledInstance() - cbStatus := instance.GetCircuitBreakerStatus() - if nil != cbStatus && cbStatus.GetStatus() == model.Open { - // 熔断状态不进行统计 - return false, nil - } - metricWindows := g.getSliceWindows(gauge.GetCalledInstance()) - if nil != cbStatus && cbStatus.GetStatus() == model.HalfOpen && cbStatus.GetCircuitBreaker() == g.Name() { - return g.halfOpenHandler.StatHalfOpenCalls(cbStatus, gauge), nil - } - g.regularStat(gauge, metricWindows[metricIdxErrRate]) - return false, nil -} - -// 熔断器从关闭到打开 -func (g *CircuitBreaker) closeToOpen(instance model.Instance, metricWindow *metric.SliceWindow, now time.Time) bool { - cbStatus := instance.GetCircuitBreakerStatus() - if nil != cbStatus && cbStatus.GetStatus() != model.Close { - return false - } - // 统计错误率 - timeRange := &metric.TimeRange{ - Start: now.Add(0 - g.cfg.GetMetricStatTimeWindow()), - End: now.Add(metricWindow.GetBucketInterval()), - } - values := metricWindow.CalcMetricsInMultiDimensions([]int{keyRequestCount, keyFailCount}, timeRange) - reqCount := values[0] - failCount := values[1] - if reqCount == 0 || reqCount < int64(g.cfg.GetRequestVolumeThreshold()) { - // 未达到其实请求数阈值 - return false - } - failRatio := float64(failCount) / float64(reqCount) - errRateThreshold := ToErrorRateThreshold(g.cfg.GetErrorRatePercent()) - if failRatio >= errRateThreshold { - // 错误率达标 - log.GetDetectLogger().Infof( - "closeToOpen %s: instance(id=%s, address=%s:%d) match condition for failRatio=%.2f(threshold=%.2f)", - g.Name(), instance.GetId(), instance.GetHost(), instance.GetPort(), failRatio, errRateThreshold) - return true - } - return false -} - -// ToErrorRateThreshold 转换成熔断错误率阈值 -func ToErrorRateThreshold(errorRatePercent int) float64 { - return float64(errorRatePercent) / 100 -} - -// 生成滑窗 -func (g *CircuitBreaker) generateSliceWindow(event *common2.PluginEvent) error { - localValue := event.EventObject.(*local.DefaultInstanceLocalValue) - metricWindows := make([]*metric.SliceWindow, metricIdxMax) - metricWindows[metricIdxErrRate] = metric.NewSliceWindow(g.Name(), - g.cfg.GetMetricNumBuckets(), g.cfg.GetBucketInterval(), maxDimension, clock.GetClock().Now().UnixNano()) - metricWindows[metricIdxHalfOpen] = g.halfOpenHandler.CreateHalfOpenMetricWindow(g.Name()) - localValue.SetSliceWindows(g.ID(), metricWindows) - return nil -} - -// CircuitBreak .熔断 -// 定期进行熔断计算,返回需要进行状态转换的实例ID -// 入参包括全量服务实例,以及当前周期的健康探测结果 -func (g *CircuitBreaker) CircuitBreak(instances []model.Instance) (*circuitbreaker.Result, error) { - result := circuitbreaker.NewCircuitBreakerResult(clock.GetClock().Now()) - for _, instance := range instances { - metricWindows := g.getSliceWindows(instance) - if g.closeToOpen(instance, metricWindows[metricIdxErrRate], result.Now) { - log.GetBaseLogger().Warnf("ErrRate circuitbreaker: close to open, instance:"+ - " Id: %s, Namespace: %s, Service: %s, Host: %s, Port: %v\n", - instance.GetId(), instance.GetNamespace(), instance.GetService(), instance.GetHost(), instance.GetPort()) - result.InstancesToOpen.Add(instance.GetId()) - continue - } - if g.halfOpenHandler.OpenToHalfOpen(instance, result.Now, g.Name()) { - log.GetBaseLogger().Infof("ErrRate circuitbreaker: open to halfopen, instance:"+ - " Id: %s, Namespace: %s, Service: %s, Host: %s, Port: %v\n", - instance.GetId(), instance.GetNamespace(), instance.GetService(), instance.GetHost(), instance.GetPort()) - result.InstancesToHalfOpen.Add(instance.GetId()) - continue - } - - halfOpenChange := g.halfOpenHandler.HalfOpenConversion(result.Now, instance, g.Name()) - switch halfOpenChange { - case common.ToOpen: - log.GetBaseLogger().Warnf("ErrRate circuitbreaker: halfopen to open, instance:"+ - " Id: %s, Namespace: %s, Service: %s, Host: %s, Port: %v\n", - instance.GetId(), instance.GetNamespace(), instance.GetService(), instance.GetHost(), instance.GetPort()) - result.InstancesToOpen.Add(instance.GetId()) - case common.ToClose: - log.GetBaseLogger().Infof("ErrRate circuitbreaker: halfopen to close, instance:"+ - " Id: %s, Namespace: %s, Service: %s, Host: %s, Port: %v\n", - instance.GetId(), instance.GetNamespace(), instance.GetService(), instance.GetHost(), instance.GetPort()) - result.InstancesToClose.Add(instance.GetId()) - } - } - if result.IsEmpty() { - return nil, nil - } - result.RequestCountAfterHalfOpen = g.halfOpenHandler.GetRequestCountAfterHalfOpen() - return result, nil -} - -// init 插件注册 -func init() { - plugin.RegisterConfigurablePlugin(&CircuitBreaker{}, &Config{}) -} diff --git a/plugin/healthcheck/http/http.go b/plugin/healthcheck/http/http.go index 4c32c3e9..0caca210 100644 --- a/plugin/healthcheck/http/http.go +++ b/plugin/healthcheck/http/http.go @@ -22,6 +22,8 @@ import ( "net/url" "time" + "github.com/polarismesh/specification/source/go/api/v1/fault_tolerance" + "github.com/polarismesh/polaris-go/pkg/config" "github.com/polarismesh/polaris-go/pkg/log" "github.com/polarismesh/polaris-go/pkg/model" @@ -123,6 +125,11 @@ func (g *Detector) doHttpDetect(address string) bool { return false } +// Protocol . +func (g *Detector) Protocol() fault_tolerance.FaultDetectRule_Protocol { + return fault_tolerance.FaultDetectRule_HTTP +} + // init 注册插件信息 func init() { plugin.RegisterConfigurablePlugin(&Detector{}, &Config{}) diff --git a/plugin/healthcheck/tcp/tcp.go b/plugin/healthcheck/tcp/tcp.go index 60d4dc0b..454cdb94 100644 --- a/plugin/healthcheck/tcp/tcp.go +++ b/plugin/healthcheck/tcp/tcp.go @@ -21,6 +21,8 @@ import ( "net" "time" + "github.com/polarismesh/specification/source/go/api/v1/fault_tolerance" + "github.com/polarismesh/polaris-go/pkg/config" "github.com/polarismesh/polaris-go/pkg/log" "github.com/polarismesh/polaris-go/pkg/model" @@ -90,6 +92,11 @@ func (g *Detector) doTCPDetect(address string) bool { return true } +// Protocol . +func (g *Detector) Protocol() fault_tolerance.FaultDetectRule_Protocol { + return fault_tolerance.FaultDetectRule_TCP +} + // IsEnable enable func (g *Detector) IsEnable(cfg config.Configuration) bool { return cfg.GetGlobal().GetSystem().GetMode() != model.ModeWithAgent diff --git a/plugin/localregistry/inmemory/inmemory.go b/plugin/localregistry/inmemory/inmemory.go index 94e89ddb..ede17ee8 100644 --- a/plugin/localregistry/inmemory/inmemory.go +++ b/plugin/localregistry/inmemory/inmemory.go @@ -162,6 +162,8 @@ func (g *LocalCache) Init(ctx *plugin.InitContext) error { g.eventToCacheHandlers[model.EventInstances] = g.newServiceCacheHandler() g.eventToCacheHandlers[model.EventRouting] = g.newRuleCacheHandler() g.eventToCacheHandlers[model.EventRateLimiting] = g.newRateLimitCacheHandler() + g.eventToCacheHandlers[model.EventCircuitBreaker] = g.newCircuitBreakerCacheHandler() + g.eventToCacheHandlers[model.EventFaultDetect] = g.newFaultDetectCacheHandler() // 批量服务 g.eventToCacheHandlers[model.EventServices] = g.newServicesHandler() g.cachePersistHandler, err = lrplug.NewCachePersistHandler( @@ -589,14 +591,18 @@ func (g *LocalCache) UpdateInstances(svcUpdateReq *localregistry.ServiceUpdateRe property := svcUpdateReq.Properties[i] instances := g.GetInstances(property.Service, true, true) svcInstancesInProto := instances.(*pb.ServiceInstancesInProto) - localValuesIntf := svcInstancesInProto.GetInstanceLocalValue(property.ID) + var localValuesIntf local.InstanceLocalValue + if len(property.ID) != 0 { + localValuesIntf = svcInstancesInProto.GetInstanceLocalValue(property.ID) + } else { + localValuesIntf = svcInstancesInProto.GetInstanceLocalValueByEndpoint(property.Host, property.Port) + } if nil == localValuesIntf { log.GetBaseLogger().Warnf( "instance %s for service %s has been expired, update ignored", property.ID, *property.Service) continue } localValues := localValuesIntf.(*local.DefaultInstanceLocalValue) - updateInstance := svcInstancesInProto.GetInstance(property.ID) for k, v := range property.Properties { switch k { case localregistry.PropertyCircuitBreakerStatus: @@ -608,11 +614,6 @@ func (g *LocalCache) UpdateInstances(svcUpdateReq *localregistry.ServiceUpdateRe (nil == preCBStatus && nextCBStatus.GetStatus() == model.Close) { cbStatusUpdated = false } - err := g.engine.SyncReportStat(model.CircuitBreakStat, - &model.CircuitBreakGauge{ChangeInstance: updateInstance, CBStatus: nextCBStatus}) - if err != nil { - log.GetBaseLogger().Errorf("fail to report circuitbreak change, error %v", err) - } case localregistry.PropertyHealthCheckStatus: localValues.SetActiveDetectStatus(v.(model.ActiveDetectStatus)) } @@ -732,6 +733,24 @@ func (g *LocalCache) newRateLimitCacheHandler() CacheHandlers { } } +// 创建熔断规则缓存操作回调集合 +func (g *LocalCache) newCircuitBreakerCacheHandler() CacheHandlers { + return CacheHandlers{ + CompareMessage: compareResource, + MessageToCacheValue: messageToServiceRule, + OnEventDeleted: g.deleteRule, + } +} + +// 创建探测规则缓存操作回调集合 +func (g *LocalCache) newFaultDetectCacheHandler() CacheHandlers { + return CacheHandlers{ + CompareMessage: compareResource, + MessageToCacheValue: messageToServiceRule, + OnEventDeleted: g.deleteRule, + } +} + // 创建批量服务回调 func (g *LocalCache) newServicesHandler() CacheHandlers { return CacheHandlers{ diff --git a/plugin/ratelimiter/common/ratelimit_slice_window.go b/plugin/ratelimiter/common/ratelimit_slice_window.go index 7e9552af..2de9126e 100644 --- a/plugin/ratelimiter/common/ratelimit_slice_window.go +++ b/plugin/ratelimiter/common/ratelimit_slice_window.go @@ -63,12 +63,11 @@ func (s *SlidingWindow) currentWindow(curTimeMs int64, reset bool) (*Window, *Wi return oldWindow, nil } else if !reset { return nil, nil - } else { - s.mutex.Lock() - expiredWindow := oldWindow.reset(oldWindowStart, windowStart) - s.mutex.Unlock() - return oldWindow, expiredWindow } + s.mutex.Lock() + expiredWindow := oldWindow.reset(oldWindowStart, windowStart) + s.mutex.Unlock() + return oldWindow, expiredWindow } // AddAndGetCurrentPassed 原子增加,并返回当前bucket diff --git a/plugin/serverconnector/common/discover.go b/plugin/serverconnector/common/discover.go index e28bc2cf..e323b81b 100644 --- a/plugin/serverconnector/common/discover.go +++ b/plugin/serverconnector/common/discover.go @@ -141,7 +141,7 @@ func (g *DiscoverConnector) doLog() { for { select { case <-g.Done(): - log.GetBaseLogger().Infof("doLog routine of grpc connector has benn terminated") + log.GetNetworkLogger().Infof("doLog routine of grpc connector has benn terminated") return case <-logLoop.C: g.updateTaskSet.Range(func(k, v interface{}) bool { @@ -170,10 +170,10 @@ func (g *DiscoverConnector) doRetry() { for { select { case <-g.Done(): - log.GetBaseLogger().Infof("doRetry routine of grpc connector has benn terminated") + log.GetNetworkLogger().Infof("doRetry routine of grpc connector has benn terminated") return case svcEventKey := <-g.retryPriorityTaskChannel: - log.GetBaseLogger().Infof("retry: start add priority task %s", svcEventKey) + log.GetNetworkLogger().Infof("retry: start add priority task %s", svcEventKey) if taskValue, ok := g.updateTaskSet.Load(svcEventKey); ok { priorityTask := taskValue.(*serviceUpdateTask) g.scheduleRetry(priorityTask) @@ -193,14 +193,14 @@ func (g *DiscoverConnector) scheduleRetry(task *serviceUpdateTask) { task.retryLock.Lock() defer task.retryLock.Unlock() if atomic.CompareAndSwapUint32(&task.longRun, retryTask, firstTask) { - log.GetBaseLogger().Infof("retry: start schedule task %s", task.ServiceEventKey) + log.GetNetworkLogger().Infof("retry: start schedule task %s", task.ServiceEventKey) select { case <-g.Done(): - log.GetBaseLogger().Infof("%s, context done, exit retry", g.ServiceConnector.GetSDKContextID()) + log.GetNetworkLogger().Infof("%s, context done, exit retry", g.ServiceConnector.GetSDKContextID()) return case <-task.retryDeadline: _ = g.addFirstTask(task) - log.GetBaseLogger().Infof("retry: success schedule task %s", task.ServiceEventKey) + log.GetNetworkLogger().Infof("retry: success schedule task %s", task.ServiceEventKey) } } } @@ -219,7 +219,7 @@ func (g *DiscoverConnector) doSend() { // 如果刚好连接切换,还没有执行到clearIdleClient,旧连接可能还是活跃的,关闭连接避免泄露 streamingClient.CloseStream(true) } - log.GetBaseLogger().Infof("doSend routine of grpc connector has benn terminated") + log.GetNetworkLogger().Infof("doSend routine of grpc connector has benn terminated") return case clientTask := <-g.taskChannel: streamingClient = g.onClientTask(streamingClient, clientTask) @@ -227,6 +227,9 @@ func (g *DiscoverConnector) doSend() { if nil != streamingClient { allTaskTimeout := g.clearTimeoutClient(streamingClient) hasSwitchedClient := g.clearSwitchedClient(streamingClient) + if hasSwitchedClient || allTaskTimeout { + log.GetNetworkLogger().Infof("trigger clear idle client, hasSwitchedClient(%+v) allTaskTimeout(%+v)", hasSwitchedClient, allTaskTimeout) + } g.clearIdleClient(streamingClient, hasSwitchedClient || allTaskTimeout) } g.updateTaskSet.Range(func(k, v interface{}) bool { @@ -235,11 +238,11 @@ func (g *DiscoverConnector) doSend() { if atomic.LoadUint32(&task.longRun) != longRunning || !task.needUpdate() { return true } - log.GetBaseLogger().Debugf( + log.GetNetworkLogger().Debugf( "start to update task %s, update interval %v", task.ServiceEventKey, task.updateInterval) streamingClient = g.processUpdateTask(streamingClient, task) if len(g.taskChannel) > 0 { - log.GetBaseLogger().Infof("firstTask received, now breakthrough updateTasks") + log.GetNetworkLogger().Infof("firstTask received, now breakthrough updateTasks") return false } return true @@ -253,7 +256,7 @@ func (g *DiscoverConnector) retryUpdateTask(updateTask *serviceUpdateTask, err e updateTask.retryLock.Lock() defer updateTask.retryLock.Unlock() if atomic.CompareAndSwapUint32(&updateTask.longRun, firstTask, retryTask) { - log.GetBaseLogger().Warnf("retry: task %s for error %v", updateTask.ServiceEventKey, err) + log.GetNetworkLogger().Warnf("retry: task %s for error %v", updateTask.ServiceEventKey, err) if notReady { // 如果是等待首次连接的,则缩短重试间隔 updateTask.retryDeadline = time.After(clock.TimeStep()) @@ -265,7 +268,7 @@ func (g *DiscoverConnector) retryUpdateTask(updateTask *serviceUpdateTask, err e g.retryPriorityTaskChannel <- updateTask.ServiceEventKey } } else { - log.GetBaseLogger().Warnf( + log.GetNetworkLogger().Warnf( "skip retry: not first task %s for error %v", updateTask.ServiceEventKey, err) updateTask.lastUpdateTime.Store(time.Now()) } @@ -276,7 +279,7 @@ const maxLogMsgSize = 4 * 1024 * 1024 // 打印应答消息 func logDiscoverResponse(resp *apiservice.DiscoverResponse, connection *network.Connection) { - if log.GetBaseLogger().IsLevelEnabled(log.DebugLog) { + if log.GetNetworkLogger().IsLevelEnabled(log.DebugLog) { svcKey := model.ServiceEventKey{ ServiceKey: model.ServiceKey{ Namespace: resp.GetService().GetNamespace().GetValue(), @@ -287,10 +290,10 @@ func logDiscoverResponse(resp *apiservice.DiscoverResponse, connection *network. jsonMarshaler := &jsonpb.Marshaler{} respJSON, _ := jsonMarshaler.MarshalToString(resp) if len(respJSON) <= maxLogMsgSize { - log.GetBaseLogger().Debugf("received response from %s(%s), service %s: \n%v", + log.GetNetworkLogger().Debugf("received response from %s(%s), service %s: \n%v", connection.ConnID, connection.Address, svcKey, respJSON) } else { - log.GetBaseLogger().Debugf("received response from %s(%s), service %s: message size exceed %v", + log.GetNetworkLogger().Debugf("received response from %s(%s), service %s: message size exceed %v", connection.ConnID, connection.Address, svcKey, maxLogMsgSize) } } @@ -306,7 +309,7 @@ func discoverResponseToEvent(resp *apiservice.DiscoverResponse, if model.IsSuccessResultCode(retCode) { svcEvent.Value = resp } else { - log.GetBaseLogger().Errorf("server error received, code %v, info: %s", retCode, errInfo) + log.GetNetworkLogger().Errorf("server error received, code %v, info: %s", retCode, errInfo) svcEvent.Error = model.NewServerSDKError(retCode, errInfo, nil, "server error from %s: %s", connection.ConnID.Address, errInfo) } @@ -316,7 +319,7 @@ func discoverResponseToEvent(resp *apiservice.DiscoverResponse, // 将任务加入调度列表 func (g *DiscoverConnector) addUpdateTaskSet(updateTask *serviceUpdateTask) { if atomic.CompareAndSwapUint32(&updateTask.longRun, firstTask, longRunning) { - log.GetBaseLogger().Infof("serviceEvent %s update has been scheduled, interval %v", + log.GetNetworkLogger().Infof("serviceEvent %s update has been scheduled, interval %v", updateTask.ServiceEventKey, updateTask.updateInterval) g.updateTaskSet.Store(updateTask.ServiceEventKey, updateTask) } @@ -341,8 +344,8 @@ func (g *DiscoverConnector) onAddListener( // 处理删除服务实例更新任务 func (g *DiscoverConnector) onDelListener(taskKey *model.ServiceEventKey) { - // log.GetBaseLogger().Infof("serviceEvent %s update has been cancelled", *taskKey) - log.GetBaseLogger().Infof("%s, onDelListener: task %s removed from updateTaskSet", + // log.GetNetworkLogger().Infof("serviceEvent %s update has been cancelled", *taskKey) + log.GetNetworkLogger().Infof("%s, onDelListener: task %s removed from updateTaskSet", g.ServiceConnector.GetSDKContextID(), *taskKey) g.updateTaskSet.Delete(*taskKey) } @@ -473,7 +476,7 @@ func (s *StreamingClient) checkErrorReport(grpcErr error, resp *apiservice.Disco // 使用streamClient进行收包,并更新服务信息 func (s *StreamingClient) receiveAndNotify() { - log.GetBaseLogger().Infof("%s, receiveAndNotify of streamingClient %s start to receive message", + log.GetNetworkLogger().Infof("%s, receiveAndNotify of streamingClient %s start to receive message", s.connector.ServiceConnector.GetSDKContextID(), s.reqID) for { resp, grpcErr := s.discoverClient.Recv() @@ -501,7 +504,7 @@ func (s *StreamingClient) receiveAndNotify() { } } // 出现了错误,退出收包协程 - log.GetBaseLogger().Infof("%s, receiveAndNotify of streamClient %s terminated", + log.GetNetworkLogger().Infof("%s, receiveAndNotify of streamClient %s terminated", s.connector.ServiceConnector.GetSDKContextID(), s.reqID) return } @@ -530,7 +533,7 @@ func (s *StreamingClient) receiveAndNotify() { // 服务如果没有被删除,则添加后续轮询 s.connector.addUpdateTaskSet(updateTask) } else { - log.GetBaseLogger().Errorf("%s, can not get task %s from streamingClient %s pendingTask", + log.GetNetworkLogger().Errorf("%s, can not get task %s from streamingClient %s pendingTask", s.connector.ServiceConnector.GetSDKContextID(), svcKey, s.reqID) } } @@ -560,7 +563,7 @@ func (g *DiscoverConnector) checkStreamingClientAvailable( } origTask, ok := streamingClient.pendingTasks[task.ServiceEventKey] if ok { - log.GetBaseLogger().Errorf("%s, checkStreamingClientAvailable:"+ + log.GetNetworkLogger().Errorf("%s, checkStreamingClientAvailable:"+ " add exist task %s to client %s, whose msgSendTime is %s, lastUpdateTime is %s", g.ServiceConnector.GetSDKContextID(), origTask, streamingClient.reqID, atomicTimeToString(origTask.msgSendTime), atomicTimeToString(origTask.lastUpdateTime)) @@ -659,7 +662,7 @@ func (s *StreamingClient) allTaskTimeout(msgTimeout time.Duration) bool { } lastRecvTime := s.getLastRecvTime() if allTaskTimeout && time.Since(*lastRecvTime) > msgTimeout { - log.GetBaseLogger().Infof("%s, allTaskTimeout: timeout on connection %s, connection lastRecv time is %v", + log.GetNetworkLogger().Infof("%s, allTaskTimeout: timeout on connection %s, connection lastRecv time is %v", s.connector.ServiceConnector.GetSDKContextID(), s.connection.ConnID, *lastRecvTime) return true } @@ -683,7 +686,7 @@ func (g *DiscoverConnector) clearTimeoutClient(client *StreamingClient) bool { return false } // 进行closeStream操作 - log.GetBaseLogger().Warnf( + log.GetNetworkLogger().Warnf( "connection %s(%s) reqID %s has pending tasks after timeout %v, start to terminate", client.connection.ConnID, client.reqID, client.connection.Address, g.messageTimeout) atomic.StoreUint32(&client.hasError, 1) @@ -731,11 +734,11 @@ func (g *DiscoverConnector) asyncUpdateTask( // 构造新的streamingClient streamingClient, err = g.newStream(task) if err != nil { - log.GetBaseLogger().Errorf("fail to create stream for service %v, error is %+v", task.ServiceEventKey, err) + log.GetNetworkLogger().Errorf("fail to create stream for service %v, error is %+v", task.ServiceEventKey, err) return nil } } - log.GetBaseLogger().Debugf("send request(id=%s) to %s for service %v", + log.GetNetworkLogger().Debugf("send request(id=%s) to %s for service %v", streamingClient.reqID, streamingClient.connection.Address, task.ServiceEventKey) task.msgSendTime.Store(curTime) taskType := atomic.LoadUint32(&task.longRun) @@ -760,7 +763,7 @@ func (g *DiscoverConnector) processUpdateTask( // 未到更新时间 return streamingClient } - log.GetBaseLogger().Debugf("start to process task %s", task.ServiceEventKey) + log.GetNetworkLogger().Debugf("start to process task %s", task.ServiceEventKey) if task.targetCluster == config.BuiltinCluster { err := g.syncUpdateTask(task) if err != nil { @@ -858,9 +861,9 @@ func (s *serviceUpdateTask) toDiscoverRequest() *apiservice.DiscoverRequest { Business: &wrappers.StringValue{Value: s.handler.GetBusiness()}, }, } - if log.GetBaseLogger().IsLevelEnabled(log.DebugLog) { + if log.GetNetworkLogger().IsLevelEnabled(log.DebugLog) { reqJSON, _ := (&jsonpb.Marshaler{}).MarshalToString(request) - log.GetBaseLogger().Debugf( + log.GetNetworkLogger().Debugf( "discover request to send is %s", reqJSON) } return request @@ -883,7 +886,7 @@ func (g *DiscoverConnector) RegisterServiceHandler(svcEventHandler *serverconnec diffSecond := g.scalableRand.Intn(3) mu.Unlock() updateTask.updateInterval = svcEventHandler.RefreshInterval + (time.Duration(diffSecond) * time.Second) - log.GetBaseLogger().Debugf( + log.GetNetworkLogger().Debugf( "register update task for service %s, update interval %v", updateTask.ServiceEventKey, updateTask.updateInterval) return g.addFirstTask(updateTask) } @@ -891,7 +894,7 @@ func (g *DiscoverConnector) RegisterServiceHandler(svcEventHandler *serverconnec // 往队列插入任务 func (g *DiscoverConnector) addFirstTask(updateTask *serviceUpdateTask) error { task := &clientTask{updateTask: updateTask, op: opAddListener} - log.GetBaseLogger().Infof("%s, addFirstTask: start to add first task for %s", + log.GetNetworkLogger().Infof("%s, addFirstTask: start to add first task for %s", g.ServiceConnector.GetSDKContextID(), updateTask) select { case <-g.Done(): @@ -899,7 +902,7 @@ func (g *DiscoverConnector) addFirstTask(updateTask *serviceUpdateTask) error { "RegisterServiceHandler: serverConnector has been destroyed") case g.taskChannel <- task: // 这里先用同步来塞,到时测试下性能,不行的话就改成异步塞 - log.GetBaseLogger().Infof("%s, addFirstTask: finish add first task for %s", + log.GetNetworkLogger().Infof("%s, addFirstTask: finish add first task for %s", g.ServiceConnector.GetSDKContextID(), updateTask) } return nil @@ -915,7 +918,7 @@ func (g *DiscoverConnector) DeRegisterServiceHandler(key *model.ServiceEventKey) task := &clientTask{ updateTask: updateTask, op: opDelListener} - log.GetBaseLogger().Infof("%s, DeRegisterServiceHandler: start to add deregister task %s", + log.GetNetworkLogger().Infof("%s, DeRegisterServiceHandler: start to add deregister task %s", g.ServiceConnector.GetSDKContextID(), updateTask) select { case <-g.Done(): @@ -923,7 +926,7 @@ func (g *DiscoverConnector) DeRegisterServiceHandler(key *model.ServiceEventKey) "DeRegisterServiceHandler: serverConnector has been destroyed") case g.taskChannel <- task: // 这里先用同步来塞,到时测试下性能,不行的话就改成异步塞 - log.GetBaseLogger().Infof("%s, DeRegisterServiceHandler: finish add deregister task %s", + log.GetNetworkLogger().Infof("%s, DeRegisterServiceHandler: finish add deregister task %s", g.ServiceConnector.GetSDKContextID(), updateTask) } return nil @@ -957,14 +960,14 @@ func (g *DiscoverConnector) syncUpdateTask(task *serviceUpdateTask) error { if err != nil { return err } - log.GetBaseLogger().Debugf("sync stream %s created, connection %s, timeout %v", + log.GetNetworkLogger().Debugf("sync stream %s created, connection %s, timeout %v", reqID, connection.ConnID, g.connectionIdleTimeout) var request = task.toDiscoverRequest() task.msgSendTime.Store(curTime) atomic.AddUint64(&task.totalRequests, 1) err = discoverClient.Send(request) if err != nil { - log.GetBaseLogger().Errorf( + log.GetNetworkLogger().Errorf( "fail to send request for service %v, error is %+v", task.ServiceEventKey, err) return err } diff --git a/plugin/servicerouter/filteronly/router.go b/plugin/servicerouter/filteronly/router.go index 17f3db37..abb9513d 100644 --- a/plugin/servicerouter/filteronly/router.go +++ b/plugin/servicerouter/filteronly/router.go @@ -77,7 +77,12 @@ func GetFilteredInstances(ctx model.ValueContext, routeInfo *servicerouter.Route // 至少要返回多少实例 allInstancesCount := clsValue.Count() minInstances := int(percentOfMinInstances * float64(allInstancesCount)) - healthyInstances := clsValue.GetInstancesSet(false, false) + var healthyInstances *model.InstanceSet + if routeInfo.IncludeCircuitBreakInstances { + healthyInstances = clsValue.GetInstancesSet(false, true) + } else { + healthyInstances = clsValue.GetInstancesSet(false, false) + } healthyCount := healthyInstances.Count() hasLimitedInstances := false if recoverAll && allInstancesCount > 0 && healthyCount <= minInstances { @@ -91,6 +96,10 @@ func GetFilteredInstances(ctx model.ValueContext, routeInfo *servicerouter.Route return result, nil } +func checkCircuitBreakerPassing() bool { + return true +} + // init 注册插件 func init() { plugin.RegisterPlugin(&InstancesFilter{}) diff --git a/plugin/servicerouter/rulebase/base.go b/plugin/servicerouter/rulebase/base.go index ea25f2ee..01b4dc0b 100644 --- a/plugin/servicerouter/rulebase/base.go +++ b/plugin/servicerouter/rulebase/base.go @@ -20,13 +20,14 @@ package rulebase import ( "os" "sort" - "strings" regexp "github.com/dlclark/regexp2" "github.com/modern-go/reflect2" apimodel "github.com/polarismesh/specification/source/go/api/v1/model" apitraffic "github.com/polarismesh/specification/source/go/api/v1/traffic_manage" + "google.golang.org/protobuf/types/known/wrapperspb" + "github.com/polarismesh/polaris-go/pkg/algorithm/match" "github.com/polarismesh/polaris-go/pkg/algorithm/rand" "github.com/polarismesh/polaris-go/pkg/log" "github.com/polarismesh/polaris-go/pkg/model" @@ -137,8 +138,7 @@ func (g *RuleBasedInstancesFilter) poolReturnPrioritySubsets(set *prioritySubset // 匹配metadata func (g *RuleBasedInstancesFilter) matchSourceMetadata(ruleMeta map[string]*apimodel.MatchString, - routeInfo *servicerouter.RouteInfo, ruleCache model.RuleCache) (match bool, - invalidRegex string, invalidRegexError error) { + routeInfo *servicerouter.RouteInfo, ruleCache model.RuleCache) (bool, string, error) { var srcMeta map[string]string if routeInfo.SourceService != nil { srcMeta = routeInfo.SourceService.GetMetadata() @@ -147,7 +147,6 @@ func (g *RuleBasedInstancesFilter) matchSourceMetadata(ruleMeta map[string]*apim if len(srcMeta) == 0 { return false, "", nil } - var err error // metadata是否全部匹配 allMetaMatched := true for ruleMetaKey, ruleMetaValue := range ruleMeta { @@ -162,43 +161,16 @@ func (g *RuleBasedInstancesFilter) matchSourceMetadata(ruleMeta map[string]*apim if !exist { return false, "", nil } - switch ruleMetaValue.Type { - case apimodel.MatchString_REGEX: - var matchExp *regexp.Regexp - matchExp, err = regexp.Compile(rawMetaValue, regexp.RE2) + allMetaMatched = match.MatchString(srcMetaValue, &apimodel.MatchString{ + Type: ruleMetaValue.Type, + Value: wrapperspb.String(rawMetaValue), + }, func(s string) *regexp.Regexp { + matchExp, err := regexp.Compile(rawMetaValue, regexp.RE2) if err != nil { - return false, rawMetaValue, err + return nil } - m, err := matchExp.FindStringMatch(srcMetaValue) - if err != nil { - return false, rawMetaValue, err - } - if m == nil || m.String() == "" { - allMetaMatched = false - } - case apimodel.MatchString_NOT_EQUALS: - allMetaMatched = srcMetaValue != rawMetaValue - case apimodel.MatchString_EXACT: - allMetaMatched = srcMetaValue == rawMetaValue - case apimodel.MatchString_IN: - find := false - tokens := strings.Split(rawMetaValue, ",") - for _, token := range tokens { - if token == srcMetaValue { - find = true - break - } - } - allMetaMatched = find - case apimodel.MatchString_NOT_IN: - tokens := strings.Split(rawMetaValue, ",") - for _, token := range tokens { - if token == srcMetaValue { - allMetaMatched = false - break - } - } - } + return matchExp + }) } else { // 假如不存在规则要求的KEY,则直接返回匹配失败 allMetaMatched = false @@ -624,7 +596,7 @@ func (g *RuleBasedInstancesFilter) getRuleFilteredInstances(ruleMatchType int, r } for _, route := range routes { // 匹配source规则 - sourceMatched, match, notMatches, invalidRegex := g.matchSource(route.Sources, routeInfo, ruleMatchType, ruleCache) + sourceMatched, matchSource, notMatches, invalidRegex := g.matchSource(route.Sources, routeInfo, ruleMatchType, ruleCache) if invalidRegex != nil { // summary.invalidRegexSources = append(summary.invalidRegexSources, invalidRegex.invalidRegexes...) @@ -636,7 +608,7 @@ func (g *RuleBasedInstancesFilter) getRuleFilteredInstances(ruleMatchType int, r } if sourceMatched { - summary.matchedSource = append(summary.matchedSource, match) + summary.matchedSource = append(summary.matchedSource, matchSource) } else { // 没有匹配成功,继续下一轮匹配 continue diff --git a/plugin/servicerouter/rulebase/rule.go b/plugin/servicerouter/rulebase/rule.go index 571d9224..da9b2e5c 100644 --- a/plugin/servicerouter/rulebase/rule.go +++ b/plugin/servicerouter/rulebase/rule.go @@ -19,7 +19,6 @@ package rulebase import ( "encoding/json" - "fmt" "sync" "github.com/golang/protobuf/jsonpb" @@ -28,13 +27,17 @@ import ( "github.com/polarismesh/polaris-go/pkg/algorithm/rand" "github.com/polarismesh/polaris-go/pkg/config" - "github.com/polarismesh/polaris-go/pkg/log" "github.com/polarismesh/polaris-go/pkg/model" "github.com/polarismesh/polaris-go/pkg/plugin" "github.com/polarismesh/polaris-go/pkg/plugin/common" "github.com/polarismesh/polaris-go/pkg/plugin/servicerouter" ) +// init 注册插件 +func init() { + plugin.RegisterConfigurablePlugin(&RuleBasedInstancesFilter{}, &RuleRouterConfig{}) +} + // RuleBasedInstancesFilter 基于路由规则的服务实例过滤器 type RuleBasedInstancesFilter struct { *plugin.PluginBase @@ -44,6 +47,7 @@ type RuleBasedInstancesFilter struct { recoverAll bool prioritySubsetPool *sync.Pool systemCfg config.SystemConfig + routerConf *RuleRouterConfig } // Type 插件类型 @@ -66,6 +70,10 @@ func (g *RuleBasedInstancesFilter) Init(ctx *plugin.InitContext) error { g.valueCtx = ctx.ValueCtx g.prioritySubsetPool = &sync.Pool{} g.systemCfg = ctx.Config.GetGlobal().GetSystem() + routerConf := ctx.Config.GetConsumer().GetServiceRouter().GetPluginConfig(g.Name()) + if routerConf != nil { + g.routerConf = routerConf.(*RuleRouterConfig) + } return nil } @@ -144,27 +152,20 @@ finally: case dstRuleSuccess: targetCluster = dstFilteredInstances default: - checkRule := routeInfo.DestService.GetNamespace() + ":" + routeInfo.DestService.GetService() - if ruleStatus == sourceRuleFail { - checkRule = routeInfo.SourceService.GetNamespace() + ":" + routeInfo.SourceService.GetService() + failoverType := routeInfo.FailOverType + if failoverType == nil { + failoverType = &g.routerConf.failoverType + } + if *failoverType == servicerouter.FailOverNone { + emptyCluster := model.NewServiceClusters(model.NewDefaultServiceInstancesWithRegistryValue(model.ServiceInfo{ + Service: withinCluster.GetClusters().GetServiceInstances().GetService(), + Namespace: withinCluster.GetClusters().GetServiceInstances().GetNamespace(), + Metadata: withinCluster.GetClusters().GetServiceInstances().GetMetadata(), + }, withinCluster.GetClusters().GetServiceInstances(), []model.Instance{})) + targetCluster = model.NewCluster(emptyCluster, withinCluster) + } else { + targetCluster = model.NewCluster(clusters, withinCluster) } - // 如果规则匹配失败, 返回错误 - notMatchedSrcText := getSourcesText(summary.notMatchedSources) - matchedSrcText := getSourcesText(summary.matchedSource) - invalidRegexSourceText := getSourcesText(summary.invalidRegexSources) - notMatchedDstText := getNotMatchedDestinationText(summary.notMatchedDestinations) - invalidRegexDstText := getNotMatchedDestinationText(summary.invalidRegexDestinations) - weightZeroDstText := getNotMatchedDestinationText(summary.weightZeroDestinations) - regexCompileErrText := getErrorRegexText(summary.errorRegexes) - errorText := fmt.Sprintf("route rule not match, rule status: %s, sourceService %s, used variables %v,"+ - " dstService %s, notMatchedSource is %s, invalidRegexSource is %s, matchedSource is %s,"+ - " notMatchedDestination is %s, invalidRegexDestination is %s, zeroWeightDestination is %s,"+ - " regexCompileErrors is %s, please check your route rule of service %s", - ruleStatus.String(), model.ToStringService(routeInfo.SourceService, true), routeInfo.EnvironmentVariables, - model.ToStringService(routeInfo.DestService, false), notMatchedSrcText, invalidRegexSourceText, - matchedSrcText, notMatchedDstText, invalidRegexDstText, weightZeroDstText, regexCompileErrText, checkRule) - log.GetBaseLogger().Errorf(errorText) - return nil, model.NewSDKError(model.ErrCodeRouteRuleNotMatch, nil, errorText) } result := servicerouter.PoolGetRouteResult(g.valueCtx) result.OutputCluster = targetCluster @@ -233,7 +234,20 @@ func checkRouteRule(routeRule model.ServiceRule) error { return nil } -// init 注册插件 -func init() { - plugin.RegisterPlugin(&RuleBasedInstancesFilter{}) +type RuleRouterConfig struct { + failoverType servicerouter.FailOverType + FailoverType string `yaml:"failoverType"` +} + +// Verify 校验配置是否OK +func (rc *RuleRouterConfig) Verify() error { + return nil +} + +// SetDefault 对关键值设置默认值 +func (rc *RuleRouterConfig) SetDefault() { + rc.failoverType = servicerouter.FailOverAll + if rc.FailoverType == "none" { + rc.failoverType = servicerouter.FailOverNone + } } diff --git a/polaris.yaml b/polaris.yaml index 2116f3ed..aeefe24b 100644 --- a/polaris.yaml +++ b/polaris.yaml @@ -201,11 +201,6 @@ consumer: #范围:[true: false] #默认值:true startUseFileCache: true - #描述:实例推空保护 - #类型:bool - #范围:[true: false] - #默认值:false - pushEmptyProtection: false #描述:服务路由相关配置 serviceRouter: # 服务路由链 @@ -250,88 +245,12 @@ consumer: #类型:bool #默认值:true enable: true - #描述:实例定时熔断检测周期 - #类型:string - #格式:^\d+(ms|s|m|h)$ - #范围:[100ms:...] - #默认值:30s - checkPeriod: 30s - #描述:熔断器半开后最大允许的请求数 - #类型:int - #范围:[3:...] - #默认值:10 - requestCountAfterHalfOpen: 10 - #描述:熔断器打开后,多久后转换为半开状态 - #类型:string - #格式:^\d+(ms|s|m|h)$ - #范围:[1s:...] - #默认值:30s - sleepWindow: 30s - #描述:熔断器半开到关闭所必须的最少成功请求数 - #类型:int - #范围:[1:requestCountAfterHalfOpen] - #默认值:8 - successCountAfterHalfOpen: 8 - #描述:熔断器半开到关闭的统计周期 - #类型:string - #范围:[10s:...] - #默认值:60s - recoverWindow: 60s - #描述:熔断器半开到关闭的统计滑桶数 - #类型:int - #范围:[1:...] - #默认值:10 - recoverNumBuckets: 10 #描述:熔断策略,SDK会根据策略名称加载对应的熔断器插件 #类型:list #范围:已注册的熔断器插件名 - #默认值:基于周期连续错误数熔断(errorCount)、以及基于周期错误率的熔断策略(errorRate) + #默认值:composite 适配服务/接口/实例 熔断插件 chain: - - errorCount - - errorRate - #描述:熔断插件配置 - plugin: - #描述:基于周期连续错误数熔断策略配置 - errorCount: - #描述:触发连续错误熔断的阈值 - #类型:int - #范围:[1:...] - #默认值:10 - continuousErrorThreshold: 10 - #描述:连续错误数的最小统计单元数量 - #类型:int - #范围:[1:...] - #默认值:10 - metricNumBuckets: 10 - #描述:连续失败的统计周期 - #类型:string - #格式:^\d+(ms|s|m|h)$ - #范围:[10ms:...] - #默认值:1m - metricStatTimeWindow: 1m0s - #描述:基于周期错误率的熔断策略配置 - errorRate: - #描述:触发错误率熔断的阈值 - #类型:double - #范围:(0:1] - #默认值:0.5 - errorRateThreshold: 0.5 - #描述:错误率熔断的最小统计单元数量 - #类型:int - #范围:[1:...] - #默认值:5 - metricNumBuckets: 5 - #描述:错误率熔断的统计周期 - #类型:string - #格式:^\d+(ms|s|m|h)$ - #范围:[1s:...] - #默认值:1m - metricStatTimeWindow: 1m0s - #描述:触发错误率熔断的最低请求阈值 - #类型:int - #范围:(0:...] - #默认值:10 - requestVolumeThreshold: 10 + - composite # 配置中心默认配置 config: # 类型转化缓存的key数量 diff --git a/test/all_suite_test.go b/test/all_suite_test.go index 644982fb..a88b843b 100644 --- a/test/all_suite_test.go +++ b/test/all_suite_test.go @@ -33,7 +33,6 @@ import ( . "gopkg.in/check.v1" "github.com/polarismesh/polaris-go/api" - "github.com/polarismesh/polaris-go/test/circuitbreak" "github.com/polarismesh/polaris-go/test/discover" "github.com/polarismesh/polaris-go/test/loadbalance" "github.com/polarismesh/polaris-go/test/ratelimit" @@ -52,28 +51,28 @@ func Test(t *testing.T) { var ( suitFunc = map[string]func(){ - "ConsumerTestingSuite": mockConsumerTestingSuite, - "ProviderTestingSuite": mockProviderTestingSuite, - "LBTestingSuite": mockLBTestingSuite, - "CircuitBreakSuite": mockCircuitBreakSuite, - "HealthCheckTestingSuite": mockHealthCheckTestingSuite, - "HealthCheckAlwaysTestingSuite": mockHealthCheckAlwaysTestingSuite, - "NearbyTestingSuite": mockNearbyTestingSuite, - "RuleRoutingTestingSuite": mockRuleRoutingTestingSuite, - "DstMetaTestingSuite": mockDstMetaTestingSuite, - "SetDivisionTestingSuite": mockSetDivisionTestingSuite, - "CanaryTestingSuite": mockCanaryTestingSuite, - "CacheTestingSuite": mockCacheTestingSuite, - "ServiceUpdateSuite": mockServiceUpdateSuite, - "ServerSwitchSuite": mockServerSwitchSuite, - "DefaultServerSuite": mockDefaultServerSuite, - "CacheFastUpdateSuite": mockCacheFastUpdateSuite, - "ServerFailOverSuite": mockServerFailOverSuite, - "EventSubscribeSuit": mockEventSubscribeSuit, - "InnerServiceLBTestingSuite": mockInnerServiceLBTestingSuite, - "LocalNormalTestingSuite": mockLocalNormalTestingSuite, - "RuleChangeTestingSuite": func() {}, - "RemoteNormalTestingSuite": func() {}, + "ConsumerTestingSuite": mockConsumerTestingSuite, + "ProviderTestingSuite": mockProviderTestingSuite, + "LBTestingSuite": mockLBTestingSuite, + // "CircuitBreakSuite": mockCircuitBreakSuite, + // "HealthCheckTestingSuite": mockHealthCheckTestingSuite, + // "HealthCheckAlwaysTestingSuite": mockHealthCheckAlwaysTestingSuite, + "NearbyTestingSuite": mockNearbyTestingSuite, + "RuleRoutingTestingSuite": mockRuleRoutingTestingSuite, + "DstMetaTestingSuite": mockDstMetaTestingSuite, + "SetDivisionTestingSuite": mockSetDivisionTestingSuite, + "CanaryTestingSuite": mockCanaryTestingSuite, + "CacheTestingSuite": mockCacheTestingSuite, + "ServiceUpdateSuite": mockServiceUpdateSuite, + "ServerSwitchSuite": mockServerSwitchSuite, + "DefaultServerSuite": mockDefaultServerSuite, + "CacheFastUpdateSuite": mockCacheFastUpdateSuite, + "ServerFailOverSuite": mockServerFailOverSuite, + "EventSubscribeSuit": mockEventSubscribeSuit, + "InnerServiceLBTestingSuite": mockInnerServiceLBTestingSuite, + "LocalNormalTestingSuite": mockLocalNormalTestingSuite, + "RuleChangeTestingSuite": func() {}, + "RemoteNormalTestingSuite": func() {}, } ) @@ -140,17 +139,17 @@ func mockInnerServiceLBTestingSuite() { Suite(&loadbalance.InnerServiceLBTestingSuite{}) } -func mockCircuitBreakSuite() { - Suite(&circuitbreak.CircuitBreakSuite{}) -} +// func mockCircuitBreakSuite() { +// Suite(&circuitbreak.CircuitBreakSuite{}) +// } -func mockHealthCheckTestingSuite() { - Suite(&circuitbreak.HealthCheckTestingSuite{}) -} +// func mockHealthCheckTestingSuite() { +// Suite(&circuitbreak.HealthCheckTestingSuite{}) +// } -func mockHealthCheckAlwaysTestingSuite() { - Suite(&circuitbreak.HealthCheckAlwaysTestingSuite{}) -} +// func mockHealthCheckAlwaysTestingSuite() { +// Suite(&circuitbreak.HealthCheckAlwaysTestingSuite{}) +// } func mockNearbyTestingSuite() { Suite(&serviceroute.NearbyTestingSuite{}) @@ -169,7 +168,7 @@ func mockSetDivisionTestingSuite() { } func mockCanaryTestingSuite() { - Suite(&serviceroute.CanaryTestingSuite{}) + // Suite(&serviceroute.CanaryTestingSuite{}) } // 缓存持久化测试 diff --git a/test/circuitbreak/circuitbreak_suite.go b/test/circuitbreak/circuitbreak_suite.go deleted file mode 100644 index d6a3f3cd..00000000 --- a/test/circuitbreak/circuitbreak_suite.go +++ /dev/null @@ -1,1121 +0,0 @@ -/** - * 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 circuitbreak - -import ( - "fmt" - log2 "log" - "net" - "os" - "sync" - "time" - - "github.com/golang/protobuf/proto" - "github.com/golang/protobuf/ptypes/wrappers" - "github.com/google/uuid" - apimodel "github.com/polarismesh/specification/source/go/api/v1/model" - "github.com/polarismesh/specification/source/go/api/v1/service_manage" - "google.golang.org/grpc" - "gopkg.in/check.v1" - - "github.com/polarismesh/polaris-go/api" - "github.com/polarismesh/polaris-go/pkg/config" - "github.com/polarismesh/polaris-go/pkg/log" - "github.com/polarismesh/polaris-go/pkg/model" - commontest "github.com/polarismesh/polaris-go/test/common" - "github.com/polarismesh/polaris-go/test/mock" - "github.com/polarismesh/polaris-go/test/util" -) - -const ( - cbNS = "cbns" - cbSVC = "cbsvc" - cbIP = "127.0.0.1" - cbPORT = commontest.CircuitBreakSuitServerPort - cbInstanceCount = 50 -) - -// CircuitBreakSuite 熔断测试套件 -type CircuitBreakSuite struct { - grpcServer *grpc.Server - grpcListener net.Listener - serviceToken string - testService *service_manage.Service - mockServer mock.NamingServer -} - -// SetUpSuite 初始化套件 -func (t *CircuitBreakSuite) SetUpSuite(c *check.C) { - grpcOptions := make([]grpc.ServerOption, 0) - maxStreams := 100000 - grpcOptions = append(grpcOptions, grpc.MaxConcurrentStreams(uint32(maxStreams))) - grpc.EnableTracing = true - - var err error - t.grpcServer = grpc.NewServer(grpcOptions...) - t.serviceToken = uuid.New().String() - t.mockServer = mock.NewNamingServer() - // 注册系统服务 - t.mockServer.RegisterServerServices(cbIP, cbPORT) - - t.mockServer.RegisterRouteRule(&service_manage.Service{ - Name: &wrappers.StringValue{Value: config.ServerMonitorService}, - Namespace: &wrappers.StringValue{Value: config.ServerNamespace}}, - t.mockServer.BuildRouteRule(config.ServerNamespace, config.ServerMonitorService)) - - t.mockServer.RegisterNamespace(&apimodel.Namespace{ - Name: &wrappers.StringValue{Value: cbNS}, - Comment: &wrappers.StringValue{Value: "for circuiebreak test"}, - Owners: &wrappers.StringValue{Value: "Circuitbreaker"}, - }) - t.testService = &service_manage.Service{ - Name: &wrappers.StringValue{Value: cbSVC}, - Namespace: &wrappers.StringValue{Value: cbNS}, - Token: &wrappers.StringValue{Value: t.serviceToken}, - } - t.mockServer.RegisterService(t.testService) - t.mockServer.GenTestInstances(t.testService, cbInstanceCount) - - service_manage.RegisterPolarisGRPCServer(t.grpcServer, t.mockServer) - - t.grpcListener, err = net.Listen("tcp", fmt.Sprintf("%s:%d", cbIP, cbPORT)) - c.Assert(err, check.IsNil) - log2.Printf("appserver listening on %s:%d\n", cbIP, cbPORT) - go func() { - if err := t.grpcServer.Serve(t.grpcListener); err != nil { - panic(err) - } - }() - waitServerReady(cbPORT) -} - -// GetName 套件名字 -func (t *CircuitBreakSuite) GetName() string { - return "CircuitBreak" -} - -// TearDownSuite 销毁套件 -func (t *CircuitBreakSuite) TearDownSuite(c *check.C) { - t.grpcServer.GracefulStop() - if util.DirExist(util.BackupDir) { - os.RemoveAll(util.BackupDir) - } - util.InsertLog(t, c.GetTestLog()) -} - -// TestErrCount 测试利用err_Count熔断器熔断实例 -func (t *CircuitBreakSuite) TestErrCount(c *check.C) { - t.mockServer.SetPrintDiscoverReturn(true) - t.testCircuitBreakByInstance(c, "errorCount", true) -} - -// 测试单实例连续错误数熔断 -func (t *CircuitBreakSuite) testErrCountByInstance( - c *check.C, targetInstance model.Instance, consumerAPI api.ConsumerAPI) { - log.GetBaseLogger().Debugf( - "The instance to ciucuitbreak by errcount: %s%d", targetInstance.GetHost(), targetInstance.GetPort()) - // 按目前的配置,连续十次上报一个实例出错, - // 那么就会触发err_count熔断器将其变为熔断开启状态 - var failCode int32 = 1 - for i := 0; i < 10; i++ { - consumerAPI.UpdateServiceCallResult( - &api.ServiceCallResult{ - ServiceCallResult: *((&model.ServiceCallResult{ - CalledInstance: targetInstance, - RetStatus: model.RetFail, - RetCode: &failCode}).SetDelay(20 * time.Millisecond))}) - } - time.Sleep(2 * time.Second) - t.openToHalfOpen(targetInstance, 20*time.Second, c, consumerAPI) - t.halfOpenToOpen(targetInstance, consumerAPI, c) - t.openToHalfOpen(targetInstance, 20*time.Second, c, consumerAPI) - t.halfOpenToClose(targetInstance, consumerAPI, c) -} - -// TestErrRate 测试利用err_rate熔断器熔断实例 -func (t *CircuitBreakSuite) TestErrRate(c *check.C) { - t.testCircuitBreakByInstance(c, "errorRate", true) -} - -func (t *CircuitBreakSuite) testCircuitBreakByInstance(c *check.C, cbWay string, oneInstance bool) { - defer util.DeleteDir(util.BackupDir) - cfg, err := config.LoadConfigurationByFile("testdata/circuitbreaker.yaml") - c.Assert(err, check.IsNil) - sdkCtx, err := api.InitContextByConfig(cfg) - c.Assert(err, check.IsNil) - consumerAPI := api.NewConsumerAPIByContext(sdkCtx) - defer consumerAPI.Destroy() - var targetInstance model.Instance - // 随机获取一个实例,并将这个实例作为熔断的目标 - if oneInstance { - request := &api.GetOneInstanceRequest{} - request.FlowID = 1111 - request.Namespace = cbNS - request.Service = cbSVC - request.Timeout = model.ToDurationPtr(2 * time.Second) - resp, err := consumerAPI.GetOneInstance(request) - c.Assert(err, check.IsNil) - c.Assert(len(resp.Instances), check.Equals, 1) - targetInstance = resp.Instances[0] - } else { - request := &api.GetInstancesRequest{} - request.FlowID = 1111 - request.Namespace = cbNS - request.Service = cbSVC - request.Timeout = model.ToDurationPtr(2 * time.Second) - resp, err := consumerAPI.GetInstances(request) - c.Assert(err, check.IsNil) - targetInstance = resp.Instances[0] - } - c.Assert(cbWay == "errorCount" || cbWay == "errorRate", check.Equals, true) - - switch cbWay { - case "errorCount": - t.testErrCountByInstance(c, targetInstance, consumerAPI) - case "errorRate": - t.testErrRateByInstance(c, targetInstance, consumerAPI) - } -} - -// 测试单实例错误率熔断 -func (t *CircuitBreakSuite) testErrRateByInstance( - c *check.C, targetInstance model.Instance, consumerAPI api.ConsumerAPI) { - log.GetBaseLogger().Debugf( - "The instance to ciucuitbreak by errRate: %s%d", targetInstance.GetHost(), targetInstance.GetPort()) - var failCode int32 = 1 - var successCode int32 = 0 - for i := 0; i < 6; i++ { - consumerAPI.UpdateServiceCallResult( - &api.ServiceCallResult{ - ServiceCallResult: *((&model.ServiceCallResult{ - CalledInstance: targetInstance, - RetStatus: model.RetSuccess, - RetCode: &successCode}).SetDelay(20 * time.Millisecond))}) - consumerAPI.UpdateServiceCallResult( - &api.ServiceCallResult{ - ServiceCallResult: *((&model.ServiceCallResult{ - CalledInstance: targetInstance, - RetStatus: model.RetFail, - RetCode: &failCode}).SetDelay(20 * time.Millisecond))}) - } - time.Sleep(2 * time.Second) - - t.openToHalfOpen(targetInstance, 60*time.Second, c, consumerAPI) - t.halfOpenToOpen(targetInstance, consumerAPI, c) - t.openToHalfOpen(targetInstance, 60*time.Second, c, consumerAPI) - t.halfOpenToClose(targetInstance, consumerAPI, c) -} - -// CheckInstanceAvailable 检查实例是否可用 -func CheckInstanceAvailable(c *check.C, consumerAPI api.ConsumerAPI, targetIns model.Instance, - available bool, namespace string, service string) { - request1 := &api.GetInstancesRequest{} - request1.FlowID = 1111 - request1.Namespace = namespace - request1.Service = service - request1.Timeout = model.ToDurationPtr(2 * time.Second) - resp1, err := consumerAPI.GetInstances(request1) - c.Assert(err, check.IsNil) - hasTargetInst := false - for _, ins := range resp1.GetInstances() { - if ins.GetId() == targetIns.GetId() { - hasTargetInst = true - break - } - } - if available { - c.Assert(hasTargetInst, check.Equals, true) - } else { - c.Assert(hasTargetInst, check.Equals, false) - } -} - -// 熔断到半开 -func (t *CircuitBreakSuite) openToHalfOpen(openInstance model.Instance, cbDuration time.Duration, c *check.C, - consumerAPI api.ConsumerAPI) { - c.Assert(openInstance.GetCircuitBreakerStatus(), check.NotNil) - c.Assert(openInstance.GetCircuitBreakerStatus().GetStatus() == model.Open, check.Equals, true) - CheckInstanceAvailable(c, consumerAPI, openInstance, false, cbNS, cbSVC) - - openTime := openInstance.GetCircuitBreakerStatus().GetStartTime() - halfOpenTime := openTime.Add(cbDuration) - fmt.Printf("To be halfopen in %v\n", halfOpenTime) - time.Sleep(time.Until(halfOpenTime) + 2*time.Second) - c.Assert(openInstance.GetCircuitBreakerStatus().GetStatus(), check.Equals, model.HalfOpen) - // CheckInstanceAvailable(c, consumerAPI, openInstance, true, cbNS, cbSVC) -} - -// 半开到熔断 -func (t *CircuitBreakSuite) halfOpenToOpen(openInstance model.Instance, consumerAPI api.ConsumerAPI, c *check.C) { - failCode := int32(1) - log.GetBaseLogger().Debugf("start report fail for open instance %s", openInstance.GetId()) - log2.Printf("instance id: %s, circuit break status: %v", openInstance.GetId(), openInstance.GetCircuitBreakerStatus()) - for i := 0; i < 2; i++ { - consumerAPI.UpdateServiceCallResult(&api.ServiceCallResult{ServiceCallResult: model.ServiceCallResult{ - CalledInstance: openInstance, - RetStatus: model.RetFail, - RetCode: &failCode, - Delay: model.ToDurationPtr(1 * time.Second)}}) - time.Sleep(10 * time.Millisecond) - } - time.Sleep(10 * time.Second) - log2.Printf("circuit break status: %v", openInstance.GetCircuitBreakerStatus()) - time.Sleep(500 * time.Millisecond) - successCode := int32(0) - consumerAPI.UpdateServiceCallResult(&api.ServiceCallResult{ServiceCallResult: model.ServiceCallResult{ - CalledInstance: openInstance, - RetStatus: model.RetSuccess, - RetCode: &successCode, - Delay: model.ToDurationPtr(1 * time.Second)}}) - time.Sleep(2 * time.Second) - // localValues = regPlug.GetInstanceLocalValue(openInstance.GetId()) - c.Assert(openInstance.GetCircuitBreakerStatus().GetStatus(), check.Equals, model.Open) - // c.Assert(localValues, check.NotNil) - CheckInstanceAvailable(c, consumerAPI, openInstance, false, cbNS, cbSVC) -} - -// 半开到关闭 -func (t *CircuitBreakSuite) halfOpenToClose(openInstance model.Instance, consumerAPI api.ConsumerAPI, c *check.C) { - successCode := int32(0) - for i := 0; i < 3; i++ { - log2.Printf("report success for instance %s", openInstance.GetId()) - util.SelectInstanceSpecificNum(c, consumerAPI, openInstance, 1, 2000) - consumerAPI.UpdateServiceCallResult(&api.ServiceCallResult{ServiceCallResult: model.ServiceCallResult{ - CalledInstance: openInstance, - RetStatus: model.RetSuccess, - RetCode: &successCode, - Delay: model.ToDurationPtr(1 * time.Second)}}) - } - time.Sleep(500 * time.Millisecond) - time.Sleep(10 * time.Second) - c.Assert(openInstance.GetCircuitBreakerStatus().GetStatus(), check.Equals, model.Close) - CheckInstanceAvailable(c, consumerAPI, openInstance, true, cbNS, cbSVC) -} - -// 通过默认配置来进行熔断测试 -func (t *CircuitBreakSuite) testCircuitBreakByDefault(skipRouter bool, c *check.C) { - fmt.Printf("TestCircuitBreakByDefault(skipRouter is %v) started\n", skipRouter) - defer util.DeleteDir(util.BackupDir) - cfg := config.NewDefaultConfiguration([]string{fmt.Sprintf("%s:%d", cbIP, cbPORT)}) - // enableStat := false - period := 10 * time.Second - // cfg.Global.StatReporter.Enable = &enableStat - cfg.Consumer.LocalCache.PersistDir = "testdata/backup" - cfg.Consumer.CircuitBreaker.CheckPeriod = &period - var err error - var consumerAPI api.ConsumerAPI - consumerAPI, err = api.NewConsumerAPIByConfig(cfg) - c.Assert(err, check.IsNil) - defer consumerAPI.Destroy() - // 等待直到完成首次地域信息拉取 - time.Sleep(1 * time.Second) - // 随机获取一个实例,并将这个实例作为熔断的目标 - request := &api.GetInstancesRequest{} - request.FlowID = 1111 - request.Namespace = cbNS - request.Service = cbSVC - request.SkipRouteFilter = skipRouter - var response *model.InstancesResponse - response, err = consumerAPI.GetInstances(request) - c.Assert(err, check.IsNil) - c.Assert(len(response.Instances) > 0, check.Equals, true) - fmt.Printf("GetInstances: first count is %d\n", len(response.Instances)) - firstInstance := response.Instances[0] - var addressToLimit = fmt.Sprintf("%s:%d", firstInstance.GetHost(), firstInstance.GetPort()) - // 运行30s - deadline := time.Now().Add(45 * time.Second) - var firstCount = len(response.Instances) - // 检查是否发生了熔断 - var hasLimited = false - // 检查是否进行了熔断恢复 - var hasResume = false - for { - if time.Now().After(deadline) { - break - } - if len(response.Instances) == firstCount-1 && !hasLimited { - fmt.Printf("instance has been limited for service %s::%s\n", request.Namespace, request.Service) - hasLimited = true - } - if len(response.Instances) == firstCount && hasLimited && !hasResume { - fmt.Printf("instance has been resumed for service %s::%s\n", request.Namespace, request.Service) - hasResume = true - } - for _, instance := range response.Instances { - address := fmt.Sprintf("%s:%d", instance.GetHost(), instance.GetPort()) - var callResult = &api.ServiceCallResult{} - if address == addressToLimit && !hasLimited { - // fmt.Printf("report fail for %s:%d\n", instance.GetHost(), instance.GetPort()) - callResult.RetStatus = model.RetFail - callResult.Delay = model.ToDurationPtr(1000 * time.Millisecond) - callResult.RetCode = proto.Int32(-1) - callResult.CalledInstance = instance - } else { - // fmt.Printf("report success for %s:%d\n", instance.GetHost(), instance.GetPort()) - callResult.RetStatus = model.RetSuccess - callResult.Delay = model.ToDurationPtr(10 * time.Millisecond) - callResult.RetCode = proto.Int32(0) - callResult.CalledInstance = instance - } - err = consumerAPI.UpdateServiceCallResult(callResult) - c.Assert(err, check.IsNil) - } - time.Sleep(50 * time.Millisecond) - response, err = consumerAPI.GetInstances(request) - c.Assert(err, check.IsNil) - // fmt.Printf("GetInstances: count is %d\n", len(response.Instances)) - } - c.Assert(hasLimited, check.Equals, true) - c.Assert(hasResume, check.Equals, true) - fmt.Printf("TestCircuitBreakByDefault(skipRouter is %v) terminated\n", skipRouter) -} - -// TestCircuitBreakByDefault 通过默认配置来进行熔断测试 -func (t *CircuitBreakSuite) TestCircuitBreakByDefault(c *check.C) { - t.testCircuitBreakByDefault(false, c) -} - -// TestErrCountByGetInstances 测试通过获取多个实例接口分配的实例也可以进行连续错误数熔断半开状态转换 -func (t *CircuitBreakSuite) TestErrCountByGetInstances(c *check.C) { - t.testCircuitBreakByInstance(c, "errorCount", false) -} - -// TestErrRateByGetInstances 测试通过获取多个实例接口分配的实例也可以进行错误率熔断半开状态转换 -func (t *CircuitBreakSuite) TestErrRateByGetInstances(c *check.C) { - t.testCircuitBreakByInstance(c, "errorRate", false) -} - -// TestErrCountTriggerOpenThreshold 连续错误熔断的阈值 测试 -func (t *CircuitBreakSuite) TestErrCountTriggerOpenThreshold(c *check.C) { - fmt.Println("--TestErrCountTriggerOpenNum") - defer util.DeleteDir(util.BackupDir) - cfg := config.NewDefaultConfiguration([]string{fmt.Sprintf("%s:%d", cbIP, cbPORT)}) - // enableStat := false - period := 2 * time.Second - // cfg.Global.StatReporter.Enable = &enableStat - cfg.Consumer.LocalCache.PersistDir = "testdata/backup" - cfg.Consumer.CircuitBreaker.CheckPeriod = &period - cfg.Consumer.GetCircuitBreaker().GetErrorCountConfig().SetContinuousErrorThreshold(20) - cfg.Consumer.GetCircuitBreaker().GetErrorRateConfig().SetErrorRatePercent(100) - - var err error - var consumerAPI api.ConsumerAPI - consumerAPI, err = api.NewConsumerAPIByConfig(cfg) - c.Assert(err, check.IsNil) - defer consumerAPI.Destroy() - // 等待直到完成首次地域信息拉取 - time.Sleep(time.Second * 1) - - // 随机获取一个实例,并将这个实例作为熔断的目标 - request := &api.GetInstancesRequest{} - request.FlowID = 1111 - request.Namespace = cbNS - request.Service = cbSVC - request.SkipRouteFilter = false - var response *model.InstancesResponse - response, err = consumerAPI.GetInstances(request) - c.Assert(err, check.IsNil) - c.Assert(len(response.Instances) > 0, check.Equals, true) - fmt.Printf("GetInstances: first count is %d\n", len(response.Instances)) - targetIns := response.Instances[0] - - var callResult = &api.ServiceCallResult{} - callResult.RetStatus = model.RetFail - callResult.Delay = model.ToDurationPtr(1000 * time.Millisecond) - callResult.RetCode = proto.Int32(-1) - callResult.CalledInstance = targetIns - - var callResultOk = &api.ServiceCallResult{} - callResultOk.RetStatus = model.RetSuccess - callResultOk.Delay = model.ToDurationPtr(1000 * time.Millisecond) - callResultOk.RetCode = proto.Int32(-1) - callResultOk.CalledInstance = targetIns - - for i := 0; i < 10; i++ { - err := consumerAPI.UpdateServiceCallResult(callResult) - c.Assert(err, check.IsNil) - for j := 0; j < 3; j++ { - err := consumerAPI.UpdateServiceCallResult(callResultOk) - c.Assert(err, check.IsNil) - } - } - time.Sleep(time.Second * 5) - if targetIns.GetCircuitBreakerStatus() != nil { - c.Assert(targetIns.GetCircuitBreakerStatus().GetStatus(), check.Equals, model.Close) - } - CheckInstanceAvailable(c, consumerAPI, targetIns, true, cbNS, cbSVC) - - for i := 0; i < 2000; i++ { - err := consumerAPI.UpdateServiceCallResult(callResult) - c.Assert(err, check.IsNil) - } - time.Sleep(time.Second * 5) - c.Assert(targetIns.GetCircuitBreakerStatus(), check.NotNil) - c.Assert(targetIns.GetCircuitBreakerStatus().GetStatus(), check.Equals, model.Open) - CheckInstanceAvailable(c, consumerAPI, targetIns, false, cbNS, cbSVC) -} - -// TestErrRateTriggerOpenThreshold 触发错误率熔断的阈值 测试 -func (t *CircuitBreakSuite) TestErrRateTriggerOpenThreshold(c *check.C) { - fmt.Println("--TestErrRateTriggerOpenThreshold") - defer util.DeleteDir(util.BackupDir) - cfg := config.NewDefaultConfiguration([]string{fmt.Sprintf("%s:%d", cbIP, cbPORT)}) - // enableStat := false - period := 2 * time.Second - // cfg.Global.StatReporter.Enable = &enableStat - cfg.Consumer.LocalCache.PersistDir = "testdata/backup" - cfg.Consumer.CircuitBreaker.CheckPeriod = &period - cfg.Consumer.GetCircuitBreaker().GetErrorCountConfig().SetContinuousErrorThreshold(20) - cfg.Consumer.GetCircuitBreaker().GetErrorRateConfig().SetErrorRatePercent(60) - - var err error - var consumerAPI api.ConsumerAPI - consumerAPI, err = api.NewConsumerAPIByConfig(cfg) - c.Assert(err, check.IsNil) - defer consumerAPI.Destroy() - // 等待直到完成首次地域信息拉取 - time.Sleep(time.Second * 1) - - // 随机获取一个实例,并将这个实例作为熔断的目标 - request := &api.GetInstancesRequest{} - request.FlowID = 1111 - request.Namespace = cbNS - request.Service = cbSVC - request.SkipRouteFilter = false - var response *model.InstancesResponse - response, err = consumerAPI.GetInstances(request) - c.Assert(err, check.IsNil) - c.Assert(len(response.Instances) > 0, check.Equals, true) - fmt.Printf("GetInstances: first count is %d\n", len(response.Instances)) - targetIns := response.Instances[0] - - var callResult = &api.ServiceCallResult{} - callResult.RetStatus = model.RetFail - callResult.Delay = model.ToDurationPtr(1000 * time.Millisecond) - callResult.RetCode = proto.Int(-1) - callResult.CalledInstance = targetIns - - var callResultOk = &api.ServiceCallResult{} - callResultOk.RetStatus = model.RetSuccess - callResultOk.Delay = model.ToDurationPtr(1000 * time.Millisecond) - callResultOk.RetCode = proto.Int(-1) - callResultOk.CalledInstance = targetIns - - for i := 0; i < 10; i++ { - err := consumerAPI.UpdateServiceCallResult(callResult) - c.Assert(err, check.IsNil) - - err = consumerAPI.UpdateServiceCallResult(callResultOk) - c.Assert(err, check.IsNil) - } - time.Sleep(time.Second * 5) - if targetIns.GetCircuitBreakerStatus() != nil { - c.Assert(targetIns.GetCircuitBreakerStatus().GetStatus(), check.Equals, model.Close) - } - CheckInstanceAvailable(c, consumerAPI, targetIns, true, cbNS, cbSVC) - - for i := 0; i < 10; i++ { - err := consumerAPI.UpdateServiceCallResult(callResult) - c.Assert(err, check.IsNil) - } - time.Sleep(time.Second * 5) - c.Assert(targetIns.GetCircuitBreakerStatus(), check.NotNil) - c.Assert(targetIns.GetCircuitBreakerStatus().GetStatus(), check.Equals, model.Open) - CheckInstanceAvailable(c, consumerAPI, targetIns, false, cbNS, cbSVC) -} - -// TestSleepWindow 设置熔断器的睡眠窗口 测试 -// 熔断sleepWindow测试, 不启用探测 -// 测试半开后最大可以获取实例的次数 -// 熔断器半开后恢复所需成功探测数测试 -func (t *CircuitBreakSuite) TestSleepWindow(c *check.C) { - fmt.Println("--TestSleepWindow") - defer util.DeleteDir(util.BackupDir) - cfg := config.NewDefaultConfiguration([]string{fmt.Sprintf("%s:%d", cbIP, cbPORT)}) - // enableStat := false - period := 1 * time.Second - // cfg.Global.StatReporter.Enable = &enableStat - cfg.Consumer.LocalCache.PersistDir = "testdata/backup" - cfg.Consumer.CircuitBreaker.CheckPeriod = &period - cfg.Consumer.GetCircuitBreaker().GetErrorCountConfig().SetContinuousErrorThreshold(10) - cfg.Consumer.GetCircuitBreaker().GetErrorCountConfig().SetMetricStatTimeWindow(time.Second * 10) - cfg.Consumer.GetCircuitBreaker().GetErrorRateConfig().SetErrorRatePercent(100) - cfg.Consumer.GetCircuitBreaker().SetSleepWindow(time.Second * 10) - cfg.Consumer.GetCircuitBreaker().SetRequestCountAfterHalfOpen(3) - cfg.Consumer.GetCircuitBreaker().SetSuccessCountAfterHalfOpen(2) - - var err error - var consumerAPI api.ConsumerAPI - consumerAPI, err = api.NewConsumerAPIByConfig(cfg) - c.Assert(err, check.IsNil) - defer consumerAPI.Destroy() - // 等待直到完成首次地域信息拉取 - time.Sleep(time.Second * 1) - - // 随机获取一个实例,并将这个实例作为熔断的目标 - request := &api.GetInstancesRequest{} - request.FlowID = 1111 - request.Namespace = cbNS - request.Service = cbSVC - request.SkipRouteFilter = false - var response *model.InstancesResponse - response, err = consumerAPI.GetInstances(request) - c.Assert(err, check.IsNil) - c.Assert(len(response.Instances) > 0, check.Equals, true) - fmt.Printf("GetInstances: first count is %d\n", len(response.Instances)) - targetIns := response.Instances[0] - - var callResult = &api.ServiceCallResult{} - callResult.RetStatus = model.RetFail - callResult.Delay = model.ToDurationPtr(1000 * time.Millisecond) - callResult.RetCode = proto.Int(-1) - callResult.CalledInstance = targetIns - for i := 0; i < 15; i++ { - err := consumerAPI.UpdateServiceCallResult(callResult) - c.Assert(err, check.IsNil) - } - time.Sleep(time.Second * 2) - c.Assert(targetIns.GetCircuitBreakerStatus().GetStatus(), check.Equals, model.Open) - CheckInstanceAvailable(c, consumerAPI, targetIns, false, cbNS, cbSVC) - time.Sleep(time.Second * 5) - c.Assert(targetIns.GetCircuitBreakerStatus().GetStatus(), check.Equals, model.Open) - CheckInstanceAvailable(c, consumerAPI, targetIns, false, cbNS, cbSVC) - time.Sleep(time.Second * 6) - c.Assert(targetIns.GetCircuitBreakerStatus().GetStatus(), check.Equals, model.HalfOpen) - CheckInstanceAvailable(c, consumerAPI, targetIns, true, cbNS, cbSVC) - - // 测试半开后最大可以获取实例的次数 - request1 := &api.GetOneInstanceRequest{} - request1.FlowID = 1111 - request1.Namespace = cbNS - request1.Service = cbSVC - useNum := 0 - for i := 0; i < 10000; i++ { - response, err := consumerAPI.GetOneInstance(request1) - c.Assert(err, check.IsNil) - c.Assert(len(response.Instances) > 0, check.Equals, true) - if response.Instances[0].GetId() == targetIns.GetId() { - useNum++ - } - } - log2.Printf("useNum: %d", useNum) - c.Assert(useNum <= 3, check.Equals, true) - - // 熔断器半开后恢复所需成功探测数测试 - var callResultOk = &api.ServiceCallResult{} - callResultOk.RetStatus = model.RetSuccess - callResultOk.Delay = model.ToDurationPtr(10 * time.Millisecond) - callResultOk.RetCode = proto.Int(0) - callResultOk.CalledInstance = targetIns - err = consumerAPI.UpdateServiceCallResult(callResultOk) - c.Assert(err, check.IsNil) - time.Sleep(time.Second * 2) - c.Assert(targetIns.GetCircuitBreakerStatus().GetStatus(), check.Equals, model.HalfOpen) - - for i := 0; i < 2; i++ { - err = consumerAPI.UpdateServiceCallResult(callResultOk) - c.Assert(err, check.IsNil) - } - time.Sleep(time.Second * 2) - fmt.Println("targetId ", targetIns.GetId()) - c.Assert(targetIns.GetCircuitBreakerStatus().GetStatus(), check.Equals, model.Close) - CheckInstanceAvailable(c, consumerAPI, targetIns, true, cbNS, cbSVC) -} - -func (t *CircuitBreakSuite) addInstance(srService string, srNamespace string, srIPAddr string, srPort uint32, health bool) { - location := &apimodel.Location{ - Region: &wrappers.StringValue{Value: "A"}, - Zone: &wrappers.StringValue{Value: "a"}, - Campus: &wrappers.StringValue{Value: "1"}, - } - ins := &service_manage.Instance{ - Id: &wrappers.StringValue{Value: uuid.New().String()}, - Service: &wrappers.StringValue{Value: srService}, - Namespace: &wrappers.StringValue{Value: srNamespace}, - Host: &wrappers.StringValue{Value: srIPAddr}, - Port: &wrappers.UInt32Value{Value: uint32(srPort)}, - Weight: &wrappers.UInt32Value{Value: 100}, - Healthy: &wrappers.BoolValue{Value: health}, - Location: location} - testService := &service_manage.Service{ - Name: &wrappers.StringValue{Value: srService}, - Namespace: &wrappers.StringValue{Value: srNamespace}, - Token: &wrappers.StringValue{Value: t.serviceToken}, - } - t.mockServer.RegisterServiceInstances(testService, []*service_manage.Instance{ins}) -} - -// TestAllCircuitBreaker 测试所有熔断器 -// 全部熔断后测试 -// 全部熔断后优先获取熔断实例 -// 全部不健康,可以触发全死全活 -func (t *CircuitBreakSuite) TestAllCircuitBreaker(c *check.C) { - fmt.Println("--TestAllCircuitBreaker") - defer util.DeleteDir(util.BackupDir) - cfg := config.NewDefaultConfiguration([]string{fmt.Sprintf("%s:%d", cbIP, cbPORT)}) - // enableStat := false - period := 1 * time.Second - // cfg.Global.StatReporter.Enable = &enableStat - cfg.Consumer.LocalCache.PersistDir = "testdata/backup" - cfg.Consumer.CircuitBreaker.CheckPeriod = &period - cfg.Consumer.GetCircuitBreaker().GetErrorCountConfig().SetContinuousErrorThreshold(10) - - var err error - var consumerAPI api.ConsumerAPI - consumerAPI, err = api.NewConsumerAPIByConfig(cfg) - c.Assert(err, check.IsNil) - defer consumerAPI.Destroy() - // 等待直到完成首次地域信息拉取 - time.Sleep(time.Second * 1) - - t.testService = &service_manage.Service{ - Name: &wrappers.StringValue{Value: "cbTest1"}, - Namespace: &wrappers.StringValue{Value: cbNS}, - Token: &wrappers.StringValue{Value: t.serviceToken}, - } - t.mockServer.RegisterService(t.testService) - t.addInstance("cbTest1", cbNS, "127.0.0.1", 1234, true) - t.addInstance("cbTest1", cbNS, "127.0.0.1", 1235, false) - - defer t.mockServer.ClearServiceInstances(t.testService) - request1 := &api.GetOneInstanceRequest{} - request1.FlowID = 1111 - request1.Namespace = cbNS - request1.Service = "cbTest1" - // 获取的实例是 t.addInstance("cbTest1", cbNS, "127.0.0.1", 1234, true) - response, err := consumerAPI.GetOneInstance(request1) - c.Assert(err, check.IsNil) - c.Assert(len(response.Instances), check.Equals, 1) - targetIns := response.GetInstances()[0] - c.Assert("127.0.0.1:1234", check.Equals, fmt.Sprintf("%s:%d", targetIns.GetHost(), targetIns.GetPort())) - - // 熔断 - var callResult = &api.ServiceCallResult{} - callResult.RetStatus = model.RetFail - callResult.Delay = model.ToDurationPtr(1000 * time.Millisecond) - callResult.RetCode = proto.Int(-1) - callResult.CalledInstance = targetIns - // 触发目标实例发生熔断 - for i := 0; i < 15; i++ { - err := consumerAPI.UpdateServiceCallResult(callResult) - c.Assert(err, check.IsNil) - } - time.Sleep(time.Second * 2) - c.Assert(targetIns.GetCircuitBreakerStatus().GetStatus(), check.Equals, model.Open) - for i := 0; i < 10; i++ { - response, err := consumerAPI.GetOneInstance(request1) - c.Assert(err, check.IsNil) - c.Assert(len(response.Instances), check.Equals, 1) - ins := response.Instances[0] - c.Assert(ins.GetCircuitBreakerStatus().GetStatus(), check.Equals, model.Open) - } - request2 := &api.GetInstancesRequest{} - request2.FlowID = 1111 - request2.Namespace = cbNS - request2.Service = "cbTest1" - instResp, err := consumerAPI.GetInstances(request2) - c.Assert(err, check.IsNil) - c.Assert(len(instResp.Instances), check.Equals, 1) - c.Assert(instResp.Instances[0].GetCircuitBreakerStatus().GetStatus(), check.Equals, model.Open) - - // 目标实例设置为不健康 - t.mockServer.UpdateServerInstanceHealthy(cbNS, "cbTest1", targetIns.GetId(), false) - time.Sleep(time.Second * 3) - oneResponse, err := consumerAPI.GetInstances(request2) - c.Log(oneResponse.GetInstances()) - c.Assert(err, check.IsNil) - c.Assert(len(oneResponse.Instances), check.Equals, 2) - for _, ins := range oneResponse.GetInstances() { - c.Assert(ins.IsHealthy(), check.Equals, false) - } -} - -// TestHalfOpenSlow 半开后低频率请求测试 -func (t *CircuitBreakSuite) TestHalfOpenSlow(c *check.C) { - fmt.Println("--TestHalfOpenSlow") - defer util.DeleteDir(util.BackupDir) - cfg := config.NewDefaultConfiguration([]string{fmt.Sprintf("%s:%d", cbIP, cbPORT)}) - // enableStat := false - period := 1 * time.Second - // cfg.Global.StatReporter.Enable = &enableStat - cfg.Consumer.LocalCache.PersistDir = "testdata/backup" - cfg.Consumer.CircuitBreaker.CheckPeriod = &period - cfg.Consumer.GetCircuitBreaker().GetErrorCountConfig().SetContinuousErrorThreshold(10) - cfg.Consumer.GetCircuitBreaker().GetErrorCountConfig().SetMetricStatTimeWindow(time.Second * 10) - cfg.Consumer.GetCircuitBreaker().GetErrorRateConfig().SetErrorRatePercent(100) - cfg.Consumer.GetCircuitBreaker().SetSleepWindow(time.Second * 10) - cfg.Consumer.GetCircuitBreaker().SetRequestCountAfterHalfOpen(150) - cfg.Consumer.GetCircuitBreaker().SetSuccessCountAfterHalfOpen(30) - cfg.Consumer.GetCircuitBreaker().SetRecoverWindow(time.Second * 20) - - var err error - var consumerAPI api.ConsumerAPI - consumerAPI, err = api.NewConsumerAPIByConfig(cfg) - c.Assert(err, check.IsNil) - defer consumerAPI.Destroy() - // 等待直到完成首次地域信息拉取 - time.Sleep(time.Second * 1) - - // 随机获取一个实例,并将这个实例作为熔断的目标 - request := &api.GetInstancesRequest{} - request.FlowID = 1111 - request.Namespace = cbNS - request.Service = cbSVC - request.SkipRouteFilter = false - var response *model.InstancesResponse - response, err = consumerAPI.GetInstances(request) - c.Assert(err, check.IsNil) - c.Assert(len(response.Instances) > 0, check.Equals, true) - fmt.Printf("GetInstances: first count is %d\n", len(response.Instances)) - targetIns := response.Instances[0] - - // 熔断 - var callResult = &api.ServiceCallResult{} - callResult.RetStatus = model.RetFail - callResult.Delay = model.ToDurationPtr(1000 * time.Millisecond) - callResult.RetCode = proto.Int(-1) - callResult.CalledInstance = targetIns - for i := 0; i < 15; i++ { - err := consumerAPI.UpdateServiceCallResult(callResult) - c.Assert(err, check.IsNil) - } - time.Sleep(time.Second * 2) - c.Assert(targetIns.GetCircuitBreakerStatus().GetStatus(), check.Equals, model.Open) - CheckInstanceAvailable(c, consumerAPI, targetIns, false, cbNS, cbSVC) - time.Sleep(time.Second * 11) - c.Assert(targetIns.GetCircuitBreakerStatus().GetStatus(), check.Equals, model.HalfOpen) - callResult.RetStatus = model.RetSuccess - CheckInstanceAvailable(c, consumerAPI, targetIns, true, cbNS, cbSVC) - consumerAPI.UpdateServiceCallResult(callResult) - - for cnt := 0; cnt < 4; cnt++ { - for i := 0; i < 30; i++ { - util.SelectInstanceSpecificNum(c, consumerAPI, targetIns, 1, 2000) - err := consumerAPI.UpdateServiceCallResult(callResult) - c.Assert(err, check.IsNil) - } - time.Sleep(time.Second * 2) - c.Assert(targetIns.GetCircuitBreakerStatus().GetStatus(), check.Equals, model.HalfOpen) - // CheckInstanceAvailable(c, consumerAPI, targetIns, true, cbNS, cbSVC) - } - for i := 0; i < 30; i++ { - // log2.Printf("i: %d, cbStatus: %v", i, targetIns.GetCircuitBreakerStatus()) - util.SelectInstanceSpecificNum(c, consumerAPI, targetIns, 1, 2000) - err := consumerAPI.UpdateServiceCallResult(callResult) - c.Assert(err, check.IsNil) - } - time.Sleep(time.Second * 2) - util.SelectInstanceSpecificNum(c, consumerAPI, targetIns, 1, 2000) - c.Assert(targetIns.GetCircuitBreakerStatus().GetStatus(), check.Equals, model.Close) - CheckInstanceAvailable(c, consumerAPI, targetIns, true, cbNS, cbSVC) -} - -const ( - setUnhealthy = 1 - setIsolate = 2 - setWeightZero = 3 -) - -// WhenOpenToHalfOpenChangToUnavailable 当熔断开后,熔断状态变为半开后,熔断状态变为不可用 -func (t *CircuitBreakSuite) WhenOpenToHalfOpenChangToUnavailable(c *check.C, flag int) { - defer util.DeleteDir(util.BackupDir) - cfg := config.NewDefaultConfiguration([]string{fmt.Sprintf("%s:%d", cbIP, cbPORT)}) - // enableStat := false - period := 1 * time.Second - // cfg.Global.StatReporter.Enable = &enableStat - cfg.Consumer.LocalCache.PersistDir = "testdata/backup" - cfg.Consumer.CircuitBreaker.CheckPeriod = &period - cfg.Consumer.GetCircuitBreaker().GetErrorCountConfig().SetContinuousErrorThreshold(10) - cfg.Consumer.GetCircuitBreaker().GetErrorCountConfig().SetMetricStatTimeWindow(time.Second * 10) - cfg.Consumer.GetCircuitBreaker().GetErrorRateConfig().SetErrorRatePercent(100) - cfg.Consumer.GetCircuitBreaker().SetSleepWindow(time.Second * 10) - cfg.Consumer.GetCircuitBreaker().SetRequestCountAfterHalfOpen(15) - cfg.Consumer.GetCircuitBreaker().SetSuccessCountAfterHalfOpen(10) - cfg.Consumer.GetCircuitBreaker().SetRecoverWindow(time.Second * 20) - - var err error - var consumerAPI api.ConsumerAPI - consumerAPI, err = api.NewConsumerAPIByConfig(cfg) - c.Assert(err, check.IsNil) - defer consumerAPI.Destroy() - // 等待直到完成首次地域信息拉取 - time.Sleep(time.Second * 1) - - // 随机获取一个实例,并将这个实例作为熔断的目标 - request := &api.GetInstancesRequest{} - request.FlowID = 1111 - request.Namespace = cbNS - request.Service = cbSVC - request.SkipRouteFilter = false - var response *model.InstancesResponse - response, err = consumerAPI.GetInstances(request) - c.Assert(err, check.IsNil) - c.Assert(len(response.Instances) > 0, check.Equals, true) - fmt.Printf("GetInstances: first count is %d\n", len(response.Instances)) - targetIns := response.Instances[0] - - // 熔断 - var callResult = &api.ServiceCallResult{} - callResult.RetStatus = model.RetFail - callResult.Delay = model.ToDurationPtr(1000 * time.Millisecond) - callResult.RetCode = proto.Int(-1) - callResult.CalledInstance = targetIns - for i := 0; i < 15; i++ { - err := consumerAPI.UpdateServiceCallResult(callResult) - c.Assert(err, check.IsNil) - } - time.Sleep(time.Second * 2) - c.Assert(targetIns.GetCircuitBreakerStatus().GetStatus(), check.Equals, model.Open) - CheckInstanceAvailable(c, consumerAPI, targetIns, false, cbNS, cbSVC) - - for i := 0; i < 20; i++ { - err := consumerAPI.UpdateServiceCallResult(callResult) - c.Assert(err, check.IsNil) - if i == 10 { - if flag == setUnhealthy { - t.mockServer.UpdateServerInstanceHealthy(cbNS, cbSVC, targetIns.GetId(), false) - } else if flag == setIsolate { - t.mockServer.UpdateServerInstanceIsolate(cbNS, cbSVC, targetIns.GetId(), true) - } else if flag == setWeightZero { - t.mockServer.UpdateServerInstanceWeight(cbNS, cbSVC, targetIns.GetId(), 0) - } - } - } - time.Sleep(time.Second * 11) - fmt.Println(targetIns.GetCircuitBreakerStatus().GetStatus()) - CheckInstanceAvailable(c, consumerAPI, targetIns, false, cbNS, cbSVC) - time.Sleep(time.Second * 2) -} - -// TestWhenOpenToHalfOpenChangToUnhealthy 当熔断开后,熔断状态变为半开后,熔断状态变为不可用 -func (t *CircuitBreakSuite) TestWhenOpenToHalfOpenChangToUnhealthy(c *check.C) { - fmt.Println("---TestWhenOpenToHalfOpenChangToUnhealthy") - t.WhenOpenToHalfOpenChangToUnavailable(c, setUnhealthy) -} - -// TestWhenOpenToHalfOpenChangToIsolate 当熔断开后,熔断状态变为半开后,熔断状态变为不可用 -func (t *CircuitBreakSuite) TestWhenOpenToHalfOpenChangToIsolate(c *check.C) { - fmt.Println("---TestWhenOpenToHalfOpenChangToIsolate") - t.WhenOpenToHalfOpenChangToUnavailable(c, setIsolate) -} - -// TestWhenOpenToHalfOpenChangToWeightZero 当熔断开后,熔断状态变为半开后,熔断权重为0 -func (t *CircuitBreakSuite) TestWhenOpenToHalfOpenChangToWeightZero(c *check.C) { - fmt.Println("---TestWhenOpenToHalfOpenChangToWeightZero") - t.WhenOpenToHalfOpenChangToUnavailable(c, setWeightZero) -} - -// 测试实例转化为半开之后,不会陷入不能分配配额的情况 -func (t *CircuitBreakSuite) testHalfOpenMustChange(c *check.C, consumer api.ConsumerAPI, cb string, success, fail int) { - defer util.DeleteDir(util.BackupDir) - // 随机获取一个实例,并将这个实例作为熔断的目标 - request := &api.GetOneInstanceRequest{} - request.FlowID = 1111 - request.Namespace = cbNS - request.Service = cbSVC - response, err := consumer.GetOneInstance(request) - c.Assert(err, check.IsNil) - c.Assert(len(response.Instances) > 0, check.Equals, true) - targetIns := response.Instances[0] - - // 熔断 - var callResult = &api.ServiceCallResult{} - callResult.RetStatus = model.RetFail - callResult.Delay = model.ToDurationPtr(1000 * time.Millisecond) - callResult.RetCode = proto.Int(-1) - callResult.CalledInstance = targetIns - t.reportCallStatus(c, consumer, targetIns, 0, model.RetSuccess, 100*time.Millisecond, success) - t.reportCallStatus(c, consumer, targetIns, 1, model.RetFail, 100*time.Millisecond, fail) - // 转为熔断状态 - time.Sleep(time.Second * 2) - c.Assert(targetIns.GetCircuitBreakerStatus().GetStatus(), check.Equals, model.Open) - c.Assert(targetIns.GetCircuitBreakerStatus().GetCircuitBreaker(), check.Equals, cb) - log2.Printf("cbstatus of targetInstance: %+v\n", targetIns.GetCircuitBreakerStatus()) - - // 转为半开状态 - time.Sleep(time.Second * 10) - c.Assert(targetIns.GetCircuitBreakerStatus().GetStatus(), check.Equals, model.HalfOpen) - - log.GetDetectLogger().Infof("start to circuit break %s when half open", targetIns.GetId()) - // 多线程并发上报失败,导致重新熔断 - wg := &sync.WaitGroup{} - wg.Add(3) - for i := 0; i < 3; i++ { - go t.reportErrorWhenHalfOpen(c, consumer, targetIns, wg) - } - wg.Wait() - time.Sleep(time.Second * 2) - c.Assert(targetIns.GetCircuitBreakerStatus().GetStatus(), check.Equals, model.Open) - c.Assert(targetIns.GetCircuitBreakerStatus().GetCircuitBreaker(), check.Equals, cb) - log2.Printf("cbstatus of targetInstance: %+v\n", targetIns.GetCircuitBreakerStatus()) - log.GetDetectLogger().Infof("end circuit break %s when half open", targetIns.GetId()) - - // 转为半开状态 - time.Sleep(time.Second * 10) - c.Assert(targetIns.GetCircuitBreakerStatus().GetStatus(), check.Equals, model.HalfOpen) - - // 用完配额 - util.SelectInstanceSpecificNum(c, consumer, targetIns, 10, 2000) - // 但是成功数少于配置要求 - t.reportCallStatus(c, consumer, targetIns, 0, model.RetSuccess, 100*time.Millisecond, 9) - time.Sleep(12 * time.Second) - // 重新熔断 - c.Assert(targetIns.GetCircuitBreakerStatus().GetStatus(), check.Equals, model.Open) - - // 转为半开状态,并分配9次调用 - time.Sleep(time.Second * 11) - c.Assert(targetIns.GetCircuitBreakerStatus().GetStatus(), check.Equals, model.HalfOpen) - util.SelectInstanceSpecificNum(c, consumer, targetIns, 9, 2000) - t.reportCallStatus(c, consumer, targetIns, 0, model.RetSuccess, 100*time.Millisecond, 9) - // 跳过recover时间,继续分配请求 - time.Sleep(20 * time.Second) - util.SelectInstanceSpecificNum(c, consumer, targetIns, 1, 2000) - t.reportCallStatus(c, consumer, targetIns, 0, model.RetSuccess, 100*time.Millisecond, 1) - // 恢复正常 - time.Sleep(2 * time.Second) - util.SelectInstanceSpecificNum(c, consumer, targetIns, 10, 2000) -} - -func (t *CircuitBreakSuite) reportErrorWhenHalfOpen(c *check.C, consumer api.ConsumerAPI, targetIns model.Instance, wg *sync.WaitGroup) { - t.reportCallStatus(c, consumer, targetIns, -1, model.RetFail, 100*time.Millisecond, 1) - wg.Done() -} - -// TestHalfOpenMustChangeErrorCount 断开熔断变更错误 -func (t *CircuitBreakSuite) TestHalfOpenMustChangeErrorCount(c *check.C) { - fmt.Println("--TestHalfOpenMustChangeErrorCount") - defer util.DeleteDir(util.BackupDir) - cfg := config.NewDefaultConfiguration([]string{fmt.Sprintf("%s:%d", cbIP, cbPORT)}) - // enableStat := false - period := 1 * time.Second - // cfg.Global.StatReporter.Enable = &enableStat - cfg.Consumer.LocalCache.PersistDir = "testdata/backup" - cfg.Consumer.CircuitBreaker.CheckPeriod = &period - cfg.Consumer.GetCircuitBreaker().GetErrorCountConfig().SetContinuousErrorThreshold(10) - cfg.Consumer.GetCircuitBreaker().GetErrorCountConfig().SetMetricStatTimeWindow(time.Second * 10) - cfg.Consumer.GetCircuitBreaker().SetSleepWindow(time.Second * 10) - cfg.Consumer.GetCircuitBreaker().SetRequestCountAfterHalfOpen(10) - cfg.Consumer.GetCircuitBreaker().SetSuccessCountAfterHalfOpen(10) - cfg.Consumer.GetCircuitBreaker().SetRecoverWindow(time.Second * 10) - cfg.Consumer.GetCircuitBreaker().GetErrorRateConfig().SetErrorRatePercent(100) - - var err error - var consumerAPI api.ConsumerAPI - consumerAPI, err = api.NewConsumerAPIByConfig(cfg) - c.Assert(err, check.IsNil) - defer consumerAPI.Destroy() - // 等待直到完成首次地域信息拉取 - time.Sleep(time.Second * 1) - t.testHalfOpenMustChange(c, consumerAPI, "errorCount", 0, 15) -} - -// TestHalfOpenMustChangeErrorRate 断开熔断变更错误率 -//func (t *CircuitBreakSuite) TestHalfOpenMustChangeErrorRate(c *check.C) { -// fmt.Println("--TestHalfOpenMustChangeErrorRate") -// defer util.DeleteDir(util.BackupDir) -// cfg := config.NewDefaultConfiguration([]string{fmt.Sprintf("%s:%d", cbIP, cbPORT)}) -// // enableStat := false -// period := 1 * time.Second -// // cfg.Global.StatReporter.Enable = &enableStat -// cfg.Consumer.LocalCache.PersistDir = "testdata/backup" -// cfg.Consumer.CircuitBreaker.CheckPeriod = &period -// cfg.Consumer.GetCircuitBreaker().GetErrorRateConfig().SetRequestVolumeThreshold(10) -// cfg.Consumer.GetCircuitBreaker().GetErrorRateConfig().SetMetricStatTimeWindow(time.Second * 10) -// cfg.Consumer.GetCircuitBreaker().SetSleepWindow(time.Second * 5) -// cfg.Consumer.GetCircuitBreaker().SetRequestCountAfterHalfOpen(10) -// cfg.Consumer.GetCircuitBreaker().SetSuccessCountAfterHalfOpen(10) -// cfg.Consumer.GetCircuitBreaker().SetRecoverWindow(time.Second * 10) -// cfg.Consumer.GetCircuitBreaker().GetErrorRateConfig().SetErrorRatePercent(80) -// var err error -// var consumerAPI api.ConsumerAPI -// consumerAPI, err = api.NewConsumerAPIByConfig(cfg) -// c.Assert(err, check.IsNil) -// defer consumerAPI.Destroy() -// // 等待直到完成首次地域信息拉取 -// time.Sleep(time.Second * 1) -// t.testHalfOpenMustChange(c, consumerAPI, "errorRate", 2, 9) -//} - -// TestAllHalfOpenReturn 断开熔断变更错误率 -func (t *CircuitBreakSuite) TestAllHalfOpenReturn(c *check.C) { - fmt.Println("--TestAllHalfOpenReturn") - defer util.DeleteDir(util.BackupDir) - cfg := config.NewDefaultConfiguration([]string{fmt.Sprintf("%s:%d", cbIP, cbPORT)}) - // enableStat := false - period := 1 * time.Second - // cfg.Global.StatReporter.Enable = &enableStat - cfg.Consumer.LocalCache.PersistDir = "testdata/backup" - cfg.Consumer.CircuitBreaker.CheckPeriod = &period - cfg.Consumer.GetCircuitBreaker().GetErrorRateConfig().SetRequestVolumeThreshold(10) - cfg.Consumer.GetCircuitBreaker().GetErrorRateConfig().SetMetricStatTimeWindow(time.Second * 10) - cfg.Consumer.GetCircuitBreaker().SetSleepWindow(time.Second * 10) - cfg.Consumer.GetCircuitBreaker().SetRequestCountAfterHalfOpen(10) - cfg.Consumer.GetCircuitBreaker().SetSuccessCountAfterHalfOpen(10) - cfg.Consumer.GetCircuitBreaker().SetRecoverWindow(time.Second * 10) - cfg.Consumer.GetCircuitBreaker().GetErrorRateConfig().SetErrorRatePercent(80) - - consumer, err := api.NewConsumerAPIByConfig(cfg) - c.Assert(err, check.IsNil) - defer consumer.Destroy() - - request := &api.GetAllInstancesRequest{} - request.FlowID = 1111 - request.Namespace = cbNS - request.Service = cbSVC - response, err := consumer.GetAllInstances(request) - c.Assert(err, check.IsNil) - c.Assert(len(response.Instances) > 0, check.Equals, true) - allInstances := response.Instances - for _, inst := range allInstances { - t.reportCallStatus(c, consumer, inst, -1, model.RetFail, 100*time.Millisecond, 11) - } - time.Sleep(1 * time.Second) - for _, inst := range allInstances { - c.Assert(inst.GetCircuitBreakerStatus().GetStatus(), check.Equals, model.Open) - } - time.Sleep(11 * time.Second) - for _, inst := range allInstances { - c.Assert(inst.GetCircuitBreakerStatus().GetStatus(), check.Equals, model.HalfOpen) - for i := 0; i < 10; i++ { - // 用完所有配额 - inst.GetCircuitBreakerStatus().Allocate() - } - } - for i := 0; i < 100; i++ { - oneReq := &api.GetOneInstanceRequest{} - oneReq.Namespace = cbNS - oneReq.Service = cbSVC - resp, err := consumer.GetOneInstance(oneReq) - c.Assert(err, check.IsNil) - inst := resp.Instances[0] - c.Assert(inst.GetCircuitBreakerStatus().GetStatus(), check.Equals, model.HalfOpen) - c.Assert(inst.GetCircuitBreakerStatus().IsAvailable(), check.Equals, false) - } -} - -func (t *CircuitBreakSuite) reportCallStatus(c *check.C, consumerAPI api.ConsumerAPI, targetIns model.Instance, code int, success model.RetStatus, cost time.Duration, times int) { - var callResult = &api.ServiceCallResult{} - callResult.RetStatus = success - callResult.Delay = model.ToDurationPtr(cost) - callResult.RetCode = proto.Int(code) - callResult.CalledInstance = targetIns - for i := 0; i < times; i++ { - err := consumerAPI.UpdateServiceCallResult(callResult) - c.Assert(err, check.IsNil) - } -} diff --git a/test/circuitbreak/healthcheck_always_suite.go b/test/circuitbreak/healthcheck_always_suite.go deleted file mode 100644 index 3749c48f..00000000 --- a/test/circuitbreak/healthcheck_always_suite.go +++ /dev/null @@ -1,163 +0,0 @@ -/** - * 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 circuitbreak - -import ( - "fmt" - "log" - "net" - "os" - "time" - - "github.com/golang/protobuf/ptypes/wrappers" - "github.com/google/uuid" - apimodel "github.com/polarismesh/specification/source/go/api/v1/model" - "github.com/polarismesh/specification/source/go/api/v1/service_manage" - "google.golang.org/grpc" - "gopkg.in/check.v1" - - "github.com/polarismesh/polaris-go/api" - "github.com/polarismesh/polaris-go/pkg/config" - "github.com/polarismesh/polaris-go/pkg/model" - commontest "github.com/polarismesh/polaris-go/test/common" - "github.com/polarismesh/polaris-go/test/mock" - "github.com/polarismesh/polaris-go/test/util" -) - -const ( - // 测试的默认命名空间 - checkAlwaysNamespace = "testCheckAlways" - // 测试的默认服务名 - checkAlwaysService = "svcAlways" - // 测试服务器的默认地址 - checkAlwaysIPAdress = "127.0.0.1" - // 测试服务器的端口 - checkAlwaysPort = commontest.HealthCheckAlwaysSuitServerPort - // 所有实例数量 - instanceTotal = 5 -) - -// HealthCheckAlwaysTestingSuite 消费者API测试套 -type HealthCheckAlwaysTestingSuite struct { - grpcServer *grpc.Server - grpcListener net.Listener - serviceToken string -} - -// GetName 套件名字 -func (t *HealthCheckAlwaysTestingSuite) GetName() string { - return "HealthCheckAlwaysSuite" -} - -// SetUpSuite 启动测试套程序 -func (t *HealthCheckAlwaysTestingSuite) SetUpSuite(c *check.C) { - defer util.DeleteDir(util.BackupDir) - grpcOptions := make([]grpc.ServerOption, 0) - maxStreams := 100000 - grpcOptions = append(grpcOptions, grpc.MaxConcurrentStreams(uint32(maxStreams))) - - // get the grpc server wired up - grpc.EnableTracing = true - - ipAddr := checkAlwaysIPAdress - shopPort := checkAlwaysPort - var err error - t.grpcServer = grpc.NewServer(grpcOptions...) - t.serviceToken = uuid.New().String() - mockServer := mock.NewNamingServer() - token := mockServer.RegisterServerService(config.ServerDiscoverService) - mockServer.RegisterServerInstance(ipAddr, shopPort, config.ServerDiscoverService, token, true) - mockServer.RegisterNamespace(&apimodel.Namespace{ - Name: &wrappers.StringValue{Value: checkAlwaysNamespace}, - Comment: &wrappers.StringValue{Value: "for healthCheck api test"}, - Owners: &wrappers.StringValue{Value: "healthCheck"}, - }) - testService := &service_manage.Service{ - Name: &wrappers.StringValue{Value: checkAlwaysService}, - Namespace: &wrappers.StringValue{Value: checkAlwaysNamespace}, - Token: &wrappers.StringValue{Value: t.serviceToken}, - } - mockServer.RegisterService(testService) - mockServer.GenTestInstancesWithHostPort(testService, instanceTotal, "127.0.0.1", 1024) - service_manage.RegisterPolarisGRPCServer(t.grpcServer, mockServer) - t.grpcListener, err = net.Listen("tcp", fmt.Sprintf("%s:%d", ipAddr, shopPort)) - if err != nil { - _ = util.DeleteDir(util.BackupDir) - log.Fatal(fmt.Sprintf("error listening appserver %v", err)) - } - log.Printf("appserver listening on %s:%d\n", ipAddr, shopPort) - go func() { - if err := t.grpcServer.Serve(t.grpcListener); err != nil { - panic(err) - } - }() - waitServerReady(shopPort) -} - -// TearDownSuite 结束测试套程序 -func (t *HealthCheckAlwaysTestingSuite) TearDownSuite(c *check.C) { - t.grpcServer.GracefulStop() - if util.DirExist(util.BackupDir) { - os.RemoveAll(util.BackupDir) - } - util.InsertLog(t, c.GetTestLog()) -} - -const ( - healthPort1025 = 1025 - - healthPort1030 = 1027 -) - -// TestHttpDetectAlways 测试持久化探测 -func (t *HealthCheckAlwaysTestingSuite) TestHttpDetectAlways(c *check.C) { - healthPorts := []int{healthPort1025, healthPort1030} - for _, port := range healthPorts { - go func(hport int) { - // 起tcp服务 - address := fmt.Sprintf("%s:%d", "127.0.0.1", hport) - log.Printf("Start Server:%s", address) - err := startHTTPServer(address, 10, 200) - c.Assert(err, check.IsNil) - }(port) - } - cfg, err := config.LoadConfigurationByFile("testdata/healthcheck.yaml") - c.Assert(err, check.IsNil) - cfg.GetConsumer().GetHealthCheck().SetChain([]string{"http"}) - cfg.GetConsumer().GetHealthCheck().SetWhen(config.HealthCheckAlways) - cfg.GetConsumer().GetHealthCheck().SetConcurrency(10) - sdkCtx, err := api.InitContextByConfig(cfg) - c.Assert(err, check.IsNil) - defer sdkCtx.Destroy() - consumerAPI := api.NewConsumerAPIByContext(sdkCtx) - // 随机获取一个实例,并将这个实例作为熔断的目标 - request := &api.GetInstancesRequest{} - request.Namespace = checkAlwaysNamespace - request.Service = checkAlwaysService - var resp *model.InstancesResponse - resp, err = consumerAPI.GetInstances(request) - c.Assert(err, check.IsNil) - c.Assert(len(resp.Instances), check.Equals, instanceTotal) - time.Sleep(10 * time.Second) - resp, err = consumerAPI.GetInstances(request) - c.Assert(err, check.IsNil) - // for _, instance := range resp.Instances { - // fmt.Printf("instance :%d, value is %v\n", instance.GetPort(), instance.GetCircuitBreakerStatus()) - // } - c.Assert(len(resp.Instances), check.Equals, len(healthPorts)) -} diff --git a/test/circuitbreak/healthcheck_suite.go b/test/circuitbreak/healthcheck_suite.go deleted file mode 100644 index de5766ee..00000000 --- a/test/circuitbreak/healthcheck_suite.go +++ /dev/null @@ -1,296 +0,0 @@ -/** - * 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 circuitbreak - -import ( - "bytes" - "fmt" - "log" - "net" - "net/http" - "os" - "time" - - "github.com/golang/protobuf/ptypes/wrappers" - "github.com/google/uuid" - apimodel "github.com/polarismesh/specification/source/go/api/v1/model" - "github.com/polarismesh/specification/source/go/api/v1/service_manage" - "google.golang.org/grpc" - "gopkg.in/check.v1" - - "github.com/polarismesh/polaris-go/api" - "github.com/polarismesh/polaris-go/pkg/config" - "github.com/polarismesh/polaris-go/pkg/model" - "github.com/polarismesh/polaris-go/pkg/model/local" - "github.com/polarismesh/polaris-go/plugin/healthcheck/utils" - commontest "github.com/polarismesh/polaris-go/test/common" - "github.com/polarismesh/polaris-go/test/mock" - "github.com/polarismesh/polaris-go/test/util" -) - -const ( - // 测试的默认命名空间 - detectNamespace = "testod" - // 测试的默认服务名 - detectService = "svc1" - // 测试服务器的默认地址 - detectIPAdress = "127.0.0.1" - // 测试服务器的端口 - detectPort = commontest.HealthCheckAlwaysSuitServerPort -) - -// HealthCheckTestingSuite 消费者API测试套 -type HealthCheckTestingSuite struct { - grpcServer *grpc.Server - grpcListener net.Listener - serviceToken string -} - -// GetName 套件名字 -func (t *HealthCheckTestingSuite) GetName() string { - return "HealthCheckSuite" -} - -// SetUpSuite 启动测试套程序 -func (t *HealthCheckTestingSuite) SetUpSuite(c *check.C) { - defer util.DeleteDir(util.BackupDir) - grpcOptions := make([]grpc.ServerOption, 0) - maxStreams := 100000 - grpcOptions = append(grpcOptions, grpc.MaxConcurrentStreams(uint32(maxStreams))) - - // get the grpc server wired up - grpc.EnableTracing = true - - ipAddr := detectIPAdress - shopPort := detectPort - var err error - t.grpcServer = grpc.NewServer(grpcOptions...) - t.serviceToken = uuid.New().String() - mockServer := mock.NewNamingServer() - token := mockServer.RegisterServerService(config.ServerDiscoverService) - mockServer.RegisterServerInstance(ipAddr, shopPort, config.ServerDiscoverService, token, true) - mockServer.RegisterNamespace(&apimodel.Namespace{ - Name: &wrappers.StringValue{Value: detectNamespace}, - Comment: &wrappers.StringValue{Value: "for healthCheck api test"}, - Owners: &wrappers.StringValue{Value: "healthCheck"}, - }) - testService := &service_manage.Service{ - Name: &wrappers.StringValue{Value: detectService}, - Namespace: &wrappers.StringValue{Value: detectNamespace}, - Token: &wrappers.StringValue{Value: t.serviceToken}, - } - mockServer.RegisterService(testService) - mockServer.GenTestInstancesWithHostPort(testService, 100, "127.0.0.1", 1024) - service_manage.RegisterPolarisGRPCServer(t.grpcServer, mockServer) - t.grpcListener, err = net.Listen("tcp", fmt.Sprintf("%s:%d", ipAddr, shopPort)) - if err != nil { - _ = util.DeleteDir(util.BackupDir) - log.Fatal(fmt.Sprintf("error listening appserver %v", err)) - } - log.Printf("appserver listening on %s:%d\n", ipAddr, shopPort) - go func() { - if err := t.grpcServer.Serve(t.grpcListener); err != nil { - panic(err) - } - }() - waitServerReady(shopPort) -} - -// TearDownSuite 结束测试套程序 -func (t *HealthCheckTestingSuite) TearDownSuite(c *check.C) { - t.grpcServer.GracefulStop() - if util.DirExist(util.BackupDir) { - os.RemoveAll(util.BackupDir) - } - util.InsertLog(t, c.GetTestLog()) -} - -// TestTCPDetection 测试TCP健康探测 -func (t *HealthCheckTestingSuite) TestTCPDetection(c *check.C) { - defer util.DeleteDir(util.BackupDir) - log.Printf("start to TestTCPDetection") - Logic(c, startTCPServer, nil, 0, true) -} - -// TestFailTCPDetection 测试TCP探测失败 -func (t *HealthCheckTestingSuite) TestFailTCPDetection(c *check.C) { - defer util.DeleteDir(util.BackupDir) - log.Printf("start to TestTCPDetectionFail") - Logic(c, startTCPServer, nil, 0, false) -} - -// TestHTTPDetection 测试HTTP健康探测 -func (t *HealthCheckTestingSuite) TestHTTPDetection(c *check.C) { - defer util.DeleteDir(util.BackupDir) - log.Printf("start to TestHTTPDetection") - Logic(c, nil, startHTTPServer, 3, true) -} - -// TestFailHTTPDetection 测试UDP探测失败 -func (t *HealthCheckTestingSuite) TestFailHTTPDetection(c *check.C) { - defer util.DeleteDir(util.BackupDir) - log.Printf("start to TestFailHTTPDetection") - Logic(c, nil, startHTTPServer, 0, false) -} - -// Logic 执行业务逻辑测试 -func Logic(c *check.C, - tcpServer func(string, int, []byte) error, httpServer func(string, int, int) error, index int, checkFlag bool) { - cfg, err := config.LoadConfigurationByFile("testdata/healthcheck.yaml") - c.Assert(err, check.IsNil) - if nil == httpServer && nil != tcpServer { - cfg.Consumer.HealthCheck.Chain = []string{"tcp"} - } - if nil != httpServer && nil == tcpServer { - cfg.Consumer.HealthCheck.Chain = []string{"http"} - } - sdkCtx, err := api.InitContextByConfig(cfg) - c.Assert(err, check.IsNil) - defer sdkCtx.Destroy() - consumerAPI := api.NewConsumerAPIByContext(sdkCtx) - // 随机获取一个实例,并将这个实例作为熔断的目标 - request := &api.GetInstancesRequest{} - request.Namespace = detectNamespace - request.Service = detectService - request.Timeout = model.ToDurationPtr(2 * time.Second) - resp, err := consumerAPI.GetInstances(request) - c.Assert(err, check.IsNil) - if len(resp.Instances) < 3 { - c.Assert(len(resp.Instances), check.Equals, 3) - } - cbID := resp.Instances[index].GetId() - log.Printf("The instance to ciucuitbreak by errcount: %v", cbID) - // 开始没有健康探测状态 - respInstance := resp.Instances[index] - localInstanceValue := respInstance.(local.InstanceLocalValue) - c.Assert(localInstanceValue.GetActiveDetectStatus(), check.IsNil) - var failCode int32 = 1 - for i := 0; i < 10; i++ { - consumerAPI.UpdateServiceCallResult(&api.ServiceCallResult{ServiceCallResult: model.ServiceCallResult{ - CalledInstance: resp.Instances[index], - RetStatus: model.RetFail, - RetCode: &failCode, - Delay: request.Timeout}}) - } - // 2s 之后,被熔断 - time.Sleep(2 * time.Second) - c.Assert(respInstance.GetCircuitBreakerStatus(), check.NotNil) - c.Assert(respInstance.GetCircuitBreakerStatus().GetStatus(), check.Equals, model.Open) - // openTime := localValues.GetCircuitBreakerStatus().GetStartTime() - CheckInstanceAvailable(c, consumerAPI, respInstance, false, detectNamespace, detectService) - - if checkFlag { - go func() { - // 起tcp服务 - address := utils.GetAddressByInstance(resp.Instances[index]) - log.Printf("Start Server:%s", address) - if tcpServer != nil { - err = tcpServer(address, 10, []byte{0x00, 0x00, 0x43, 0x21}) - } else if httpServer != nil { - err = httpServer(address, 10, 200) - } - c.Assert(err, check.IsNil) - }() - } - // 5s 之后,探测为Health - time.Sleep(5 * time.Second) - if checkFlag { - c.Assert(localInstanceValue.GetActiveDetectStatus(), check.NotNil) - c.Assert(localInstanceValue.GetActiveDetectStatus().GetStatus(), check.Equals, model.Healthy) - // 熔断状态修改为HalfOpen - c.Assert(respInstance.GetCircuitBreakerStatus().GetStatus(), check.Equals, model.HalfOpen) - CheckInstanceAvailable(c, consumerAPI, respInstance, true, detectNamespace, detectService) - } else { - c.Assert(localInstanceValue.GetActiveDetectStatus(), check.NotNil) - c.Assert(localInstanceValue.GetActiveDetectStatus().GetStatus(), check.Equals, model.Dead) - // 熔断状态修改为HalfOpen - c.Assert(respInstance.GetCircuitBreakerStatus().GetStatus() != model.HalfOpen, check.Equals, true) - CheckInstanceAvailable(c, consumerAPI, respInstance, false, detectNamespace, detectService) - } -} - -// startTCPServer 起一个TCP服务 -func startTCPServer(address string, sTime int, retByte []byte) error { - listener, err := net.Listen("tcp", address) - if err != nil { - fmt.Println("Error listening", err.Error()) - return err // 终止程序 - } - defer listener.Close() - // 监听并接受来自客户端的连接 - for { - task := func() error { - conn, err := listener.Accept() - if err != nil { - fmt.Println("Error accepting", err.Error()) - return err // 终止程序 - } - defer conn.Close() - go func(conn net.Conn) { - buf := make([]byte, 4) - recvBuf := bytes.Buffer{} - for { - var n int - n, err = conn.Read(buf[0:]) - if n > 0 { - recvBuf.Write(buf[0:n]) - if recvBuf.Len() >= 4 { - time.Sleep(time.Duration(sTime) * time.Millisecond) - conn.Write(retByte) - break - } - } - } - }(conn) - return nil - } - if err := task(); err != nil { - return err - } - } -} - -// startHttpServer 启动一个Http服务 -func startHTTPServer(address string, sTime int, statusCode int) error { - mux := http.NewServeMux() - mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) { - time.Sleep(time.Duration(sTime) * time.Millisecond) - log.Printf("receive health http detection") - w.WriteHeader(statusCode) - }) - log.Printf("httpserver ready, addr %s", address) - err := http.ListenAndServe(address, mux) - if err != nil { - log.Printf("httpserver err %v", err) - } - return err -} - -func waitServerReady(port int) { - for { - conn, err := grpc.Dial(fmt.Sprintf("%s:%d", detectIPAdress, port), grpc.WithBlock(), grpc.WithInsecure()) - if err != nil { - log.Printf("dial failed, err:%v", err) - time.Sleep(time.Second) - continue - } - _ = conn.Close() - break - } - log.Println("grpc server is ready") -} diff --git a/test/serviceroute/canary_router_suite.go b/test/serviceroute/canary_router_suite.go deleted file mode 100644 index 5702c483..00000000 --- a/test/serviceroute/canary_router_suite.go +++ /dev/null @@ -1,974 +0,0 @@ -/** - * 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 serviceroute - -import ( - "fmt" - "log" - "net" - "os/user" - "time" - - "github.com/golang/protobuf/ptypes/wrappers" - "github.com/google/uuid" - apimodel "github.com/polarismesh/specification/source/go/api/v1/model" - "github.com/polarismesh/specification/source/go/api/v1/service_manage" - "google.golang.org/grpc" - "gopkg.in/check.v1" - - "github.com/polarismesh/polaris-go/api" - "github.com/polarismesh/polaris-go/pkg/config" - "github.com/polarismesh/polaris-go/pkg/model" - commontest "github.com/polarismesh/polaris-go/test/common" - "github.com/polarismesh/polaris-go/test/mock" - "github.com/polarismesh/polaris-go/test/util" -) - -const ( - // 测试的默认命名空间 - canaryNamespace = "dstCanaryNs" - // 测试的默认服务名 - canaryService = "dstCanarySvc" - // 测试服务器的默认地址 - canaryIPAddress = "127.0.0.1" - // 测试服务器的端口 - canaryPort = commontest.CanarySuitServerPort - // 测试monitor的地址 - canaryMonitorIPAddr = "127.0.0.1" - // 测试monitor的端口 - canaryMonitorPort = 8119 -) - -// CanaryTestingSuite 元数据过滤路由插件测试用例 -type CanaryTestingSuite struct { - mockServer mock.NamingServer - grpcServer *grpc.Server - grpcListener net.Listener - serviceToken string - testService *service_manage.Service -} - -// GetName 套件名字 -func (t *CanaryTestingSuite) GetName() string { - return "CanaryTestingSuite" -} - -// SetUpSuite 启动测试套程序 -func (t *CanaryTestingSuite) SetUpSuite(c *check.C) { - fmt.Println("----------------SetUpSuite") - grpcOptions := make([]grpc.ServerOption, 0) - maxStreams := 100000 - grpcOptions = append(grpcOptions, grpc.MaxConcurrentStreams(uint32(maxStreams))) - - // get the grpc server wired up - grpc.EnableTracing = true - - ipAddr := canaryIPAddress - shopPort := canaryPort - var err error - t.grpcServer = grpc.NewServer(grpcOptions...) - t.serviceToken = uuid.New().String() - t.mockServer = mock.NewNamingServer() - token := t.mockServer.RegisterServerService(config.ServerDiscoverService) - t.mockServer.RegisterServerInstance(ipAddr, shopPort, config.ServerDiscoverService, token, true) - - t.mockServer.RegisterNamespace(&apimodel.Namespace{ - Name: &wrappers.StringValue{Value: canaryNamespace}, - Comment: &wrappers.StringValue{Value: "for consumer api test"}, - Owners: &wrappers.StringValue{Value: "ConsumerAPI"}, - }) - t.mockServer.RegisterServerServices(ipAddr, shopPort) - t.testService = &service_manage.Service{ - Name: &wrappers.StringValue{Value: canaryService}, - Namespace: &wrappers.StringValue{Value: canaryNamespace}, - Token: &wrappers.StringValue{Value: t.serviceToken}, - } - t.mockServer.RegisterService(t.testService) - // t.mockServer.GenTestInstances(t.testService, normalInstances) - // t.mockServer.GenTestInstancesWithMeta(t.testService, addMetaCount, map[string]string{addMetaKey: addMetaValue}) - - service_manage.RegisterPolarisGRPCServer(t.grpcServer, t.mockServer) - t.grpcListener, err = net.Listen("tcp", fmt.Sprintf("%s:%d", ipAddr, shopPort)) - if err != nil { - log.Fatalf("error listening appserver %v", err) - } - log.Printf("appserver listening on %s:%d\n", ipAddr, shopPort) - go func() { - // t.grpcServer.Serve(t.grpcListener) - if err := t.grpcServer.Serve(t.grpcListener); err != nil { - panic(err) - } - }() -} - -// TearDownSuite 结束测试套程序 -func (t *CanaryTestingSuite) TearDownSuite(c *check.C) { - t.grpcServer.GracefulStop() - util.InsertLog(t, c.GetTestLog()) -} - -// TestCanaryNormal01 正常逻辑测试 -func (t *CanaryTestingSuite) TestCanaryNormal01(c *check.C) { - DeleteBackUpDir() - fmt.Println("-----------TestCanaryNormal01") - t.mockServer.GenTestInstancesWithMeta(t.testService, 1, map[string]string{model.CanaryMetaKey: "useV1"}) - t.mockServer.GenTestInstancesWithMeta(t.testService, 2, map[string]string{model.CanaryMetaKey: "useV2"}) - t.mockServer.GenTestInstances(t.testService, 3) - t.mockServer.SetServiceMetadata(t.serviceToken, model.CanaryMetadataEnable, "true") - cfg := config.NewDefaultConfiguration( - []string{fmt.Sprintf("%s:%d", canaryIPAddress, canaryPort)}) - cfg.GetConsumer().GetServiceRouter().SetChain([]string{config.DefaultServiceRouterCanary}) - - cfg.GetConsumer().GetLocalCache().SetStartUseFileCache(false) - consumer, err := api.NewConsumerAPIByConfig(cfg) - c.Assert(err, check.IsNil) - defer consumer.Destroy() - - defer t.mockServer.ClearServiceInstances(t.testService) - defer t.mockServer.SetServiceMetadata(t.serviceToken, model.CanaryMetadataEnable, "false") - - getAllReq := &api.GetAllInstancesRequest{} - getAllReq.Namespace = canaryNamespace - getAllReq.Service = canaryService - respAll, err := consumer.GetAllInstances(getAllReq) - c.Assert(err, check.IsNil) - c.Assert(len(respAll.GetInstances()), check.Equals, 6) - var v1Inst model.Instance - for _, v := range respAll.GetInstances() { - metaData := v.GetMetadata() - if metaData != nil { - if mv, ok := metaData[model.CanaryMetaKey]; ok { - if mv == "useV1" { - v1Inst = v - break - } - } - } - } - fmt.Println("----v1Inst", v1Inst) - var getInstancesReq *api.GetOneInstanceRequest - getInstancesReq = &api.GetOneInstanceRequest{} - getInstancesReq.FlowID = 1 - getInstancesReq.Namespace = canaryNamespace - getInstancesReq.Service = canaryService - getInstancesReq.Metadata = make(map[string]string) - getInstancesReq.Canary = "useV1" - for i := 0; i < 100; i++ { - resp, err := consumer.GetOneInstance(getInstancesReq) - c.Assert(err, check.IsNil) - c.Assert(len(resp.GetInstances()), check.Equals, 1) - instance := resp.GetInstances()[0] - c.Assert(instance.GetId(), check.Equals, v1Inst.GetId()) - } - - getInstancesReq1 := &api.GetInstancesRequest{} - getInstancesReq1.FlowID = 1 - getInstancesReq1.Namespace = canaryNamespace - getInstancesReq1.Service = canaryService - resp, err := consumer.GetInstances(getInstancesReq1) - c.Assert(err, check.IsNil) - c.Assert(len(resp.GetInstances()), check.Equals, 3) - for _, v := range resp.GetInstances() { - c.Assert(v.IsIsolated(), check.Equals, false) - c.Assert(v.GetWeight() != 0, check.Equals, true) - c.Assert(v.GetId() != v1Inst.GetId(), check.Equals, true) - } - getInstancesReq1.Metadata = make(map[string]string) - getInstancesReq1.Canary = "useV1" - resp, err = consumer.GetInstances(getInstancesReq1) - c.Assert(err, check.IsNil) - c.Assert(len(resp.GetInstances()), check.Equals, 1) - for _, v := range resp.GetInstances() { - c.Assert(v.IsIsolated(), check.Equals, false) - c.Assert(v.GetWeight() != 0, check.Equals, true) - c.Assert(v.GetId() == v1Inst.GetId(), check.Equals, true) - } -} - -// TestCanaryNormal02 正常逻辑不带金丝雀标签 -func (t *CanaryTestingSuite) TestCanaryNormal02(c *check.C) { - DeleteBackUpDir() - t.mockServer.GenTestInstancesWithMeta(t.testService, 1, map[string]string{model.CanaryMetaKey: "useV1"}) - t.mockServer.GenTestInstancesWithMeta(t.testService, 2, map[string]string{model.CanaryMetaKey: "useV2"}) - t.mockServer.GenTestInstances(t.testService, 3) - t.mockServer.SetServiceMetadata(t.serviceToken, model.CanaryMetadataEnable, "true") - cfg := config.NewDefaultConfiguration( - []string{fmt.Sprintf("%s:%d", canaryIPAddress, canaryPort)}) - cfg.GetConsumer().GetServiceRouter().SetChain([]string{config.DefaultServiceRouterCanary}) - cfg.GetConsumer().GetLocalCache().SetStartUseFileCache(false) - - consumer, err := api.NewConsumerAPIByConfig(cfg) - c.Assert(err, check.IsNil) - defer consumer.Destroy() - - defer t.mockServer.ClearServiceInstances(t.testService) - defer t.mockServer.SetServiceMetadata(t.serviceToken, model.CanaryMetadataEnable, "false") - - getAllReq := &api.GetAllInstancesRequest{} - getAllReq.Namespace = canaryNamespace - getAllReq.Service = canaryService - respAll, err := consumer.GetAllInstances(getAllReq) - c.Assert(err, check.IsNil) - c.Assert(len(respAll.GetInstances()), check.Equals, 6) - var v1Inst model.Instance - for _, v := range respAll.GetInstances() { - metaData := v.GetMetadata() - if metaData != nil { - if mv, ok := metaData[model.CanaryMetaKey]; ok { - if mv == "useV1" { - v1Inst = v - break - } - } - } - } - fmt.Println("----v1Inst", v1Inst) - var getInstancesReq *api.GetOneInstanceRequest - getInstancesReq = &api.GetOneInstanceRequest{} - getInstancesReq.FlowID = 1 - getInstancesReq.Namespace = canaryNamespace - getInstancesReq.Service = canaryService - getInstancesReq.Metadata = make(map[string]string) - for i := 0; i < 100; i++ { - resp, err := consumer.GetOneInstance(getInstancesReq) - c.Assert(err, check.IsNil) - c.Assert(len(resp.GetInstances()), check.Equals, 1) - instance := resp.GetInstances()[0] - c.Assert(instance.GetId() != v1Inst.GetId(), check.Equals, true) - } -} - -// TestCanaryNormal03 服务不启用金丝雀 -func (t *CanaryTestingSuite) TestCanaryNormal03(c *check.C) { - DeleteBackUpDir() - t.mockServer.GenTestInstancesWithMeta(t.testService, 2, map[string]string{model.CanaryMetaKey: "useV1"}) - t.mockServer.GenTestInstancesWithMeta(t.testService, 1, map[string]string{model.CanaryMetaKey: "useV2"}) - t.mockServer.GenTestInstances(t.testService, 3) - t.mockServer.SetServiceMetadata(t.serviceToken, model.CanaryMetadataEnable, "false") - cfg := config.NewDefaultConfiguration( - []string{fmt.Sprintf("%s:%d", canaryIPAddress, canaryPort)}) - cfg.GetConsumer().GetServiceRouter().SetChain([]string{config.DefaultServiceRouterCanary}) - cfg.GetConsumer().GetLocalCache().SetStartUseFileCache(false) - consumer, err := api.NewConsumerAPIByConfig(cfg) - c.Assert(err, check.IsNil) - defer consumer.Destroy() - - defer t.mockServer.ClearServiceInstances(t.testService) - defer t.mockServer.SetServiceMetadata(t.serviceToken, model.CanaryMetadataEnable, "false") - - getInstancesReq1 := &api.GetInstancesRequest{} - getInstancesReq1.FlowID = 1 - getInstancesReq1.Namespace = canaryNamespace - getInstancesReq1.Service = canaryService - resp, err := consumer.GetInstances(getInstancesReq1) - c.Assert(err, check.IsNil) - c.Assert(len(resp.GetInstances()), check.Equals, 6) - for _, v := range resp.GetInstances() { - c.Assert(v.IsIsolated(), check.Equals, false) - c.Assert(v.GetWeight() != 0, check.Equals, true) - } - - getInstancesReq1.Metadata = make(map[string]string) - getInstancesReq1.Canary = "useV1" - resp, err = consumer.GetInstances(getInstancesReq1) - c.Assert(err, check.IsNil) - c.Assert(len(resp.GetInstances()), check.Equals, 6) - for _, v := range resp.GetInstances() { - c.Assert(v.IsIsolated(), check.Equals, false) - c.Assert(v.GetWeight() != 0, check.Equals, true) - } -} - -// CircuitBreakerInstance Circuit Breaker Instance -func CircuitBreakerInstance(instance model.Instance, consumer api.ConsumerAPI, c *check.C) { - var errCode int32 - errCode = 1 - for i := 0; i < 20; i++ { - err := consumer.UpdateServiceCallResult( - &api.ServiceCallResult{ - ServiceCallResult: *((&model.ServiceCallResult{ - CalledInstance: instance, - RetStatus: model.RetFail, - RetCode: &errCode}).SetDelay(20 * time.Millisecond))}) - c.Assert(err, check.IsNil) - } -} - -// CloseCbInstance 关闭circuit breaker -func CloseCbInstance(instance model.Instance, consumer api.ConsumerAPI, c *check.C) { - var errCode int32 - errCode = 1 - for i := 0; i < 10; i++ { - // util.SelectInstanceSpecificNum(c, consumer, instance, 1, 200) - instance.GetCircuitBreakerStatus().Allocate() - err := consumer.UpdateServiceCallResult( - &api.ServiceCallResult{ - ServiceCallResult: *((&model.ServiceCallResult{ - CalledInstance: instance, - RetStatus: model.RetSuccess, - RetCode: &errCode}).SetDelay(20 * time.Millisecond))}) - c.Assert(err, check.IsNil) - } -} - -// CloseCbInstances is used to close the circuit breaker instance -func CloseCbInstances(namespace, service string, consumer api.ConsumerAPI, c *check.C, maxTimes int) { - request := &api.GetOneInstanceRequest{} - request.FlowID = 1111 - request.Namespace = namespace - request.Service = service - request.Timeout = model.ToDurationPtr(2 * time.Second) - var errCode int32 - errCode = 1 - for i := 0; i < maxTimes; i++ { - resp, err := consumer.GetOneInstance(request) - c.Assert(err, check.IsNil) - c.Assert(len(resp.Instances), check.Equals, 1) - err = consumer.UpdateServiceCallResult( - &api.ServiceCallResult{ - ServiceCallResult: *((&model.ServiceCallResult{ - CalledInstance: resp.Instances[0], - RetStatus: model.RetSuccess, - RetCode: &errCode}).SetDelay(20 * time.Millisecond))}) - c.Assert(err, check.IsNil) - } -} - -// CheckInstanceHasCanaryMeta is used to check the instance has canary metadata -func CheckInstanceHasCanaryMeta(instance model.Instance, canaryValue string) int { - metaData := instance.GetMetadata() - if metaData == nil { - return NormalInstance - } - for k, v := range metaData { - if k == model.CanaryMetaKey { - if v == canaryValue { - return CanaryInstance - } - return OtherCanaryInstance - } - } - return NormalInstance -} - -const ( - // NormalInstance is the normal instance - NormalInstance = 1 - // CanaryInstance is the canary instance - CanaryInstance = 2 - // OtherCanaryInstance is the other canary instance - OtherCanaryInstance = 3 -) - -// SplitInstances is used to split the instances into two groups -func SplitInstances(consumer api.ConsumerAPI, canaryVal string) map[int][]model.Instance { - getAllReq := &api.GetAllInstancesRequest{} - getAllReq.Namespace = canaryNamespace - getAllReq.Service = canaryService - respAll, _ := consumer.GetAllInstances(getAllReq) - RetMap := make(map[int][]model.Instance) - for _, inst := range respAll.GetInstances() { - insType := CheckInstanceHasCanaryMeta(inst, canaryVal) - RetMap[insType] = append(RetMap[insType], inst) - } - return RetMap -} - -func checkGetInstancesByCanaryType(consumer api.ConsumerAPI, instSize int, cType int, c *check.C) { - getInstancesReq1 := &api.GetInstancesRequest{} - getInstancesReq1.FlowID = 1 - getInstancesReq1.Namespace = canaryNamespace - getInstancesReq1.Service = canaryService - if cType == CanaryInstance { - getInstancesReq1.Canary = "useV1" - } - resp, err := consumer.GetInstances(getInstancesReq1) - c.Assert(err, check.IsNil) - c.Assert(len(resp.GetInstances()), check.Equals, instSize) - for _, v := range resp.GetInstances() { - c.Assert(v.IsIsolated(), check.Equals, false) - c.Assert(v.GetWeight() != 0, check.Equals, true) - c.Assert(CheckInstanceHasCanaryMeta(v, "useV1"), check.Equals, cType) - } -} - -// TestCanaryException01 异常测试, 服务启用金丝雀路由,有两个目标金丝雀实例, 1个正常实例, 1个其他版本金丝实例 -// 先一个目标金丝雀实例熔断 -- 只能获取到可用的一个金丝雀实例 -// 两个目标金丝雀实例熔断 -- 获取正常实例 -// 正常实例熔断 -- 获取到 其他版本金丝实例 -// 其他版本金丝实例熔断 -- 获取到金丝雀实例 -func (t *CanaryTestingSuite) TestCanaryException01(c *check.C) { - DeleteBackUpDir() - fmt.Println("-----------TestCanaryException01") - t.mockServer.GenTestInstancesWithMeta(t.testService, 2, map[string]string{model.CanaryMetaKey: "useV1"}) - t.mockServer.GenTestInstancesWithMeta(t.testService, 1, map[string]string{model.CanaryMetaKey: "useV2"}) - t.mockServer.GenTestInstances(t.testService, 2) - t.mockServer.SetServiceMetadata(t.serviceToken, model.CanaryMetadataEnable, "true") - cfg := config.NewDefaultConfiguration( - []string{fmt.Sprintf("%s:%d", canaryIPAddress, canaryPort)}) - cfg.GetConsumer().GetServiceRouter().SetChain([]string{config.DefaultServiceRouterCanary}) - cfg.GetConsumer().GetCircuitBreaker().SetSleepWindow(time.Second * 20) - cfg.GetConsumer().GetCircuitBreaker().SetCheckPeriod(time.Second * 3) - cfg.GetConsumer().GetCircuitBreaker().GetErrorCountConfig().SetMetricStatTimeWindow(time.Second * 5) - cfg.GetConsumer().GetCircuitBreaker().GetErrorRateConfig().SetMetricStatTimeWindow(time.Second * 5) - cfg.GetConsumer().GetLocalCache().SetStartUseFileCache(false) - consumer, err := api.NewConsumerAPIByConfig(cfg) - c.Assert(err, check.IsNil) - defer consumer.Destroy() - - defer t.mockServer.ClearServiceInstances(t.testService) - defer t.mockServer.SetServiceMetadata(t.serviceToken, model.CanaryMetadataEnable, "false") - - instMap := SplitInstances(consumer, "useV1") - _ = instMap - - getInstancesReq1 := &api.GetInstancesRequest{} - getInstancesReq1.FlowID = 1 - getInstancesReq1.Namespace = canaryNamespace - getInstancesReq1.Service = canaryService - getInstancesReq1.Canary = "useV1" - resp, err := consumer.GetInstances(getInstancesReq1) - c.Assert(err, check.IsNil) - c.Assert(len(resp.GetInstances()), check.Equals, 2) - for _, v := range resp.GetInstances() { - c.Assert(v.IsIsolated(), check.Equals, false) - c.Assert(v.GetWeight() != 0, check.Equals, true) - isCanary := false - if v.GetMetadata() != nil { - if v1, ok := v.GetMetadata()[model.CanaryMetaKey]; ok { - if v1 == "useV1" { - isCanary = true - } - } - } - c.Assert(isCanary, check.Equals, true) - } - var tarIns1 = resp.GetInstances()[0] - var tarIns2 = resp.GetInstances()[1] - CircuitBreakerInstance(tarIns1, consumer, c) - time.Sleep(time.Second * 5) - c.Assert(tarIns1.GetCircuitBreakerStatus().GetStatus(), check.Equals, model.Open) - var getInstancesReq *api.GetOneInstanceRequest - getInstancesReq = &api.GetOneInstanceRequest{} - getInstancesReq.FlowID = 1 - getInstancesReq.Namespace = canaryNamespace - getInstancesReq.Service = canaryService - getInstancesReq.Metadata = make(map[string]string) - getInstancesReq.Canary = "useV1" - - for i := 0; i < 100; i++ { - resp, err := consumer.GetOneInstance(getInstancesReq) - c.Assert(err, check.IsNil) - c.Assert(len(resp.GetInstances()), check.Equals, 1) - instance := resp.GetInstances()[0] - c.Assert(instance.GetId(), check.Equals, tarIns2.GetId()) - } - CircuitBreakerInstance(tarIns2, consumer, c) - time.Sleep(time.Second * 5) - c.Assert(tarIns2.GetCircuitBreakerStatus().GetStatus(), check.Equals, model.Open) - - for i := 0; i < 100; i++ { - resp, err := consumer.GetOneInstance(getInstancesReq) - c.Assert(err, check.IsNil) - c.Assert(len(resp.GetInstances()), check.Equals, 1) - instance := resp.GetInstances()[0] - c.Assert(CheckInstanceHasCanaryMeta(instance, "useV1"), check.Equals, NormalInstance) - } - - for _, v := range instMap[NormalInstance] { - ins := v - CircuitBreakerInstance(ins, consumer, c) - } - time.Sleep(time.Second * 5) - - for _, v := range instMap[NormalInstance] { - c.Assert(v.GetCircuitBreakerStatus(), check.NotNil) - c.Assert(v.GetCircuitBreakerStatus().GetStatus(), check.Equals, model.Open) - } - - for i := 0; i < 100; i++ { - resp, err := consumer.GetOneInstance(getInstancesReq) - c.Assert(err, check.IsNil) - c.Assert(len(resp.GetInstances()), check.Equals, 1) - instance := resp.GetInstances()[0] - c.Assert(CheckInstanceHasCanaryMeta(instance, "useV1"), check.Equals, OtherCanaryInstance) - } - - for _, v := range instMap[OtherCanaryInstance] { - ins := v - CircuitBreakerInstance(ins, consumer, c) - } - time.Sleep(time.Second * 5) - for _, v := range instMap[OtherCanaryInstance] { - c.Assert(v.GetCircuitBreakerStatus().GetStatus(), check.Equals, model.Open) - } - - for i := 0; i < 100; i++ { - resp, err := consumer.GetOneInstance(getInstancesReq) - c.Assert(err, check.IsNil) - c.Assert(len(resp.GetInstances()), check.Equals, 1) - instance := resp.GetInstances()[0] - c.Assert(CheckInstanceHasCanaryMeta(instance, "useV1"), check.Equals, CanaryInstance) - } - - time.Sleep(time.Second * 20) - log.Printf("len(instMap[CanaryInstance]): %v", len(instMap[CanaryInstance])) - for _, v := range instMap[CanaryInstance] { - ins := v - fmt.Printf("[CanaryInstance] %+v", ins) - CloseCbInstance(ins, consumer, c) - } - time.Sleep(time.Second * 10) - for _, v := range instMap[CanaryInstance] { - fmt.Printf("[CanaryInstance] %+v", v) - c.Assert(v.GetCircuitBreakerStatus().GetStatus(), check.Equals, model.Close) - } - for i := 0; i < 3; i++ { - checkGetInstancesByCanaryType(consumer, 2, CanaryInstance, c) - } -} - -// TestCanaryException02 异常测试, 服务启用金丝雀路由,测试不带金丝雀标签, 有1个目标金丝雀实例, 2个正常实例, 1个其他版本金丝实例 -// 先一个目标正常实例熔断 -- 只能获取到可用的一个正常实例 -// 两个目标正常实例熔断 -- 获取正常带金丝雀标签实例(共2个) -// 带金丝雀标签实例熔断 -- 获取到正常实例 -func (t *CanaryTestingSuite) TestCanaryException02(c *check.C) { - defer util.DeleteDir("/Users/angevil/polaris/backup") - t.mockServer.GenTestInstancesWithMeta(t.testService, 2, map[string]string{model.CanaryMetaKey: "useV1"}) - t.mockServer.GenTestInstancesWithMeta(t.testService, 1, map[string]string{model.CanaryMetaKey: "useV2"}) - t.mockServer.GenTestInstances(t.testService, 2) - t.mockServer.SetServiceMetadata(t.serviceToken, model.CanaryMetadataEnable, "true") - cfg := config.NewDefaultConfiguration( - []string{fmt.Sprintf("%s:%d", canaryIPAddress, canaryPort)}) - cfg.GetConsumer().GetServiceRouter().SetChain([]string{config.DefaultServiceRouterCanary}) - cfg.GetConsumer().GetCircuitBreaker().SetSleepWindow(time.Second * 20) - cfg.GetConsumer().GetCircuitBreaker().SetCheckPeriod(time.Second * 3) - cfg.GetConsumer().GetCircuitBreaker().GetErrorCountConfig().SetMetricStatTimeWindow(time.Second * 5) - cfg.GetConsumer().GetCircuitBreaker().GetErrorRateConfig().SetMetricStatTimeWindow(time.Second * 5) - cfg.GetConsumer().GetLocalCache().SetStartUseFileCache(false) - consumer, err := api.NewConsumerAPIByConfig(cfg) - c.Assert(err, check.IsNil) - defer consumer.Destroy() - - defer t.mockServer.ClearServiceInstances(t.testService) - defer t.mockServer.SetServiceMetadata(t.serviceToken, model.CanaryMetadataEnable, "false") - - instMap := SplitInstances(consumer, "useV1") - _ = instMap - - getInstancesReq1 := &api.GetInstancesRequest{} - getInstancesReq1.FlowID = 1 - getInstancesReq1.Namespace = canaryNamespace - getInstancesReq1.Service = canaryService - getInstancesReq1.Metadata = make(map[string]string) - getInstancesReq1.Metadata[model.CanaryMetaKey] = "" - resp, err := consumer.GetInstances(getInstancesReq1) - c.Assert(err, check.IsNil) - c.Assert(len(resp.GetInstances()), check.Equals, 2) - for _, v := range resp.GetInstances() { - c.Assert(CheckInstanceHasCanaryMeta(v, "useV1"), check.Equals, NormalInstance) - } - var tarIns1 model.Instance = resp.GetInstances()[0] - var tarIns2 model.Instance = resp.GetInstances()[1] - CircuitBreakerInstance(tarIns1, consumer, c) - time.Sleep(time.Second * 5) - c.Assert(tarIns1.GetCircuitBreakerStatus().GetStatus(), check.Equals, model.Open) - var getInstancesReq *api.GetOneInstanceRequest - getInstancesReq = &api.GetOneInstanceRequest{} - getInstancesReq.FlowID = 1 - getInstancesReq.Namespace = canaryNamespace - getInstancesReq.Service = canaryService - getInstancesReq.Metadata = make(map[string]string) - getInstancesReq.Canary = "" - for i := 0; i < 100; i++ { - resp, err := consumer.GetOneInstance(getInstancesReq) - c.Assert(err, check.IsNil) - c.Assert(len(resp.GetInstances()), check.Equals, 1) - instance := resp.GetInstances()[0] - c.Assert(instance.GetId(), check.Equals, tarIns2.GetId()) - } - CircuitBreakerInstance(tarIns2, consumer, c) - time.Sleep(time.Second * 5) - c.Assert(tarIns2.GetCircuitBreakerStatus().GetStatus(), check.Equals, model.Open) - - for i := 0; i < 100; i++ { - resp, err := consumer.GetOneInstance(getInstancesReq) - c.Assert(err, check.IsNil) - c.Assert(len(resp.GetInstances()), check.Equals, 1) - instance := resp.GetInstances()[0] - insType := CheckInstanceHasCanaryMeta(instance, "useV1") - c.Assert(insType == CanaryInstance || insType == OtherCanaryInstance, check.Equals, true) - } - - for _, v := range instMap[CanaryInstance] { - CircuitBreakerInstance(v, consumer, c) - } - for _, v := range instMap[OtherCanaryInstance] { - CircuitBreakerInstance(v, consumer, c) - } - time.Sleep(time.Second * 5) - for _, v := range instMap[CanaryInstance] { - c.Assert(v.GetCircuitBreakerStatus().GetStatus(), check.Equals, model.Open) - } - for _, v := range instMap[OtherCanaryInstance] { - c.Assert(v.GetCircuitBreakerStatus().GetStatus(), check.Equals, model.Open) - } - for i := 0; i < 100; i++ { - resp, err := consumer.GetOneInstance(getInstancesReq) - c.Assert(err, check.IsNil) - c.Assert(len(resp.GetInstances()), check.Equals, 1) - instance := resp.GetInstances()[0] - c.Assert(CheckInstanceHasCanaryMeta(instance, "useV1"), check.Equals, NormalInstance) - } -} - -// TestCanaryException03 异常测试, 服务启用金丝雀路由,测试带金丝雀标签, 有1个目标金丝雀实例, 1个正常实例 -// 获取到金丝雀实例 -// 金丝雀实例熔断, 获取到正常实例 -// 金丝雀实例恢复, 获取到金丝雀实例 -func (t *CanaryTestingSuite) TestCanaryException03(c *check.C) { - DeleteBackUpDir() - t.mockServer.GenTestInstancesWithMeta(t.testService, 1, map[string]string{model.CanaryMetaKey: "useV1"}) - t.mockServer.GenTestInstances(t.testService, 1) - t.mockServer.SetServiceMetadata(t.serviceToken, model.CanaryMetadataEnable, "true") - cfg := config.NewDefaultConfiguration( - []string{fmt.Sprintf("%s:%d", canaryIPAddress, canaryPort)}) - cfg.GetConsumer().GetServiceRouter().SetChain([]string{config.DefaultServiceRouterCanary}) - cfg.GetConsumer().GetCircuitBreaker().SetSleepWindow(time.Second * 20) - cfg.GetConsumer().GetCircuitBreaker().SetCheckPeriod(time.Second * 3) - cfg.GetConsumer().GetCircuitBreaker().GetErrorCountConfig().SetMetricStatTimeWindow(time.Second * 5) - cfg.GetConsumer().GetCircuitBreaker().GetErrorRateConfig().SetMetricStatTimeWindow(time.Second * 5) - cfg.GetConsumer().GetLocalCache().SetStartUseFileCache(false) - consumer, err := api.NewConsumerAPIByConfig(cfg) - c.Assert(err, check.IsNil) - defer consumer.Destroy() - - defer t.mockServer.ClearServiceInstances(t.testService) - defer t.mockServer.SetServiceMetadata(t.serviceToken, model.CanaryMetadataEnable, "false") - - instMap := SplitInstances(consumer, "useV1") - _ = instMap - - var tarIns model.Instance - var getInstancesReq *api.GetOneInstanceRequest - getInstancesReq = &api.GetOneInstanceRequest{} - getInstancesReq.FlowID = 1 - getInstancesReq.Namespace = canaryNamespace - getInstancesReq.Service = canaryService - getInstancesReq.Metadata = make(map[string]string) - getInstancesReq.Canary = "useV1" - for i := 0; i < 100; i++ { - resp, err := consumer.GetOneInstance(getInstancesReq) - c.Assert(err, check.IsNil) - c.Assert(len(resp.GetInstances()), check.Equals, 1) - instance := resp.GetInstances()[0] - c.Assert(CheckInstanceHasCanaryMeta(instance, "useV1"), check.Equals, CanaryInstance) - tarIns = instance - } - CircuitBreakerInstance(tarIns, consumer, c) - time.Sleep(time.Second * 5) - c.Assert(tarIns.GetCircuitBreakerStatus().GetStatus(), check.Equals, model.Open) - - for i := 0; i < 100; i++ { - resp, err := consumer.GetOneInstance(getInstancesReq) - c.Assert(err, check.IsNil) - c.Assert(len(resp.GetInstances()), check.Equals, 1) - instance := resp.GetInstances()[0] - insType := CheckInstanceHasCanaryMeta(instance, "useV1") - c.Assert(insType, check.Equals, NormalInstance) - } - time.Sleep(time.Second * 22) - CloseCbInstance(tarIns, consumer, c) - time.Sleep(time.Second * 5) - for i := 0; i < 100; i++ { - resp, err := consumer.GetOneInstance(getInstancesReq) - c.Assert(err, check.IsNil) - c.Assert(len(resp.GetInstances()), check.Equals, 1) - instance := resp.GetInstances()[0] - c.Assert(CheckInstanceHasCanaryMeta(instance, "useV1"), check.Equals, CanaryInstance) - } -} - -// TestCanaryException04 异常测试, 服务启用金丝雀路由,测试不带带金丝雀标签, 有1个目标金丝雀实例, 1个正常实例 -// 获取到正常实例 -// 正常实例熔断, 获取到金丝雀实例 -// 正常实例恢复, 获取到正常实例 -func (t *CanaryTestingSuite) TestCanaryException04(c *check.C) { - DeleteBackUpDir() - t.mockServer.GenTestInstancesWithMeta(t.testService, 1, map[string]string{model.CanaryMetaKey: "useV1"}) - t.mockServer.GenTestInstances(t.testService, 1) - t.mockServer.SetServiceMetadata(t.serviceToken, model.CanaryMetadataEnable, "true") - cfg := config.NewDefaultConfiguration( - []string{fmt.Sprintf("%s:%d", canaryIPAddress, canaryPort)}) - cfg.GetConsumer().GetServiceRouter().SetChain([]string{config.DefaultServiceRouterCanary}) - cfg.GetConsumer().GetCircuitBreaker().SetSleepWindow(time.Second * 20) - cfg.GetConsumer().GetCircuitBreaker().SetCheckPeriod(time.Second * 3) - cfg.GetConsumer().GetCircuitBreaker().GetErrorCountConfig().SetMetricStatTimeWindow(time.Second * 5) - cfg.GetConsumer().GetCircuitBreaker().GetErrorRateConfig().SetMetricStatTimeWindow(time.Second * 5) - cfg.GetConsumer().GetLocalCache().SetStartUseFileCache(false) - consumer, err := api.NewConsumerAPIByConfig(cfg) - c.Assert(err, check.IsNil) - defer consumer.Destroy() - - defer t.mockServer.ClearServiceInstances(t.testService) - defer t.mockServer.SetServiceMetadata(t.serviceToken, model.CanaryMetadataEnable, "false") - - instMap := SplitInstances(consumer, "useV1") - _ = instMap - - var tarIns model.Instance - var getInstancesReq *api.GetOneInstanceRequest - getInstancesReq = &api.GetOneInstanceRequest{} - getInstancesReq.FlowID = 1 - getInstancesReq.Namespace = canaryNamespace - getInstancesReq.Service = canaryService - for i := 0; i < 100; i++ { - resp, err := consumer.GetOneInstance(getInstancesReq) - c.Assert(err, check.IsNil) - c.Assert(len(resp.GetInstances()), check.Equals, 1) - instance := resp.GetInstances()[0] - c.Assert(CheckInstanceHasCanaryMeta(instance, "useV1"), check.Equals, NormalInstance) - tarIns = instance - } - CircuitBreakerInstance(tarIns, consumer, c) - time.Sleep(time.Second * 5) - c.Assert(tarIns.GetCircuitBreakerStatus().GetStatus(), check.Equals, model.Open) - - for i := 0; i < 100; i++ { - resp, err := consumer.GetOneInstance(getInstancesReq) - c.Assert(err, check.IsNil) - c.Assert(len(resp.GetInstances()), check.Equals, 1) - instance := resp.GetInstances()[0] - insType := CheckInstanceHasCanaryMeta(instance, "useV1") - c.Assert(insType, check.Equals, CanaryInstance) - } - time.Sleep(time.Second * 22) - CloseCbInstance(tarIns, consumer, c) - time.Sleep(time.Second * 5) - for i := 0; i < 100; i++ { - resp, err := consumer.GetOneInstance(getInstancesReq) - c.Assert(err, check.IsNil) - c.Assert(len(resp.GetInstances()), check.Equals, 1) - instance := resp.GetInstances()[0] - c.Assert(CheckInstanceHasCanaryMeta(instance, "useV1"), check.Equals, NormalInstance) - } -} - -// TestCanaryNormal04 测试canary切换到normal -// 和SetDivision一起使用 -// set路由过滤后有金丝雀实例 -func (t *CanaryTestingSuite) TestCanaryNormal04(c *check.C) { - DeleteBackUpDir() - inst1MetaMap := make(map[string]string) - inst1MetaMap[model.CanaryMetaKey] = "isCanary" - inst1MetaMap[internalSetEnableKey] = setEnable - inst1MetaMap[internalSetNameKey] = "set1" - t.mockServer.GenTestInstancesWithMeta(t.testService, 1, inst1MetaMap) - t.mockServer.GenTestInstancesWithMeta(t.testService, 1, inst1MetaMap) - - inst2MetaMap := make(map[string]string) - inst2MetaMap[model.CanaryMetaKey] = "AnotherCanary" - inst2MetaMap[internalSetEnableKey] = setEnable - inst2MetaMap[internalSetNameKey] = "set1" - t.mockServer.GenTestInstancesWithMeta(t.testService, 1, inst2MetaMap) - - inst3MetaMap := make(map[string]string) - inst3MetaMap[internalSetEnableKey] = setEnable - inst3MetaMap[internalSetNameKey] = "set1" - t.mockServer.GenTestInstancesWithMeta(t.testService, 1, inst3MetaMap) - t.mockServer.GenTestInstancesWithMeta(t.testService, 1, inst3MetaMap) - - t.mockServer.SetServiceMetadata(t.serviceToken, model.CanaryMetadataEnable, "true") - cfg := config.NewDefaultConfiguration( - []string{fmt.Sprintf("%s:%d", canaryIPAddress, canaryPort)}) - cfg.GetConsumer().GetServiceRouter().SetChain([]string{config.DefaultServiceRouterSetDivision, - config.DefaultServiceRouterCanary}) - cfg.GetConsumer().GetLocalCache().SetStartUseFileCache(false) - consumer, err := api.NewConsumerAPIByConfig(cfg) - c.Assert(err, check.IsNil) - defer consumer.Destroy() - - defer t.mockServer.ClearServiceInstances(t.testService) - defer t.mockServer.SetServiceMetadata(t.serviceToken, model.CanaryMetadataEnable, "false") - - // 测试不带金丝雀标签 - var getOneInstanceReq *api.GetOneInstanceRequest - getOneInstanceReq = &api.GetOneInstanceRequest{} - getOneInstanceReq.FlowID = 1 - getOneInstanceReq.Namespace = canaryNamespace - getOneInstanceReq.Service = canaryService - getOneInstanceReq.SourceService = &model.ServiceInfo{ - Namespace: canaryNamespace, - Service: canaryService, - Metadata: map[string]string{internalSetNameKey: "set1"}, - } - for i := 0; i < 10; i++ { - resp, err := consumer.GetOneInstance(getOneInstanceReq) - c.Assert(err, check.IsNil) - c.Assert(len(resp.GetInstances()), check.Equals, 1) - instance := resp.GetInstances()[0] - c.Assert(CheckInstanceHasCanaryMeta(instance, "isCanary"), check.Equals, NormalInstance) - } - getInstancesReq1 := &api.GetInstancesRequest{} - getInstancesReq1.FlowID = 1 - getInstancesReq1.Namespace = canaryNamespace - getInstancesReq1.Service = canaryService - getInstancesReq1.SourceService = &model.ServiceInfo{ - Namespace: canaryNamespace, - Service: canaryService, - Metadata: map[string]string{internalSetNameKey: "set1"}, - } - resp, err := consumer.GetInstances(getInstancesReq1) - c.Assert(err, check.IsNil) - c.Assert(len(resp.GetInstances()), check.Equals, 2) - for _, v := range resp.GetInstances() { - c.Assert(v.IsIsolated(), check.Equals, false) - c.Assert(v.GetWeight() != 0, check.Equals, true) - fmt.Println(v.GetId(), v.GetMetadata()) - c.Assert(CheckInstanceHasCanaryMeta(v, "isCanary"), check.Equals, NormalInstance) - } - - // 测试带金丝雀标签 - getOneInstanceReq.Canary = "isCanary" - for i := 0; i < 10; i++ { - resp, err := consumer.GetOneInstance(getOneInstanceReq) - c.Assert(err, check.IsNil) - c.Assert(len(resp.GetInstances()), check.Equals, 1) - instance := resp.GetInstances()[0] - c.Assert(CheckInstanceHasCanaryMeta(instance, "isCanary"), check.Equals, CanaryInstance) - } - - getInstancesReq1.Canary = "isCanary" - resp, err = consumer.GetInstances(getInstancesReq1) - c.Assert(err, check.IsNil) - c.Assert(len(resp.GetInstances()), check.Equals, 2) - for _, v := range resp.GetInstances() { - c.Assert(v.IsIsolated(), check.Equals, false) - c.Assert(v.GetWeight() != 0, check.Equals, true) - c.Assert(CheckInstanceHasCanaryMeta(v, "isCanary"), check.Equals, CanaryInstance) - } -} - -func (t *CanaryTestingSuite) addInstance(region, zone, campus string, health bool, metaData map[string]string) { - location := &apimodel.Location{ - Region: &wrappers.StringValue{Value: region}, - Zone: &wrappers.StringValue{Value: zone}, - Campus: &wrappers.StringValue{Value: campus}, - } - ins := &service_manage.Instance{ - Id: &wrappers.StringValue{Value: uuid.New().String()}, - Service: &wrappers.StringValue{Value: canaryService}, - Namespace: &wrappers.StringValue{Value: canaryNamespace}, - Host: &wrappers.StringValue{Value: srIPAddr}, - Port: &wrappers.UInt32Value{Value: uint32(srPort)}, - Weight: &wrappers.UInt32Value{Value: uint32(100)}, - Healthy: &wrappers.BoolValue{Value: health}, - Location: location} - testService := &service_manage.Service{ - Name: &wrappers.StringValue{Value: canaryService}, - Namespace: &wrappers.StringValue{Value: canaryNamespace}, - Token: &wrappers.StringValue{Value: t.serviceToken}, - } - if metaData != nil { - ins.Metadata = metaData - } - t.mockServer.RegisterServiceInstances(testService, []*service_manage.Instance{ins}) -} - -// TestCanaryNormal05 和nearbyRouter一起使用 -func (t *CanaryTestingSuite) TestCanaryNormal05(c *check.C) { - DeleteBackUpDir() - inst1MetaMap := make(map[string]string) - inst1MetaMap[model.CanaryMetaKey] = "isCanary" - t.addInstance("A", "a", "1", true, inst1MetaMap) - t.addInstance("A", "a", "1", true, inst1MetaMap) - t.addInstance("A", "a", "1", true, nil) - t.addInstance("A", "a", "1", true, nil) - t.addInstance("A", "a", "1", true, nil) - t.mockServer.SetServiceMetadata(t.serviceToken, model.CanaryMetadataEnable, "true") - - cfg := config.NewDefaultConfiguration( - []string{fmt.Sprintf("%s:%d", canaryIPAddress, canaryPort)}) - cfg.GetConsumer().GetServiceRouter().SetChain([]string{config.DefaultServiceRouterNearbyBased, - config.DefaultServiceRouterCanary}) - cfg.GetConsumer().GetLocalCache().SetStartUseFileCache(false) - consumer, err := api.NewConsumerAPIByConfig(cfg) - c.Assert(err, check.IsNil) - defer consumer.Destroy() - - defer t.mockServer.ClearServiceInstances(t.testService) - defer t.mockServer.SetServiceMetadata(t.serviceToken, model.CanaryMetadataEnable, "false") - - var getOneInstanceReq *api.GetOneInstanceRequest - getOneInstanceReq = &api.GetOneInstanceRequest{} - getOneInstanceReq.FlowID = 1 - getOneInstanceReq.Namespace = canaryNamespace - getOneInstanceReq.Service = canaryService - - // 测试不带金丝雀标签 - for i := 0; i < 100; i++ { - resp, err := consumer.GetOneInstance(getOneInstanceReq) - c.Assert(err, check.IsNil) - c.Assert(len(resp.GetInstances()), check.Equals, 1) - instance := resp.GetInstances()[0] - c.Assert(CheckInstanceHasCanaryMeta(instance, "isCanary"), check.Equals, NormalInstance) - } - getInstancesReq1 := &api.GetInstancesRequest{} - getInstancesReq1.FlowID = 1 - getInstancesReq1.Namespace = canaryNamespace - getInstancesReq1.Service = canaryService - resp, err := consumer.GetInstances(getInstancesReq1) - c.Assert(err, check.IsNil) - c.Assert(len(resp.GetInstances()), check.Equals, 3) - for _, v := range resp.GetInstances() { - c.Assert(v.IsIsolated(), check.Equals, false) - c.Assert(v.GetWeight() != 0, check.Equals, true) - c.Assert(CheckInstanceHasCanaryMeta(v, "isCanary"), check.Equals, NormalInstance) - } - - // 测试带金丝雀标签 - getOneInstanceReq.Canary = "isCanary" - for i := 0; i < 100; i++ { - resp, err := consumer.GetOneInstance(getOneInstanceReq) - c.Assert(err, check.IsNil) - c.Assert(len(resp.GetInstances()), check.Equals, 1) - instance := resp.GetInstances()[0] - c.Assert(CheckInstanceHasCanaryMeta(instance, "isCanary"), check.Equals, CanaryInstance) - } - getInstancesReq1.Canary = "isCanary" - resp, err = consumer.GetInstances(getInstancesReq1) - c.Assert(err, check.IsNil) - c.Assert(len(resp.GetInstances()), check.Equals, 2) - for _, v := range resp.GetInstances() { - c.Assert(v.IsIsolated(), check.Equals, false) - c.Assert(v.GetWeight() != 0, check.Equals, true) - c.Assert(CheckInstanceHasCanaryMeta(v, "isCanary"), check.Equals, CanaryInstance) - } -} - -// DeleteBackUpDir 删除备份目录 -func DeleteBackUpDir() { - current, err := user.Current() - if err != nil { - log.Fatalf(err.Error()) - } - homeDir := current.HomeDir - fmt.Printf("Home Directory: %s\n", homeDir) - var filePath = fmt.Sprintf("%s/polaris/backup", homeDir) - util.DeleteDir(filePath) -} diff --git a/test/testdata/cache.yaml b/test/testdata/cache.yaml index 93aea273..96a65339 100644 --- a/test/testdata/cache.yaml +++ b/test/testdata/cache.yaml @@ -42,22 +42,8 @@ consumer: plugin: {} circuitBreaker: enable: true - checkPeriod: 5s - requestCountAfterHalfOpen: 3 - sleepWindow: 5s - successCountAfterHalfOpen: 2 chain: - - errorCount - - errorRate - plugin: - errorCount: - continuousErrorThreshold: 10 - metricStatTimeWindow: 1s - errorRate: - errorRateThreshold: 0.5 - metricNumBuckets: 12 - metricStatTimeWindow: 1m0s - requestVolumeThreshold: 10 + - composite outlierDetection: enable: true checkPeriod: 10s diff --git a/test/testdata/circuitbreaker.yaml b/test/testdata/circuitbreaker.yaml index 5476b058..fa5b778c 100644 --- a/test/testdata/circuitbreaker.yaml +++ b/test/testdata/circuitbreaker.yaml @@ -56,25 +56,7 @@ consumer: successCountAfterHalfOpen: 2 # 熔断插件配置 chain: - # 按照错误率进行熔断 - - errorRate - # 按照连续请求错误进行熔断 - - errorCount - plugin: - errorRate: - # 只有请求数达到某个阈值才执行熔断计算,默认10 - requestVolumeThreshold: 12 - # 触发熔断的错误率阈值,默认0.5 - errorRateThreshold: 0.5 - # 错误率统计时间窗口,默认1分钟 - metricStatTimeWindow: 5s - # 统计窗口细分的桶数量,默认12 - metricNumBuckets: 12 - errorCount: - # 连续错误数阈值 - continuousErrorThreshold: 10 - # 连续错误数统计时间窗口,默认1秒 - metricStatTimeWindow: 1s + - composite loadbalancer: # 负载均衡类型 type: weightedRandom diff --git a/test/testdata/conf_test.yaml b/test/testdata/conf_test.yaml index dc3da734..3a8ddb7d 100644 --- a/test/testdata/conf_test.yaml +++ b/test/testdata/conf_test.yaml @@ -45,25 +45,7 @@ consumer: successCountAfterHalfOpen: 2 # 熔断插件配置 chain: - # 按照错误率进行熔断 - - errorRate - # 按照连续请求错误进行熔断 - - errorCount - plugin: - errorRate: - # 只有请求数达到某个阈值才执行熔断计算,默认10 - requestVolumeThreshold: 10 - # 触发熔断的错误率阈值,默认0.5 - errorRateThreshold: 0.5 - # 错误率统计时间窗口,默认1分钟 - metricStatTimeWindow: 5s - # 统计窗口细分的桶数量,默认12 - metricNumBuckets: 12 - errorCount: - # 连续错误数阈值 - continuousErrorThreshold: 10 - # 连续错误数统计时间窗口,默认1秒 - metricStatTimeWindow: 1s + - composite outlierDetection: # 是否开启探测 enable: true diff --git a/test/testdata/conf_test2.yaml b/test/testdata/conf_test2.yaml index 5f2f2b55..a1101f57 100644 --- a/test/testdata/conf_test2.yaml +++ b/test/testdata/conf_test2.yaml @@ -42,25 +42,7 @@ consumer: successCountAfterHalfOpen: 2 # 熔断插件配置 chain: - # 按照错误率进行熔断 - - errorRate - # 按照连续请求错误进行熔断 - - errorCount - plugin: - errorRate: - # 只有请求数达到某个阈值才执行熔断计算,默认10 - requestVolumeThreshold: 10 - # 触发熔断的错误率阈值,默认0.5 - errorRateThreshold: 0.5 - # 错误率统计时间窗口,默认1分钟 - metricStatTimeWindow: 5s - # 统计窗口细分的桶数量,默认12 - metricNumBuckets: 12 - errorCount: - # 连续错误数阈值 - continuousErrorThreshold: 10 - # 连续错误数统计时间窗口,默认1秒 - metricStatTimeWindow: 1s + - composite outlierDetection: # 是否开启探测 enable: true diff --git a/test/testdata/conf_test3.yaml b/test/testdata/conf_test3.yaml index 5f2f2b55..a1101f57 100644 --- a/test/testdata/conf_test3.yaml +++ b/test/testdata/conf_test3.yaml @@ -42,25 +42,7 @@ consumer: successCountAfterHalfOpen: 2 # 熔断插件配置 chain: - # 按照错误率进行熔断 - - errorRate - # 按照连续请求错误进行熔断 - - errorCount - plugin: - errorRate: - # 只有请求数达到某个阈值才执行熔断计算,默认10 - requestVolumeThreshold: 10 - # 触发熔断的错误率阈值,默认0.5 - errorRateThreshold: 0.5 - # 错误率统计时间窗口,默认1分钟 - metricStatTimeWindow: 5s - # 统计窗口细分的桶数量,默认12 - metricNumBuckets: 12 - errorCount: - # 连续错误数阈值 - continuousErrorThreshold: 10 - # 连续错误数统计时间窗口,默认1秒 - metricStatTimeWindow: 1s + - composite outlierDetection: # 是否开启探测 enable: true diff --git a/test/testdata/conf_test4.yaml b/test/testdata/conf_test4.yaml index 5f2f2b55..a1101f57 100644 --- a/test/testdata/conf_test4.yaml +++ b/test/testdata/conf_test4.yaml @@ -42,25 +42,7 @@ consumer: successCountAfterHalfOpen: 2 # 熔断插件配置 chain: - # 按照错误率进行熔断 - - errorRate - # 按照连续请求错误进行熔断 - - errorCount - plugin: - errorRate: - # 只有请求数达到某个阈值才执行熔断计算,默认10 - requestVolumeThreshold: 10 - # 触发熔断的错误率阈值,默认0.5 - errorRateThreshold: 0.5 - # 错误率统计时间窗口,默认1分钟 - metricStatTimeWindow: 5s - # 统计窗口细分的桶数量,默认12 - metricNumBuckets: 12 - errorCount: - # 连续错误数阈值 - continuousErrorThreshold: 10 - # 连续错误数统计时间窗口,默认1秒 - metricStatTimeWindow: 1s + - composite outlierDetection: # 是否开启探测 enable: true diff --git a/test/testdata/consumer.yaml b/test/testdata/consumer.yaml index 40ddeab4..eb6a6d65 100644 --- a/test/testdata/consumer.yaml +++ b/test/testdata/consumer.yaml @@ -56,25 +56,7 @@ consumer: successCountAfterHalfOpen: 2 # 熔断插件配置 chain: - # 按照错误率进行熔断 - - errorRate - # 按照连续请求错误进行熔断 - - errorCount - plugin: - errorRate: - # 只有请求数达到某个阈值才执行熔断计算,默认10 - requestVolumeThreshold: 10 - # 触发熔断的错误率阈值,默认0.5 - errorRateThreshold: 0.5 - # 错误率统计时间窗口,默认1分钟 - metricStatTimeWindow: 5s - # 统计窗口细分的桶数量,默认12 - metricNumBuckets: 12 - errorCount: - # 连续错误数阈值 - continuousErrorThreshold: 10 - # 连续错误数统计时间窗口,默认1秒 - metricStatTimeWindow: 1s + - composite outlierDetection: # 是否开启探测 enable: true diff --git a/test/testdata/healthcheck.yaml b/test/testdata/healthcheck.yaml index 577b2929..43fe1f90 100644 --- a/test/testdata/healthcheck.yaml +++ b/test/testdata/healthcheck.yaml @@ -46,25 +46,7 @@ consumer: successCountAfterHalfOpen: 2 # 熔断插件配置 chain: - # 按照错误率进行熔断 - - errorRate - # 按照连续请求错误进行熔断 - - errorCount - plugin: - errorRate: - # 只有请求数达到某个阈值才执行熔断计算,默认10 - requestVolumeThreshold: 12 - # 触发熔断的错误率阈值,默认0.5 - errorRateThreshold: 0.5 - # 错误率统计时间窗口,默认1分钟 - metricStatTimeWindow: 5s - # 统计窗口细分的桶数量,默认12 - metricNumBuckets: 12 - errorCount: - # 连续错误数阈值 - continuousErrorThreshold: 10 - # 连续错误数统计时间窗口,默认1秒 - metricStatTimeWindow: 1s + - composite healthCheck: # 何时开启探测 when: on_recover diff --git a/test/testdata/monitor.yaml b/test/testdata/monitor.yaml index 83072f52..539df1e6 100644 --- a/test/testdata/monitor.yaml +++ b/test/testdata/monitor.yaml @@ -46,17 +46,7 @@ consumer: sleepWindow: 5s successCountAfterHalfOpen: 2 chain: - - errorCount - - errorRate - plugin: - errorCount: - continuousErrorThreshold: 10 - metricStatTimeWindow: 1s - errorRate: - errorRateThreshold: 0.5 - metricNumBuckets: 12 - metricStatTimeWindow: 1m0s - requestCountAfterHalfOpen: 3 + - composite outlierDetection: enable: true checkPeriod: 10s diff --git a/test/testdata/service_sp_conf.yaml b/test/testdata/service_sp_conf.yaml index 0c9e35a8..77350c6f 100644 --- a/test/testdata/service_sp_conf.yaml +++ b/test/testdata/service_sp_conf.yaml @@ -53,25 +53,7 @@ consumer: successCountAfterHalfOpen: 2 # 熔断插件配置 chain: - # 按照错误率进行熔断 - - errorRate - # 按照连续请求错误进行熔断 - - errorCount - plugin: - errorRate: - # 只有请求数达到某个阈值才执行熔断计算,默认10 - requestVolumeThreshold: 10 - # 触发熔断的错误率阈值,默认0.5 - errorRateThreshold: 0.5 - # 错误率统计时间窗口,默认1分钟 - metricStatTimeWindow: 5s - # 统计窗口细分的桶数量,默认12 - metricNumBuckets: 12 - errorCount: - # 连续错误数阈值 - continuousErrorThreshold: 10 - # 连续错误数统计时间窗口,默认1秒 - metricStatTimeWindow: 1s + - composite outlierDetection: # 是否开启探测 enable: true @@ -138,25 +120,7 @@ consumer: successCountAfterHalfOpen: 2 # 熔断插件配置 chain: - # 按照错误率进行熔断 - - errorRate - # 按照连续请求错误进行熔断 - - errorCount - plugin: - errorRate: - # 只有请求数达到某个阈值才执行熔断计算,默认10 - requestVolumeThreshold: 10 - # 触发熔断的错误率阈值,默认0.5 - errorRateThreshold: 0.5 - # 错误率统计时间窗口,默认1分钟 - metricStatTimeWindow: 5s - # 统计窗口细分的桶数量,默认12 - metricNumBuckets: 12 - errorCount: - # 连续错误数阈值 - continuousErrorThreshold: 10 - # 连续错误数统计时间窗口,默认1秒 - metricStatTimeWindow: 1s + - composite serviceRouter: percentOfMinInstances: 0.3 # 服务路由链 diff --git a/test/testdata/sr_nearby.yaml b/test/testdata/sr_nearby.yaml index 68631158..fb96eabe 100644 --- a/test/testdata/sr_nearby.yaml +++ b/test/testdata/sr_nearby.yaml @@ -48,25 +48,7 @@ consumer: successCountAfterHalfOpen: 2 # 熔断插件配置 chain: - # 按照错误率进行熔断 - - errorRate - # 按照连续请求错误进行熔断 - - errorCount - plugin: - errorRate: - # 只有请求数达到某个阈值才执行熔断计算,默认10 - requestVolumeThreshold: 12 - # 触发熔断的错误率阈值,默认0.5 - errorRateThreshold: 0.5 - # 错误率统计时间窗口,默认1分钟 - metricStatTimeWindow: 5s - # 统计窗口细分的桶数量,默认12 - metricNumBuckets: 12 - errorCount: - # 连续错误数阈值 - continuousErrorThreshold: 10 - # 连续错误数统计时间窗口,默认1秒 - metricStatTimeWindow: 1s + - composite loadbalancer: # 负载均衡类型 type: weightedRandom diff --git a/test/testdata/sr_rule.yaml b/test/testdata/sr_rule.yaml index 95e1d575..32407ec2 100644 --- a/test/testdata/sr_rule.yaml +++ b/test/testdata/sr_rule.yaml @@ -56,25 +56,7 @@ consumer: successCountAfterHalfOpen: 2 # 熔断插件配置 chain: - # 按照错误率进行熔断 - - errorRate - # 按照连续请求错误进行熔断 - - errorCount - plugin: - errorRate: - # 只有请求数达到某个阈值才执行熔断计算,默认10 - requestVolumeThreshold: 10 - # 触发熔断的错误率阈值,默认0.5 - errorRateThreshold: 0.5 - # 错误率统计时间窗口,默认1分钟 - metricStatTimeWindow: 5s - # 统计窗口细分的桶数量,默认12 - metricNumBuckets: 12 - errorCount: - # 连续错误数阈值 - continuousErrorThreshold: 10 - # 连续错误数统计时间窗口,默认1秒 - metricStatTimeWindow: 1s + - composite outlierDetection: # 是否开启探测 enable: true diff --git a/test/testdata/sr_rule_variable.yaml b/test/testdata/sr_rule_variable.yaml index 3b5b665a..2e833199 100644 --- a/test/testdata/sr_rule_variable.yaml +++ b/test/testdata/sr_rule_variable.yaml @@ -57,25 +57,7 @@ consumer: successCountAfterHalfOpen: 2 # 熔断插件配置 chain: - # 按照错误率进行熔断 - - errorRate - # 按照连续请求错误进行熔断 - - errorCount - plugin: - errorRate: - # 只有请求数达到某个阈值才执行熔断计算,默认10 - requestVolumeThreshold: 10 - # 触发熔断的错误率阈值,默认0.5 - errorRateThreshold: 0.5 - # 错误率统计时间窗口,默认1分钟 - metricStatTimeWindow: 5s - # 统计窗口细分的桶数量,默认12 - metricNumBuckets: 12 - errorCount: - # 连续错误数阈值 - continuousErrorThreshold: 10 - # 连续错误数统计时间窗口,默认1秒 - metricStatTimeWindow: 1s + - composite outlierDetection: # 是否开启探测 enable: true diff --git a/test/testdata/sr_setdivision.yaml b/test/testdata/sr_setdivision.yaml index 55d17162..f865cab5 100644 --- a/test/testdata/sr_setdivision.yaml +++ b/test/testdata/sr_setdivision.yaml @@ -54,25 +54,7 @@ consumer: successCountAfterHalfOpen: 2 # 熔断插件配置 chain: - # 按照错误率进行熔断 - - errorRate - # 按照连续请求错误进行熔断 - - errorCount - plugin: - errorRate: - # 只有请求数达到某个阈值才执行熔断计算,默认10 - requestVolumeThreshold: 10 - # 触发熔断的错误率阈值,默认0.5 - errorRateThreshold: 0.5 - # 错误率统计时间窗口,默认1分钟 - metricStatTimeWindow: 5s - # 统计窗口细分的桶数量,默认12 - metricNumBuckets: 12 - errorCount: - # 连续错误数阈值 - continuousErrorThreshold: 10 - # 连续错误数统计时间窗口,默认1秒 - metricStatTimeWindow: 1s + - composite outlierDetection: # 是否开启探测 enable: true diff --git a/test/testdata/subscribe.yaml b/test/testdata/subscribe.yaml index bec0eb46..12e0cbc8 100644 --- a/test/testdata/subscribe.yaml +++ b/test/testdata/subscribe.yaml @@ -75,25 +75,7 @@ consumer: successCountAfterHalfOpen: 2 # 熔断插件配置 chain: - # 按照错误率进行熔断 - - errorRate - # 按照连续请求错误进行熔断 - - errorCount - plugin: - errorRate: - # 只有请求数达到某个阈值才执行熔断计算,默认10 - requestVolumeThreshold: 10 - # 触发熔断的错误率阈值,默认0.5 - errorRateThreshold: 0.5 - # 错误率统计时间窗口,默认1分钟 - metricStatTimeWindow: 5s - # 统计窗口细分的桶数量,默认12 - metricNumBuckets: 12 - errorCount: - # 连续错误数阈值 - continuousErrorThreshold: 10 - # 连续错误数统计时间窗口,默认1秒 - metricStatTimeWindow: 1s + - composite outlierDetection: # 是否开启探测 enable: true