All Downloads are FREE. Search and download functionalities are using the official Maven repository.

sviolet.thistle.model.statistic.SlidingWindowCounter Maven / Gradle / Ivy

/*
 * Copyright (C) 2015-2019 S.Violet
 *
 * 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.
 *
 * Project GitHub: https://github.com/shepherdviolet/thistle
 * Email: [email protected]
 */

package sviolet.thistle.model.statistic;

import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 

滑动窗口计数器

* *

计数, 并统计一定时间内的计数值, 数值不精确, 存在一定的近似计算.

* *

例如, 我们要统计最近5分钟内的交易量, 统计精度为10秒, 即samplingDuration=10000, statisticalPeriod=30, * (5分钟 = 10秒 * 30, 实际上程序生成了30个计数器, 每个计数器用来累加10秒钟的计数, 这样就能统计最近十分钟的计数值了). * 在交易发生时, 调用getAndAdd(1)/addAndGet(1)方法计数. 在需要获取计数值时, 调用get(?)获取最近一段时间内的计数值, * getRecently(10000)获取最近10秒钟内的计数值, getRecently(60000)获取最近一分钟内的计数值, getTotally()获取最近五分钟内的计数值.

* *

* 建议:
* 1.statisticalPeriod不要设置的太大, 这个值有多少, 内部就有多少个计数器
* 2.如果只需要统计一种时间尺度, 比如最近10秒, 建议: new SlidingWindowCounter(10000, 4), getRecently(10000)
* 3.如果需要统计多种时间尺度, 比如最近10秒和最近5分钟, 建议: new SlidingWindowCounter(10000, 30), getRecently(10000), getTotally()
*

* * @author S.Violet */ public class SlidingWindowCounter { private final SlidingWindowArray slidingWindowArray; /** * @param samplingDuration 采样时长, ms, 取值范围 >= 10 * @param statisticalPeriod 统计周期, 取值范围 > 0, 统计时长 = 采样时长 * 统计周期 */ public SlidingWindowCounter(int samplingDuration, int statisticalPeriod) { this(samplingDuration, statisticalPeriod, SlidingWindowArray.DEFAULT_TIME_REVERSE_THRESHOLD); } /** * @param samplingDuration 采样时长, ms, 取值范围 >= 10 * @param statisticalPeriod 统计周期, 取值范围 > 0, 统计时长 = 采样时长 * 统计周期 * @param timeReverseThreshold 当时间倒流的情况超过该设定值, 会重置所有统计数据, 默认64, 用于应对服务器时间重设的情况 */ public SlidingWindowCounter(int samplingDuration, int statisticalPeriod, int timeReverseThreshold) { this.slidingWindowArray = new SlidingWindowArray<>(statisticalPeriod, samplingDuration, timeReverseThreshold, new SlidingWindowArray.ElementOperator() { @Override public AtomicInteger reset(AtomicInteger element) { //create if (element == null) { return new AtomicInteger(0); } //reset element.set(0); return element; } }); } /** * 先取值后做加法 * @param delta 增加的数字 * @return 计算前的数值(当前采样时长内的计数值, 不是整个统计周期内的计数值) */ public int getAndAdd(int delta){ return getAndAdd(delta, System.currentTimeMillis()); } /** * 先做加法后取值 * @param delta 增加的数字 * @return 计算后的数值(当前采样时长内的计数值, 不是整个统计周期内的计数值) */ public int addAndGet(int delta) { return addAndGet(delta, System.currentTimeMillis()); } /** * 先取值后做加法 * @param delta 增加的数字 * @param currentTimeMillis 当前时间(毫秒数), 注意, 这个时间不可以回拨, 回拨超过2个单位时间时会触发所有计数清零 * @return 计算前的数值(当前采样时长内的计数值, 不是整个统计周期内的计数值) */ protected int getAndAdd(int delta, long currentTimeMillis){ return slidingWindowArray.getElement(currentTimeMillis).getAndAdd(delta); } /** * 先做加法后取值 * @param delta 增加的数字 * @param currentTimeMillis 当前时间(毫秒数), 注意, 这个时间不可以回拨, 回拨超过2个单位时间时会触发所有计数清零 * @return 计算后的数值(当前采样时长内的计数值, 不是整个统计周期内的计数值) */ protected int addAndGet(int delta, long currentTimeMillis) { return slidingWindowArray.getElement(currentTimeMillis).addAndGet(delta); } /** * 获取最近一段时间内的计数值, 数值不精确, 存在一定的近似计算. * @param duration 时间 * @return 最近一段时间内的计数值 */ public int getRecently(int duration) { return getRecently(duration, System.currentTimeMillis()); } /** * 获取整个统计周期内的计数值, 数值不精确, 存在一定的近似计算. * @return 整个统计周期内的计数值 */ public int getTotally(){ return getRecently(Integer.MAX_VALUE, System.currentTimeMillis()); } /** * 获取最近一段时间内的计数值, 数值不精确, 存在一定的近似计算. * @param duration 时间 * @param currentTimeMillis 当前时间(毫秒数), 注意, 这个时间不可以回拨, 回拨超过2个单位时间时会触发所有计数清零 * @return 最近一段时间内的计数值 */ protected int getRecently(int duration, long currentTimeMillis) { //获取指定时间范围的统计信息 List> elements = slidingWindowArray.getElementsAccurately(currentTimeMillis, duration); printDebugLog(duration, currentTimeMillis, elements); //数值累加 int result = 0; for (SlidingWindowArray.Element element : elements) { if (element.getWeight() < 1.0f) { //数值 * 权重 = 近似结果 result += Math.round((float) element.getValue().get() * element.getWeight()); } else { result += element.getValue().get(); } } return result; } protected void printDebugLog(int duration, long currentTimeMillis, List> elements) { //override to print debug log } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy