dubbo.apache.org/dubbo-go/v3@v3.1.1/filter/sentinel/filter.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 sentinel provides a filter when using sentinel. 19 // Integrate Sentinel Go MUST HAVE: 20 // 1. Must initialize Sentinel Go run environment, refer to https://github.com/alibaba/sentinel-golang/blob/master/api/init.go 21 // 2. Register rules for resources user want to guard 22 package sentinel 23 24 import ( 25 "context" 26 "fmt" 27 "strings" 28 "sync" 29 ) 30 31 import ( 32 sentinel "github.com/alibaba/sentinel-golang/api" 33 "github.com/alibaba/sentinel-golang/core/base" 34 "github.com/alibaba/sentinel-golang/logging" 35 36 "github.com/dubbogo/gost/log/logger" 37 ) 38 39 import ( 40 "dubbo.apache.org/dubbo-go/v3/common" 41 "dubbo.apache.org/dubbo-go/v3/common/constant" 42 "dubbo.apache.org/dubbo-go/v3/common/extension" 43 "dubbo.apache.org/dubbo-go/v3/filter" 44 "dubbo.apache.org/dubbo-go/v3/protocol" 45 ) 46 47 func init() { 48 extension.SetFilter(constant.SentinelConsumerFilterKey, newSentinelConsumerFilter) 49 extension.SetFilter(constant.SentinelProviderFilterKey, newSentinelProviderFilter) 50 if err := logging.ResetGlobalLogger(DubboLoggerWrapper{Logger: logger.GetLogger()}); err != nil { 51 logger.Errorf("[Sentinel Filter] fail to ingest dubbo logger into sentinel") 52 } 53 } 54 55 type DubboLoggerWrapper struct { 56 logger.Logger 57 } 58 59 func (d DubboLoggerWrapper) Debug(msg string, keysAndValues ...interface{}) { 60 d.Logger.Debug(logging.AssembleMsg(logging.GlobalCallerDepth, "DEBUG", msg, nil, keysAndValues...)) 61 } 62 63 func (d DubboLoggerWrapper) DebugEnabled() bool { 64 return true 65 } 66 67 func (d DubboLoggerWrapper) Info(msg string, keysAndValues ...interface{}) { 68 d.Logger.Info(logging.AssembleMsg(logging.GlobalCallerDepth, "INFO", msg, nil, keysAndValues...)) 69 } 70 71 func (d DubboLoggerWrapper) InfoEnabled() bool { 72 return true 73 } 74 75 func (d DubboLoggerWrapper) Warn(msg string, keysAndValues ...interface{}) { 76 d.Logger.Warn(logging.AssembleMsg(logging.GlobalCallerDepth, "WARN", msg, nil, keysAndValues...)) 77 } 78 79 func (d DubboLoggerWrapper) WarnEnabled() bool { 80 return true 81 } 82 83 func (d DubboLoggerWrapper) Error(err error, msg string, keysAndValues ...interface{}) { 84 d.Logger.Warn(logging.AssembleMsg(logging.GlobalCallerDepth, "ERROR", msg, err, keysAndValues...)) 85 } 86 87 func (d DubboLoggerWrapper) ErrorEnabled() bool { 88 return true 89 } 90 91 func sentinelExit(ctx context.Context, result protocol.Result) { 92 if methodEntry := ctx.Value(MethodEntryKey); methodEntry != nil { 93 e := methodEntry.(*base.SentinelEntry) 94 sentinel.TraceError(e, result.Error()) 95 e.Exit() 96 } 97 if interfaceEntry := ctx.Value(InterfaceEntryKey); interfaceEntry != nil { 98 e := interfaceEntry.(*base.SentinelEntry) 99 sentinel.TraceError(e, result.Error()) 100 e.Exit() 101 } 102 } 103 104 var ( 105 providerOnce sync.Once 106 sentinelProvider *sentinelProviderFilter 107 ) 108 109 type sentinelProviderFilter struct{} 110 111 func newSentinelProviderFilter() filter.Filter { 112 if sentinelProvider == nil { 113 providerOnce.Do(func() { 114 sentinelProvider = &sentinelProviderFilter{} 115 }) 116 } 117 return sentinelProvider 118 } 119 120 func (d *sentinelProviderFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { 121 interfaceResourceName, methodResourceName := getResourceName(invoker, invocation, getProviderPrefix()) 122 123 var ( 124 interfaceEntry *base.SentinelEntry 125 methodEntry *base.SentinelEntry 126 b *base.BlockError 127 ) 128 interfaceEntry, b = sentinel.Entry(interfaceResourceName, sentinel.WithResourceType(base.ResTypeRPC), sentinel.WithTrafficType(base.Inbound)) 129 if b != nil { 130 // interface blocked 131 return sentinelDubboProviderFallback(ctx, invoker, invocation, b) 132 } 133 ctx = context.WithValue(ctx, InterfaceEntryKey, interfaceEntry) 134 135 methodEntry, b = sentinel.Entry(methodResourceName, 136 sentinel.WithResourceType(base.ResTypeRPC), 137 sentinel.WithTrafficType(base.Inbound), 138 sentinel.WithArgs(invocation.Arguments()...)) 139 if b != nil { 140 // method blocked 141 return sentinelDubboProviderFallback(ctx, invoker, invocation, b) 142 } 143 ctx = context.WithValue(ctx, MethodEntryKey, methodEntry) 144 return invoker.Invoke(ctx, invocation) 145 } 146 147 func (d *sentinelProviderFilter) OnResponse(ctx context.Context, result protocol.Result, _ protocol.Invoker, _ protocol.Invocation) protocol.Result { 148 sentinelExit(ctx, result) 149 return result 150 } 151 152 var ( 153 consumerOnce sync.Once 154 sentinelConsumer *sentinelConsumerFilter 155 ) 156 157 type sentinelConsumerFilter struct{} 158 159 func newSentinelConsumerFilter() filter.Filter { 160 if sentinelConsumer == nil { 161 consumerOnce.Do(func() { 162 sentinelConsumer = &sentinelConsumerFilter{} 163 }) 164 } 165 return sentinelConsumer 166 } 167 168 func (d *sentinelConsumerFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { 169 interfaceResourceName, methodResourceName := getResourceName(invoker, invocation, getConsumerPrefix()) 170 var ( 171 interfaceEntry *base.SentinelEntry 172 methodEntry *base.SentinelEntry 173 b *base.BlockError 174 ) 175 176 interfaceEntry, b = sentinel.Entry(interfaceResourceName, sentinel.WithResourceType(base.ResTypeRPC), sentinel.WithTrafficType(base.Outbound)) 177 if b != nil { 178 // interface blocked 179 return sentinelDubboConsumerFallback(ctx, invoker, invocation, b) 180 } 181 ctx = context.WithValue(ctx, InterfaceEntryKey, interfaceEntry) 182 183 methodEntry, b = sentinel.Entry(methodResourceName, sentinel.WithResourceType(base.ResTypeRPC), 184 sentinel.WithTrafficType(base.Outbound), sentinel.WithArgs(invocation.Arguments()...)) 185 if b != nil { 186 // method blocked 187 return sentinelDubboConsumerFallback(ctx, invoker, invocation, b) 188 } 189 ctx = context.WithValue(ctx, MethodEntryKey, methodEntry) 190 191 return invoker.Invoke(ctx, invocation) 192 } 193 194 func (d *sentinelConsumerFilter) OnResponse(ctx context.Context, result protocol.Result, _ protocol.Invoker, _ protocol.Invocation) protocol.Result { 195 sentinelExit(ctx, result) 196 return result 197 } 198 199 var ( 200 sentinelDubboConsumerFallback = getDefaultDubboFallback() 201 sentinelDubboProviderFallback = getDefaultDubboFallback() 202 ) 203 204 type DubboFallback func(context.Context, protocol.Invoker, protocol.Invocation, *base.BlockError) protocol.Result 205 206 func SetDubboConsumerFallback(f DubboFallback) { 207 sentinelDubboConsumerFallback = f 208 } 209 210 func SetDubboProviderFallback(f DubboFallback) { 211 sentinelDubboProviderFallback = f 212 } 213 214 func getDefaultDubboFallback() DubboFallback { 215 return func(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation, blockError *base.BlockError) protocol.Result { 216 return &protocol.RPCResult{Err: blockError} 217 } 218 } 219 220 const ( 221 DefaultProviderPrefix = "dubbo:provider:" 222 DefaultConsumerPrefix = "dubbo:consumer:" 223 224 MethodEntryKey = constant.DubboCtxKey("$$sentinelMethodEntry") 225 InterfaceEntryKey = constant.DubboCtxKey("$$sentinelInterfaceEntry") 226 ) 227 228 func getResourceName(invoker protocol.Invoker, invocation protocol.Invocation, prefix string) (interfaceResourceName, methodResourceName string) { 229 var sb strings.Builder 230 231 sb.WriteString(prefix) 232 if getInterfaceGroupAndVersionEnabled() { 233 interfaceResourceName = getColonSeparatedKey(invoker.GetURL()) 234 } else { 235 interfaceResourceName = invoker.GetURL().Service() 236 } 237 sb.WriteString(interfaceResourceName) 238 sb.WriteString(":") 239 sb.WriteString(invocation.MethodName()) 240 sb.WriteString("(") 241 isFirst := true 242 for _, v := range invocation.ParameterTypes() { 243 if !isFirst { 244 sb.WriteString(",") 245 } 246 sb.WriteString(v.Name()) 247 isFirst = false 248 } 249 sb.WriteString(")") 250 methodResourceName = sb.String() 251 return 252 } 253 254 func getConsumerPrefix() string { 255 return DefaultConsumerPrefix 256 } 257 258 func getProviderPrefix() string { 259 return DefaultProviderPrefix 260 } 261 262 func getInterfaceGroupAndVersionEnabled() bool { 263 return true 264 } 265 266 func getColonSeparatedKey(url *common.URL) string { 267 return fmt.Sprintf("%s:%s:%s", 268 url.Service(), 269 url.GetParam(constant.GroupKey, ""), 270 url.GetParam(constant.VersionKey, "")) 271 }