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  }