dubbo.apache.org/dubbo-go/v3@v3.1.1/xds/client/requests_counter.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 /* 19 * 20 * Copyright 2020 gRPC authors. 21 * 22 */ 23 24 package client 25 26 import ( 27 "fmt" 28 "sync" 29 "sync/atomic" 30 ) 31 32 type clusterNameAndServiceName struct { 33 clusterName, edsServcieName string 34 } 35 36 type clusterRequestsCounter struct { 37 mu sync.Mutex 38 clusters map[clusterNameAndServiceName]*ClusterRequestsCounter 39 } 40 41 var src = &clusterRequestsCounter{ 42 clusters: make(map[clusterNameAndServiceName]*ClusterRequestsCounter), 43 } 44 45 // ClusterRequestsCounter is used to track the total inflight requests for a 46 // service with the provided name. 47 type ClusterRequestsCounter struct { 48 ClusterName string 49 EDSServiceName string 50 numRequests uint32 51 } 52 53 // GetClusterRequestsCounter returns the ClusterRequestsCounter with the 54 // provided serviceName. If one does not exist, it creates it. 55 func GetClusterRequestsCounter(clusterName, edsServiceName string) *ClusterRequestsCounter { 56 src.mu.Lock() 57 defer src.mu.Unlock() 58 k := clusterNameAndServiceName{ 59 clusterName: clusterName, 60 edsServcieName: edsServiceName, 61 } 62 c, ok := src.clusters[k] 63 if !ok { 64 c = &ClusterRequestsCounter{ClusterName: clusterName} 65 src.clusters[k] = c 66 } 67 return c 68 } 69 70 // StartRequest starts a request for a cluster, incrementing its number of 71 // requests by 1. Returns an error if the max number of requests is exceeded. 72 func (c *ClusterRequestsCounter) StartRequest(max uint32) error { 73 // Note that during race, the limits could be exceeded. This is allowed: 74 // "Since the implementation is eventually consistent, races between threads 75 // may allow limits to be potentially exceeded." 76 // https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/upstream/circuit_breaking#arch-overview-circuit-break. 77 if atomic.LoadUint32(&c.numRequests) >= max { 78 return fmt.Errorf("max requests %v exceeded on service %v", max, c.ClusterName) 79 } 80 atomic.AddUint32(&c.numRequests, 1) 81 return nil 82 } 83 84 // EndRequest ends a request for a service, decrementing its number of requests 85 // by 1. 86 func (c *ClusterRequestsCounter) EndRequest() { 87 atomic.AddUint32(&c.numRequests, ^uint32(0)) 88 } 89 90 // ClearCounterForTesting clears the counter for the service. Should be only 91 // used in tests. 92 func ClearCounterForTesting(clusterName, edsServiceName string) { 93 src.mu.Lock() 94 defer src.mu.Unlock() 95 k := clusterNameAndServiceName{ 96 clusterName: clusterName, 97 edsServcieName: edsServiceName, 98 } 99 c, ok := src.clusters[k] 100 if !ok { 101 return 102 } 103 c.numRequests = 0 104 } 105 106 // ClearAllCountersForTesting clears all the counters. Should be only used in 107 // tests. 108 func ClearAllCountersForTesting() { 109 src.mu.Lock() 110 defer src.mu.Unlock() 111 src.clusters = make(map[clusterNameAndServiceName]*ClusterRequestsCounter) 112 }