如何计算给定间隔内的事件数?

时间:2018-01-24 07:23:27

标签: java algorithm counter performancecounter

我需要知道不同事件发生的频率。例如,在过去15分钟内发生了多少HTTP请求。因为可能有大量的事件(数百万),所以必须使用有限的内存。

Java中有没有可以执行此操作的util类?

如何在Java中实现这个自我?

理论用法代码如下:

FrequencyCounter counter = new FrequencyCounter( 15, TimeUnit.Minutes );
...
counter.add();
...
int count = counter.getCount();

编辑:它必须是一个实时值,每分钟可以改变一千次,并且每分钟会查询数千次。基于数据库或文件的解决方案是不可能的。

3 个答案:

答案 0 :(得分:0)

我能想到的最好的方法是使用另一个“计时”线程 如果您担心内存量,可以添加eventsCounter大小的阈值(Integer.MAX_VALUE似乎是自然选择)。

这是一个实现的例子,它也是线程安全的:

public class FrequencyCounter {

    private AtomicInteger eventsCounter = new AtomicInteger(0);
    private int timeCounter;
    private boolean active;

    public FrequencyCounter(int timeInSeconds) {
        timeCounter = timeInSeconds;
        active = true;
    }

    // Call this method whenever an interesting event occurs
    public int add() {
        if(active) {
            int current;
            do {
                current = eventsCounter.get();
            } while (eventsCounter.compareAndSet(current, current + 1));

            return current + 1;
        }
        else return -1;
    }

    // Get current number of events
    public int getCount() {
        return eventsCounter.get();
    }

    // Start the FrequencyCounter
    public void run() {
        Thread timer = new Thread(() -> {
            while(timeCounter > 0) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                timeCounter --;
            }
            active = false;
        });
        timer.start();
    }
}

答案 1 :(得分:0)

预定的执行者服务如何?

class TimedValue{
    int startValue;
    int finishedValue;
    TimedValue(int start){
         startValue = start;
    }
}

List<TimedValue> intervals = new CopyOnWriteArrayList<>();

//then when starting a measurement.
TimeValue value = new TimedValue();

//set the start value. 
Callable<TimedValue> callable = ()->{
    //performs the task.

    value.setValueAtFinish(getCount());
    return value;
}

ScheduledExecutorService executor = Executors.newScheduledThreadPool(2);

ScheduledFuture<TimedValue> future = executor.schedule(
                                       callable, 
                                       TimeUnit.MINUTES, 
                                       15);

executor.schedule(()->itervals.add(
                        future.get(),
                        TimeUnit.MINUTES, 
                        future.getDelay(TimeUnit.MINUTES
                      );

这是一个复杂的方法。

我可能只有一个List<LoggedValues>并以固定的速率累积该列表中的值。然后,只要您想知道间隔,就可以进行检查。

答案 2 :(得分:0)

这是我对这种计数器的实现。具有默认精度的内存使用量少于100个字节。内存使用情况与事件计数无关。

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

/**
 * A counter that counts events within the past time interval. All events that occurred before this interval will be
 * removed from the counter.
 */
public class FrequencyCounter {

    private final long          monitoringInterval;

    private final int[]         details;

    private final AtomicInteger currentCount = new AtomicInteger();

    private long                startInterval;

    private int                 total;

    /**
     * Create a new instance of the counter for the given interval.
     * 
     * @param interval the time to monitor/count the events.
     * @param unit the time unit of the {@code interval} argument
     */
    FrequencyCounter( long interval, TimeUnit unit ) {
        this( interval, unit, 16 );
    }

    /**
     * Create a new instance of the counter for the given interval.
     * 
     * @param interval the time to monitor/count the events.
     * @param unit the time unit of the {@code interval} argument
     * @param precision the count of time slices for the for the measurement
     */
    FrequencyCounter( long interval, TimeUnit unit, int precision ) {
        monitoringInterval = unit.toMillis( interval );
        if( monitoringInterval <= 0 ) {
            throw new IllegalArgumentException( "Interval mus be a positive value:" + interval );
        }
        details = new int[precision];
        startInterval = System.currentTimeMillis() - monitoringInterval;
    }

    /**
     * Count a single event.
     */
    public void increment() {
        checkInterval( System.currentTimeMillis() );
        currentCount.incrementAndGet();
    }

    /**
     * Get the current value of the counter.
     * 
     * @return the counter value
     */
    public int getCount() {
        long currentTime = System.currentTimeMillis();
        checkInterval( currentTime );
        long diff = currentTime - startInterval - monitoringInterval;

        double partFactor = (diff * details.length / (double)monitoringInterval);
        int part = (int)(details[0] * partFactor);
        return total + currentCount.get() - part;
    }

    /**
     * Check the interval of the detail counters and move the interval if needed.
     * 
     * @param time the current time
     */
    private void checkInterval( final long time ) {
        if( (time - startInterval - monitoringInterval) > monitoringInterval / details.length ) {
            synchronized( details ) {
                long detailInterval = monitoringInterval / details.length;
                while( (time - startInterval - monitoringInterval) > detailInterval ) {
                    int currentValue = currentCount.getAndSet( 0 );
                    if( (total | currentValue) == 0 ) {
                        // for the case that the counter was not used for a long time
                        startInterval = time - monitoringInterval;
                        return;
                    }
                    int size = details.length - 1;
                    total += currentValue - details[0];
                    System.arraycopy( details, 1, details, 0, size );
                    details[size] = currentValue;
                    startInterval += detailInterval;
                }
            }
        }
    }
}