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

org.apache.dubbo.metrics.aggregate.SlidingWindow Maven / Gradle / Ivy

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 org.apache.dubbo.metrics.aggregate;

import org.apache.dubbo.common.utils.Assert;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReferenceArray;
import java.util.concurrent.locks.ReentrantLock;

/**
 * SlidingWindow adopts sliding window algorithm for statistics.
 * 

* A window contains {@code paneCount} panes, * {@code intervalInMs} = {@code paneCount} * {@code paneIntervalInMs} * * @param Value type for window statistics. */ public abstract class SlidingWindow { /** * The number of panes the sliding window contains. */ protected int paneCount; /** * Total time interval of the sliding window in milliseconds. */ protected long intervalInMs; /** * Time interval of a pane in milliseconds. */ protected long paneIntervalInMs; /** * The panes reference, supports atomic operations. */ protected final AtomicReferenceArray> referenceArray; /** * The lock is used only when current pane is deprecated. */ private final ReentrantLock updateLock = new ReentrantLock(); protected SlidingWindow(int paneCount, long intervalInMs) { Assert.assertTrue(paneCount > 0, "pane count is invalid: " + paneCount); Assert.assertTrue(intervalInMs > 0, "total time interval of the sliding window should be positive"); Assert.assertTrue(intervalInMs % paneCount == 0, "total time interval needs to be evenly divided"); this.paneCount = paneCount; this.intervalInMs = intervalInMs; this.paneIntervalInMs = intervalInMs / paneCount; this.referenceArray = new AtomicReferenceArray<>(paneCount); } /** * Get the pane at the current timestamp. * * @return the pane at current timestamp. */ public Pane currentPane() { return currentPane(System.currentTimeMillis()); } /** * Get the pane at the specified timestamp in milliseconds. * * @param timeMillis a timestamp in milliseconds. * @return the pane at the specified timestamp if the time is valid; null if time is invalid. */ public Pane currentPane(long timeMillis) { if (timeMillis < 0) { return null; } int paneIdx = calculatePaneIdx(timeMillis); long paneStartInMs = calculatePaneStart(timeMillis); while (true) { Pane oldPane = referenceArray.get(paneIdx); // Create a pane instance when the pane does not exist. if (oldPane == null) { Pane pane = new Pane<>(paneIntervalInMs, paneStartInMs, newEmptyValue(timeMillis)); if (referenceArray.compareAndSet(paneIdx, null, pane)) { return pane; } else { // Contention failed, the thread will yield its time slice to wait for pane available. Thread.yield(); } } // else if (paneStartInMs == oldPane.getStartInMs()) { return oldPane; } // The pane has deprecated. To avoid the overhead of creating a new instance, reset the original pane // directly. else if (paneStartInMs > oldPane.getStartInMs()) { if (updateLock.tryLock()) { try { return resetPaneTo(oldPane, paneStartInMs); } finally { updateLock.unlock(); } } else { // Contention failed, the thread will yield its time slice to wait for pane available. Thread.yield(); } } // The specified timestamp has passed. else if (paneStartInMs < oldPane.getStartInMs()) { return new Pane<>(paneIntervalInMs, paneStartInMs, newEmptyValue(timeMillis)); } } } /** * Get statistic value from pane at the specified timestamp. * * @param timeMillis the specified timestamp in milliseconds. * @return the statistic value if pane at the specified timestamp is up-to-date; otherwise null. */ public T getPaneValue(long timeMillis) { if (timeMillis < 0) { return null; } int paneIdx = calculatePaneIdx(timeMillis); Pane pane = referenceArray.get(paneIdx); if (pane == null || !pane.isTimeInWindow(timeMillis)) { return null; } return pane.getValue(); } /** * Create a new statistic value for pane. * * @param timeMillis the specified timestamp in milliseconds. * @return new empty statistic value. */ public abstract T newEmptyValue(long timeMillis); /** * Reset given pane to the specified start time and reset the value. * * @param pane the given pane. * @param startInMs the start timestamp of the pane in milliseconds. * @return reset pane. */ protected abstract Pane resetPaneTo(final Pane pane, long startInMs); /** * Calculate the pane index corresponding to the specified timestamp. * * @param timeMillis the specified timestamp. * @return the pane index corresponding to the specified timestamp. */ private int calculatePaneIdx(long timeMillis) { return (int) ((timeMillis / paneIntervalInMs) % paneCount); } /** * Calculate the pane start corresponding to the specified timestamp. * * @param timeMillis the specified timestamp. * @return the pane start corresponding to the specified timestamp. */ protected long calculatePaneStart(long timeMillis) { return timeMillis - timeMillis % paneIntervalInMs; } /** * Checks if the specified pane is deprecated at the current timestamp. * * @param pane the specified pane. * @return true if the pane is deprecated; otherwise false. */ public boolean isPaneDeprecated(final Pane pane) { return isPaneDeprecated(System.currentTimeMillis(), pane); } /** * Checks if the specified pane is deprecated at the specified timestamp. * * @param timeMillis the specified time. * @param pane the specified pane. * @return true if the pane is deprecated; otherwise false. */ public boolean isPaneDeprecated(long timeMillis, final Pane pane) { // the pane is '[)' return (timeMillis - pane.getStartInMs()) > intervalInMs; } /** * Get valid pane list for entire sliding window at the current time. * The list will only contain "valid" panes. * * @return valid pane list for entire sliding window. */ public List> list() { return list(System.currentTimeMillis()); } /** * Get valid pane list for entire sliding window at the specified time. * The list will only contain "valid" panes. * * @param timeMillis the specified time. * @return valid pane list for entire sliding window. */ public List> list(long timeMillis) { if (timeMillis < 0) { return new ArrayList<>(); } List> result = new ArrayList<>(paneCount); for (int idx = 0; idx < paneCount; idx++) { Pane pane = referenceArray.get(idx); if (pane == null || isPaneDeprecated(timeMillis, pane)) { continue; } result.add(pane); } return result; } /** * Get aggregated value list for entire sliding window at the current time. * The list will only contain value from "valid" panes. * * @return aggregated value list for entire sliding window. */ public List values() { return values(System.currentTimeMillis()); } /** * Get aggregated value list for entire sliding window at the specified time. * The list will only contain value from "valid" panes. * * @return aggregated value list for entire sliding window. */ public List values(long timeMillis) { if (timeMillis < 0) { return new ArrayList<>(); } List result = new ArrayList<>(paneCount); for (int idx = 0; idx < paneCount; idx++) { Pane pane = referenceArray.get(idx); if (pane == null || isPaneDeprecated(timeMillis, pane)) { continue; } result.add(pane.getValue()); } return result; } /** * Get total interval of the sliding window in milliseconds. * * @return the total interval in milliseconds. */ public long getIntervalInMs() { return intervalInMs; } /** * Get pane interval of the sliding window in milliseconds. * * @return the interval of a pane in milliseconds. */ public long getPaneIntervalInMs() { return paneIntervalInMs; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy