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 }