frp/pkg/util/metric/date_counter.go
2026-05-12 11:09:19 +08:00

138 lines
3.2 KiB
Go

// Copyright 2017 fatedier, fatedier@gmail.com
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package metric
import (
"sync"
"time"
"k8s.io/utils/clock"
)
type DateCounter interface {
TodayCount() int64
GetLastDaysCount(lastdays int64) []int64
Inc(int64)
Dec(int64)
Snapshot() DateCounter
Clear()
}
func NewDateCounter(reserveDays int64) DateCounter {
if reserveDays <= 0 {
reserveDays = 1
}
return newStandardDateCounter(reserveDays)
}
type StandardDateCounter struct {
reserveDays int64
counts []int64
clock clock.PassiveClock
lastUpdateDate time.Time
mu sync.Mutex
}
func newStandardDateCounter(reserveDays int64) *StandardDateCounter {
return newStandardDateCounterWithClock(reserveDays, clock.RealClock{})
}
func newStandardDateCounterWithClock(reserveDays int64, clk clock.PassiveClock) *StandardDateCounter {
if clk == nil {
clk = clock.RealClock{}
}
return &StandardDateCounter{
reserveDays: reserveDays,
counts: make([]int64, reserveDays),
clock: clk,
lastUpdateDate: startOfDay(clk.Now()),
}
}
func (c *StandardDateCounter) TodayCount() int64 {
c.mu.Lock()
defer c.mu.Unlock()
c.rotate(c.clock.Now())
return c.counts[0]
}
func (c *StandardDateCounter) GetLastDaysCount(lastdays int64) []int64 {
if lastdays > c.reserveDays {
lastdays = c.reserveDays
}
counts := make([]int64, lastdays)
c.mu.Lock()
defer c.mu.Unlock()
c.rotate(c.clock.Now())
copy(counts, c.counts)
return counts
}
func (c *StandardDateCounter) Inc(count int64) {
c.mu.Lock()
defer c.mu.Unlock()
c.rotate(c.clock.Now())
c.counts[0] += count
}
func (c *StandardDateCounter) Dec(count int64) {
c.mu.Lock()
defer c.mu.Unlock()
c.rotate(c.clock.Now())
c.counts[0] -= count
}
func (c *StandardDateCounter) Snapshot() DateCounter {
c.mu.Lock()
defer c.mu.Unlock()
tmp := newStandardDateCounterWithClock(c.reserveDays, c.clock)
copy(tmp.counts, c.counts)
return tmp
}
func (c *StandardDateCounter) Clear() {
c.mu.Lock()
defer c.mu.Unlock()
clear(c.counts)
}
// rotate
// Must hold the lock before calling this function.
func (c *StandardDateCounter) rotate(now time.Time) {
now = startOfDay(now)
days := int(now.Sub(c.lastUpdateDate).Hours() / 24)
reserveDays := int(c.reserveDays)
if days <= 0 {
return
} else if days >= reserveDays {
c.counts = make([]int64, c.reserveDays)
c.lastUpdateDate = now
return
}
newCounts := make([]int64, c.reserveDays)
copy(newCounts[days:], c.counts[:reserveDays-days])
c.counts = newCounts
c.lastUpdateDate = now
}
// startOfDay returns midnight in t's location.
func startOfDay(t time.Time) time.Time {
return time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location())
}