github.com/kisexp/xdchain@v0.0.0-20211206025815-490d6b732aa7/metrics/meter.go (about) 1 package metrics 2 3 import ( 4 "sync" 5 "sync/atomic" 6 "time" 7 ) 8 9 // Meters count events to produce exponentially-weighted moving average rates 10 // at one-, five-, and fifteen-minutes and a mean rate. 11 type Meter interface { 12 Count() int64 13 Mark(int64) 14 Rate1() float64 15 Rate5() float64 16 Rate15() float64 17 RateMean() float64 18 Snapshot() Meter 19 Stop() 20 } 21 22 // GetOrRegisterMeter returns an existing Meter or constructs and registers a 23 // new StandardMeter. 24 // Be sure to unregister the meter from the registry once it is of no use to 25 // allow for garbage collection. 26 func GetOrRegisterMeter(name string, r Registry) Meter { 27 if nil == r { 28 r = DefaultRegistry 29 } 30 return r.GetOrRegister(name, NewMeter).(Meter) 31 } 32 33 // GetOrRegisterMeterForced returns an existing Meter or constructs and registers a 34 // new StandardMeter no matter the global switch is enabled or not. 35 // Be sure to unregister the meter from the registry once it is of no use to 36 // allow for garbage collection. 37 func GetOrRegisterMeterForced(name string, r Registry) Meter { 38 if nil == r { 39 r = DefaultRegistry 40 } 41 return r.GetOrRegister(name, NewMeterForced).(Meter) 42 } 43 44 // NewMeter constructs a new StandardMeter and launches a goroutine. 45 // Be sure to call Stop() once the meter is of no use to allow for garbage collection. 46 func NewMeter() Meter { 47 if !Enabled { 48 return NilMeter{} 49 } 50 m := newStandardMeter() 51 arbiter.Lock() 52 defer arbiter.Unlock() 53 arbiter.meters[m] = struct{}{} 54 if !arbiter.started { 55 arbiter.started = true 56 go arbiter.tick() 57 } 58 return m 59 } 60 61 // NewMeterForced constructs a new StandardMeter and launches a goroutine no matter 62 // the global switch is enabled or not. 63 // Be sure to call Stop() once the meter is of no use to allow for garbage collection. 64 func NewMeterForced() Meter { 65 m := newStandardMeter() 66 arbiter.Lock() 67 defer arbiter.Unlock() 68 arbiter.meters[m] = struct{}{} 69 if !arbiter.started { 70 arbiter.started = true 71 go arbiter.tick() 72 } 73 return m 74 } 75 76 // NewRegisteredMeter constructs and registers a new StandardMeter 77 // and launches a goroutine. 78 // Be sure to unregister the meter from the registry once it is of no use to 79 // allow for garbage collection. 80 func NewRegisteredMeter(name string, r Registry) Meter { 81 c := NewMeter() 82 if nil == r { 83 r = DefaultRegistry 84 } 85 r.Register(name, c) 86 return c 87 } 88 89 // NewRegisteredMeterForced constructs and registers a new StandardMeter 90 // and launches a goroutine no matter the global switch is enabled or not. 91 // Be sure to unregister the meter from the registry once it is of no use to 92 // allow for garbage collection. 93 func NewRegisteredMeterForced(name string, r Registry) Meter { 94 c := NewMeterForced() 95 if nil == r { 96 r = DefaultRegistry 97 } 98 r.Register(name, c) 99 return c 100 } 101 102 // MeterSnapshot is a read-only copy of another Meter. 103 type MeterSnapshot struct { 104 // WARNING: The `temp` field is accessed atomically. 105 // On 32 bit platforms, only 64-bit aligned fields can be atomic. The struct is 106 // guaranteed to be so aligned, so take advantage of that. For more information, 107 // see https://golang.org/pkg/sync/atomic/#pkg-note-BUG. 108 temp int64 109 count int64 110 rate1, rate5, rate15, rateMean float64 111 } 112 113 // Count returns the count of events at the time the snapshot was taken. 114 func (m *MeterSnapshot) Count() int64 { return m.count } 115 116 // Mark panics. 117 func (*MeterSnapshot) Mark(n int64) { 118 panic("Mark called on a MeterSnapshot") 119 } 120 121 // Rate1 returns the one-minute moving average rate of events per second at the 122 // time the snapshot was taken. 123 func (m *MeterSnapshot) Rate1() float64 { return m.rate1 } 124 125 // Rate5 returns the five-minute moving average rate of events per second at 126 // the time the snapshot was taken. 127 func (m *MeterSnapshot) Rate5() float64 { return m.rate5 } 128 129 // Rate15 returns the fifteen-minute moving average rate of events per second 130 // at the time the snapshot was taken. 131 func (m *MeterSnapshot) Rate15() float64 { return m.rate15 } 132 133 // RateMean returns the meter's mean rate of events per second at the time the 134 // snapshot was taken. 135 func (m *MeterSnapshot) RateMean() float64 { return m.rateMean } 136 137 // Snapshot returns the snapshot. 138 func (m *MeterSnapshot) Snapshot() Meter { return m } 139 140 // Stop is a no-op. 141 func (m *MeterSnapshot) Stop() {} 142 143 // NilMeter is a no-op Meter. 144 type NilMeter struct{} 145 146 // Count is a no-op. 147 func (NilMeter) Count() int64 { return 0 } 148 149 // Mark is a no-op. 150 func (NilMeter) Mark(n int64) {} 151 152 // Rate1 is a no-op. 153 func (NilMeter) Rate1() float64 { return 0.0 } 154 155 // Rate5 is a no-op. 156 func (NilMeter) Rate5() float64 { return 0.0 } 157 158 // Rate15 is a no-op. 159 func (NilMeter) Rate15() float64 { return 0.0 } 160 161 // RateMean is a no-op. 162 func (NilMeter) RateMean() float64 { return 0.0 } 163 164 // Snapshot is a no-op. 165 func (NilMeter) Snapshot() Meter { return NilMeter{} } 166 167 // Stop is a no-op. 168 func (NilMeter) Stop() {} 169 170 // StandardMeter is the standard implementation of a Meter. 171 type StandardMeter struct { 172 lock sync.RWMutex 173 snapshot *MeterSnapshot 174 a1, a5, a15 EWMA 175 startTime time.Time 176 stopped uint32 177 } 178 179 func newStandardMeter() *StandardMeter { 180 return &StandardMeter{ 181 snapshot: &MeterSnapshot{}, 182 a1: NewEWMA1(), 183 a5: NewEWMA5(), 184 a15: NewEWMA15(), 185 startTime: time.Now(), 186 } 187 } 188 189 // Stop stops the meter, Mark() will be a no-op if you use it after being stopped. 190 func (m *StandardMeter) Stop() { 191 stopped := atomic.SwapUint32(&m.stopped, 1) 192 if stopped != 1 { 193 arbiter.Lock() 194 delete(arbiter.meters, m) 195 arbiter.Unlock() 196 } 197 } 198 199 // Count returns the number of events recorded. 200 // It updates the meter to be as accurate as possible 201 func (m *StandardMeter) Count() int64 { 202 m.lock.Lock() 203 defer m.lock.Unlock() 204 m.updateMeter() 205 return m.snapshot.count 206 } 207 208 // Mark records the occurrence of n events. 209 func (m *StandardMeter) Mark(n int64) { 210 atomic.AddInt64(&m.snapshot.temp, n) 211 } 212 213 // Rate1 returns the one-minute moving average rate of events per second. 214 func (m *StandardMeter) Rate1() float64 { 215 m.lock.RLock() 216 defer m.lock.RUnlock() 217 return m.snapshot.rate1 218 } 219 220 // Rate5 returns the five-minute moving average rate of events per second. 221 func (m *StandardMeter) Rate5() float64 { 222 m.lock.RLock() 223 defer m.lock.RUnlock() 224 return m.snapshot.rate5 225 } 226 227 // Rate15 returns the fifteen-minute moving average rate of events per second. 228 func (m *StandardMeter) Rate15() float64 { 229 m.lock.RLock() 230 defer m.lock.RUnlock() 231 return m.snapshot.rate15 232 } 233 234 // RateMean returns the meter's mean rate of events per second. 235 func (m *StandardMeter) RateMean() float64 { 236 m.lock.RLock() 237 defer m.lock.RUnlock() 238 return m.snapshot.rateMean 239 } 240 241 // Snapshot returns a read-only copy of the meter. 242 func (m *StandardMeter) Snapshot() Meter { 243 m.lock.RLock() 244 snapshot := *m.snapshot 245 m.lock.RUnlock() 246 return &snapshot 247 } 248 249 func (m *StandardMeter) updateSnapshot() { 250 // should run with write lock held on m.lock 251 snapshot := m.snapshot 252 snapshot.rate1 = m.a1.Rate() 253 snapshot.rate5 = m.a5.Rate() 254 snapshot.rate15 = m.a15.Rate() 255 snapshot.rateMean = float64(snapshot.count) / time.Since(m.startTime).Seconds() 256 } 257 258 func (m *StandardMeter) updateMeter() { 259 // should only run with write lock held on m.lock 260 n := atomic.SwapInt64(&m.snapshot.temp, 0) 261 m.snapshot.count += n 262 m.a1.Update(n) 263 m.a5.Update(n) 264 m.a15.Update(n) 265 } 266 267 func (m *StandardMeter) tick() { 268 m.lock.Lock() 269 defer m.lock.Unlock() 270 m.updateMeter() 271 m.a1.Tick() 272 m.a5.Tick() 273 m.a15.Tick() 274 m.updateSnapshot() 275 } 276 277 // meterArbiter ticks meters every 5s from a single goroutine. 278 // meters are references in a set for future stopping. 279 type meterArbiter struct { 280 sync.RWMutex 281 started bool 282 meters map[*StandardMeter]struct{} 283 ticker *time.Ticker 284 } 285 286 var arbiter = meterArbiter{ticker: time.NewTicker(5 * time.Second), meters: make(map[*StandardMeter]struct{})} 287 288 // Ticks meters on the scheduled interval 289 func (ma *meterArbiter) tick() { 290 for range ma.ticker.C { 291 ma.tickMeters() 292 } 293 } 294 295 func (ma *meterArbiter) tickMeters() { 296 ma.RLock() 297 defer ma.RUnlock() 298 for meter := range ma.meters { 299 meter.tick() 300 } 301 }