dubbo.apache.org/dubbo-go/v3@v3.1.1/filter/polaris/limit/limiter.go (about) 1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package limit 19 20 import ( 21 "errors" 22 "fmt" 23 "time" 24 ) 25 26 import ( 27 "github.com/dubbogo/gost/log/logger" 28 29 "github.com/polarismesh/polaris-go" 30 "github.com/polarismesh/polaris-go/pkg/flow/data" 31 "github.com/polarismesh/polaris-go/pkg/model" 32 v1 "github.com/polarismesh/polaris-go/pkg/model/pb/v1" 33 ) 34 35 import ( 36 "dubbo.apache.org/dubbo-go/v3/common" 37 "dubbo.apache.org/dubbo-go/v3/common/constant" 38 "dubbo.apache.org/dubbo-go/v3/config" 39 "dubbo.apache.org/dubbo-go/v3/protocol" 40 remotingpolaris "dubbo.apache.org/dubbo-go/v3/remoting/polaris" 41 "dubbo.apache.org/dubbo-go/v3/remoting/polaris/parser" 42 ) 43 44 type polarisTpsLimiter struct { 45 limitAPI polaris.LimitAPI 46 } 47 48 func (pl *polarisTpsLimiter) IsAllowable(url *common.URL, invocation protocol.Invocation) bool { 49 if err := remotingpolaris.Check(); errors.Is(err, remotingpolaris.ErrorNoOpenPolarisAbility) { 50 logger.Debug("[TpsLimiter][Polaris] not open polaris ratelimit ability") 51 return true 52 } 53 54 var err error 55 56 pl.limitAPI, err = remotingpolaris.GetLimiterAPI() 57 if err != nil { 58 logger.Error("[TpsLimiter][Polaris] create polaris LimitAPI fail : %+v", err) 59 return true 60 } 61 62 req := pl.buildQuotaRequest(url, invocation) 63 if req == nil { 64 return true 65 } 66 logger.Debugf("[TpsLimiter][Polaris] quota req : %+v", req) 67 68 resp, err := pl.limitAPI.GetQuota(req) 69 if err != nil { 70 logger.Error("[TpsLimiter][Polaris] ns:%s svc:%s get quota fail : %+v", remotingpolaris.GetNamespace(), url.Service(), err) 71 return true 72 } 73 74 return resp.Get().Code == model.QuotaResultOk 75 } 76 77 func (pl *polarisTpsLimiter) buildQuotaRequest(url *common.URL, invoaction protocol.Invocation) polaris.QuotaRequest { 78 ns := remotingpolaris.GetNamespace() 79 applicationMode := false 80 for _, item := range config.GetRootConfig().Registries { 81 if item.Protocol == constant.PolarisKey { 82 applicationMode = item.RegistryType == constant.ServiceKey 83 } 84 } 85 86 svc := url.Interface() 87 method := invoaction.MethodName() 88 if applicationMode { 89 svc = config.GetApplicationConfig().Name 90 method = url.Interface() + "/" + invoaction.MethodName() 91 } 92 93 req := polaris.NewQuotaRequest() 94 req.SetNamespace(ns) 95 req.SetService(svc) 96 req.SetMethod(method) 97 98 matchs, ok := pl.buildArguments(req.(*model.QuotaRequestImpl)) 99 if !ok { 100 return nil 101 } 102 103 attachement := invoaction.Attachments() 104 arguments := invoaction.Arguments() 105 106 for i := range matchs { 107 item := matchs[i] 108 switch item.GetType() { 109 case v1.MatchArgument_HEADER: 110 if val, ok := attachement[item.GetKey()]; ok { 111 req.AddArgument(model.BuildHeaderArgument(item.GetKey(), fmt.Sprintf("%+v", val))) 112 } 113 case v1.MatchArgument_QUERY: 114 if val := parser.ParseArgumentsByExpression(item.GetKey(), arguments); val != nil { 115 req.AddArgument(model.BuildQueryArgument(item.GetKey(), fmt.Sprintf("%+v", val))) 116 } 117 case v1.MatchArgument_CALLER_IP: 118 callerIp := url.GetParam(constant.RemoteAddr, "") 119 if len(callerIp) != 0 { 120 req.AddArgument(model.BuildCallerIPArgument(callerIp)) 121 } 122 case model.ArgumentTypeCallerService: 123 } 124 } 125 126 return req 127 } 128 129 func (pl *polarisTpsLimiter) buildArguments(req *model.QuotaRequestImpl) ([]*v1.MatchArgument, bool) { 130 engine := pl.limitAPI.SDKContext().GetEngine() 131 132 getRuleReq := &data.CommonRateLimitRequest{ 133 DstService: model.ServiceKey{ 134 Namespace: req.GetNamespace(), 135 Service: req.GetService(), 136 }, 137 Trigger: model.NotifyTrigger{ 138 EnableDstRateLimit: true, 139 }, 140 ControlParam: model.ControlParam{ 141 Timeout: time.Millisecond * 500, 142 }, 143 } 144 145 if err := engine.SyncGetResources(getRuleReq); err != nil { 146 logger.Error("[TpsLimiter][Polaris] ns:%s svc:%s get RateLimit Rule fail : %+v", req.GetNamespace(), req.GetService(), err) 147 return nil, false 148 } 149 150 svcRule := getRuleReq.RateLimitRule 151 if svcRule == nil || svcRule.GetValue() == nil { 152 logger.Warnf("[TpsLimiter][Polaris] ns:%s svc:%s get RateLimit Rule is nil", req.GetNamespace(), req.GetService()) 153 return nil, false 154 } 155 156 rules, ok := svcRule.GetValue().(*v1.RateLimit) 157 if !ok { 158 logger.Error("[TpsLimiter][Polaris] ns:%s svc:%s get RateLimit Rule invalid", req.GetNamespace(), req.GetService()) 159 return nil, false 160 } 161 162 ret := make([]*v1.MatchArgument, 0, 4) 163 for i := range rules.GetRules() { 164 rule := rules.GetRules()[i] 165 if len(rule.GetArguments()) == 0 { 166 continue 167 } 168 169 ret = append(ret, rule.Arguments...) 170 } 171 172 return ret, true 173 }