dubbo.apache.org/dubbo-go/v3@v3.1.1/protocol/rpc_status.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 protocol
    19  
    20  import (
    21  	"sync"
    22  	"sync/atomic"
    23  	"time"
    24  )
    25  
    26  import (
    27  	"github.com/dubbogo/gost/log/logger"
    28  
    29  	uberAtomic "go.uber.org/atomic"
    30  )
    31  
    32  import (
    33  	"dubbo.apache.org/dubbo-go/v3/common"
    34  	"dubbo.apache.org/dubbo-go/v3/common/constant"
    35  )
    36  
    37  var (
    38  	methodStatistics    sync.Map        // url -> { methodName : RPCStatus}
    39  	serviceStatistic    sync.Map        // url -> RPCStatus
    40  	invokerBlackList    sync.Map        // store unhealthy url blackList
    41  	blackListCacheDirty uberAtomic.Bool // store if the cache in chain is not refreshed by blacklist
    42  	blackListRefreshing int32           // store if the refresing method is processing
    43  )
    44  
    45  func init() {
    46  	blackListCacheDirty.Store(false)
    47  }
    48  
    49  // RPCStatus is URL statistics.
    50  type RPCStatus struct {
    51  	active                        int32
    52  	failed                        int32
    53  	total                         int32
    54  	totalElapsed                  int64
    55  	failedElapsed                 int64
    56  	maxElapsed                    int64
    57  	failedMaxElapsed              int64
    58  	succeededMaxElapsed           int64
    59  	successiveRequestFailureCount int32
    60  	lastRequestFailedTimestamp    int64
    61  }
    62  
    63  // GetActive gets active.
    64  func (rpc *RPCStatus) GetActive() int32 {
    65  	return atomic.LoadInt32(&rpc.active)
    66  }
    67  
    68  // GetFailed gets failed.
    69  func (rpc *RPCStatus) GetFailed() int32 {
    70  	return atomic.LoadInt32(&rpc.failed)
    71  }
    72  
    73  // GetTotal gets total.
    74  func (rpc *RPCStatus) GetTotal() int32 {
    75  	return atomic.LoadInt32(&rpc.total)
    76  }
    77  
    78  // GetTotalElapsed gets total elapsed.
    79  func (rpc *RPCStatus) GetTotalElapsed() int64 {
    80  	return atomic.LoadInt64(&rpc.totalElapsed)
    81  }
    82  
    83  // GetFailedElapsed gets failed elapsed.
    84  func (rpc *RPCStatus) GetFailedElapsed() int64 {
    85  	return atomic.LoadInt64(&rpc.failedElapsed)
    86  }
    87  
    88  // GetMaxElapsed gets max elapsed.
    89  func (rpc *RPCStatus) GetMaxElapsed() int64 {
    90  	return atomic.LoadInt64(&rpc.maxElapsed)
    91  }
    92  
    93  // GetFailedMaxElapsed gets failed max elapsed.
    94  func (rpc *RPCStatus) GetFailedMaxElapsed() int64 {
    95  	return atomic.LoadInt64(&rpc.failedMaxElapsed)
    96  }
    97  
    98  // GetSucceededMaxElapsed gets succeeded max elapsed.
    99  func (rpc *RPCStatus) GetSucceededMaxElapsed() int64 {
   100  	return atomic.LoadInt64(&rpc.succeededMaxElapsed)
   101  }
   102  
   103  // GetLastRequestFailedTimestamp gets last request failed timestamp.
   104  func (rpc *RPCStatus) GetLastRequestFailedTimestamp() int64 {
   105  	return atomic.LoadInt64(&rpc.lastRequestFailedTimestamp)
   106  }
   107  
   108  // GetSuccessiveRequestFailureCount gets successive request failure count.
   109  func (rpc *RPCStatus) GetSuccessiveRequestFailureCount() int32 {
   110  	return atomic.LoadInt32(&rpc.successiveRequestFailureCount)
   111  }
   112  
   113  // GetURLStatus get URL RPC status.
   114  func GetURLStatus(url *common.URL) *RPCStatus {
   115  	rpcStatus, found := serviceStatistic.Load(url.Key())
   116  	if !found {
   117  		rpcStatus, _ = serviceStatistic.LoadOrStore(url.Key(), &RPCStatus{})
   118  	}
   119  	return rpcStatus.(*RPCStatus)
   120  }
   121  
   122  // GetMethodStatus get method RPC status.
   123  func GetMethodStatus(url *common.URL, methodName string) *RPCStatus {
   124  	identifier := url.Key()
   125  	methodMap, found := methodStatistics.Load(identifier)
   126  	if !found {
   127  		methodMap, _ = methodStatistics.LoadOrStore(identifier, &sync.Map{})
   128  	}
   129  
   130  	methodActive := methodMap.(*sync.Map)
   131  	rpcStatus, found := methodActive.Load(methodName)
   132  	if !found {
   133  		rpcStatus, _ = methodActive.LoadOrStore(methodName, &RPCStatus{})
   134  	}
   135  
   136  	status := rpcStatus.(*RPCStatus)
   137  	return status
   138  }
   139  
   140  // BeginCount gets begin count.
   141  func BeginCount(url *common.URL, methodName string) {
   142  	beginCount0(GetURLStatus(url))
   143  	beginCount0(GetMethodStatus(url, methodName))
   144  }
   145  
   146  // EndCount gets end count.
   147  func EndCount(url *common.URL, methodName string, elapsed int64, succeeded bool) {
   148  	endCount0(GetURLStatus(url), elapsed, succeeded)
   149  	endCount0(GetMethodStatus(url, methodName), elapsed, succeeded)
   150  }
   151  
   152  // private methods
   153  func beginCount0(rpcStatus *RPCStatus) {
   154  	atomic.AddInt32(&rpcStatus.active, 1)
   155  }
   156  
   157  func endCount0(rpcStatus *RPCStatus, elapsed int64, succeeded bool) {
   158  	atomic.AddInt32(&rpcStatus.active, -1)
   159  	atomic.AddInt32(&rpcStatus.total, 1)
   160  	atomic.AddInt64(&rpcStatus.totalElapsed, elapsed)
   161  
   162  	if rpcStatus.maxElapsed < elapsed {
   163  		atomic.StoreInt64(&rpcStatus.maxElapsed, elapsed)
   164  	}
   165  	if succeeded {
   166  		if rpcStatus.succeededMaxElapsed < elapsed {
   167  			atomic.StoreInt64(&rpcStatus.succeededMaxElapsed, elapsed)
   168  		}
   169  		atomic.StoreInt32(&rpcStatus.successiveRequestFailureCount, 0)
   170  	} else {
   171  		atomic.StoreInt64(&rpcStatus.lastRequestFailedTimestamp, CurrentTimeMillis())
   172  		atomic.AddInt32(&rpcStatus.successiveRequestFailureCount, 1)
   173  		atomic.AddInt32(&rpcStatus.failed, 1)
   174  		atomic.AddInt64(&rpcStatus.failedElapsed, elapsed)
   175  		if rpcStatus.failedMaxElapsed < elapsed {
   176  			atomic.StoreInt64(&rpcStatus.failedMaxElapsed, elapsed)
   177  		}
   178  	}
   179  }
   180  
   181  // CurrentTimeMillis get current timestamp
   182  func CurrentTimeMillis() int64 {
   183  	return time.Now().UnixNano() / int64(time.Millisecond)
   184  }
   185  
   186  // CleanAllStatus is used to clean all status
   187  func CleanAllStatus() {
   188  	delete1 := func(key, _ interface{}) bool {
   189  		methodStatistics.Delete(key)
   190  		return true
   191  	}
   192  	methodStatistics.Range(delete1)
   193  	delete2 := func(key, _ interface{}) bool {
   194  		serviceStatistic.Delete(key)
   195  		return true
   196  	}
   197  	serviceStatistic.Range(delete2)
   198  	delete3 := func(key, _ interface{}) bool {
   199  		invokerBlackList.Delete(key)
   200  		return true
   201  	}
   202  	invokerBlackList.Range(delete3)
   203  }
   204  
   205  // GetInvokerHealthyStatus get invoker's conn healthy status
   206  func GetInvokerHealthyStatus(invoker Invoker) bool {
   207  	_, found := invokerBlackList.Load(invoker.GetURL().Key())
   208  	return !found
   209  }
   210  
   211  // SetInvokerUnhealthyStatus add target invoker to black list
   212  func SetInvokerUnhealthyStatus(invoker Invoker) {
   213  	invokerBlackList.Store(invoker.GetURL().Key(), invoker)
   214  	logger.Info("Add invoker ip = ", invoker.GetURL().Location, " to black list")
   215  	blackListCacheDirty.Store(true)
   216  }
   217  
   218  // RemoveInvokerUnhealthyStatus remove unhealthy status of target invoker from blacklist
   219  func RemoveInvokerUnhealthyStatus(invoker Invoker) {
   220  	invokerBlackList.Delete(invoker.GetURL().Key())
   221  	logger.Info("Remove invoker ip = ", invoker.GetURL().Location, " from black list")
   222  	blackListCacheDirty.Store(true)
   223  }
   224  
   225  // GetBlackListInvokers get at most size of blockSize invokers from black list
   226  func GetBlackListInvokers(blockSize int) []Invoker {
   227  	resultIvks := make([]Invoker, 0, 16)
   228  	invokerBlackList.Range(func(k, v interface{}) bool {
   229  		resultIvks = append(resultIvks, v.(Invoker))
   230  		return true
   231  	})
   232  	if blockSize > len(resultIvks) {
   233  		return resultIvks
   234  	}
   235  	return resultIvks[:blockSize]
   236  }
   237  
   238  // RemoveUrlKeyUnhealthyStatus called when event of provider unregister, delete from black list
   239  func RemoveUrlKeyUnhealthyStatus(key string) {
   240  	invokerBlackList.Delete(key)
   241  	logger.Info("Remove invoker key = ", key, " from black list")
   242  	blackListCacheDirty.Store(true)
   243  }
   244  
   245  func GetAndRefreshState() bool {
   246  	state := blackListCacheDirty.Load()
   247  	blackListCacheDirty.Store(false)
   248  	return state
   249  }
   250  
   251  // TryRefreshBlackList start 3 gr to check at most block=16 invokers in black list
   252  // if target invoker is available, then remove it from black list
   253  func TryRefreshBlackList() {
   254  	if atomic.CompareAndSwapInt32(&blackListRefreshing, 0, 1) {
   255  		wg := sync.WaitGroup{}
   256  		defer func() {
   257  			atomic.CompareAndSwapInt32(&blackListRefreshing, 1, 0)
   258  		}()
   259  
   260  		ivks := GetBlackListInvokers(constant.DefaultBlackListRecoverBlock)
   261  		logger.Debug("blackList len = ", len(ivks))
   262  
   263  		for i := 0; i < 3; i++ {
   264  			wg.Add(1)
   265  			go func(ivks []Invoker, i int) {
   266  				defer wg.Done()
   267  				for j := range ivks {
   268  					if j%3-i == 0 && ivks[j].(Invoker).IsAvailable() {
   269  						RemoveInvokerUnhealthyStatus(ivks[i])
   270  					}
   271  				}
   272  			}(ivks, i)
   273  		}
   274  		wg.Wait()
   275  	}
   276  }