2020-12-22 11:05:52 +08:00
|
|
|
package we.stats;
|
|
|
|
|
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
import java.util.Map;
|
|
|
|
|
import java.util.Map.Entry;
|
|
|
|
|
import java.util.Set;
|
|
|
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
|
|
|
import java.util.concurrent.ConcurrentMap;
|
|
|
|
|
import java.util.concurrent.ExecutorService;
|
|
|
|
|
import java.util.concurrent.Executors;
|
|
|
|
|
|
|
|
|
|
import org.slf4j.Logger;
|
|
|
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Flow Statistic
|
|
|
|
|
*
|
|
|
|
|
* @author Francis Dong
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
public class FlowStat {
|
|
|
|
|
|
|
|
|
|
private static final Logger log = LoggerFactory.getLogger(FlowStat.class);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Time slot interval in millisecond
|
|
|
|
|
*/
|
|
|
|
|
public static long INTERVAL = 1000;
|
|
|
|
|
|
|
|
|
|
/**
|
2020-12-25 10:34:59 +08:00
|
|
|
* Resource ID for all resources entry
|
2020-12-22 11:05:52 +08:00
|
|
|
*/
|
2020-12-25 10:34:59 +08:00
|
|
|
public static String ALL_RESOURCES = "_ALL_RESOURCES";
|
2020-12-22 11:05:52 +08:00
|
|
|
|
|
|
|
|
/**
|
2020-12-25 10:34:59 +08:00
|
|
|
* A string Resource ID as key
|
2020-12-22 11:05:52 +08:00
|
|
|
*/
|
2020-12-25 10:34:59 +08:00
|
|
|
public ConcurrentMap<String, ResourceStat> resourceStats = new ConcurrentHashMap<>();
|
2020-12-22 11:05:52 +08:00
|
|
|
|
|
|
|
|
private ExecutorService pool = Executors.newFixedThreadPool(1);
|
|
|
|
|
|
|
|
|
|
public FlowStat() {
|
|
|
|
|
runHousekeepJob();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void runHousekeepJob() {
|
|
|
|
|
pool.submit(new HousekeepJob(this));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns the current time slot ID
|
|
|
|
|
*
|
|
|
|
|
* @return
|
|
|
|
|
*/
|
|
|
|
|
public long currentTimeSlotId() {
|
|
|
|
|
return (System.currentTimeMillis() / INTERVAL) * INTERVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns the time slot ID of the specified time
|
|
|
|
|
*
|
|
|
|
|
* @param timeMilli
|
|
|
|
|
* @return
|
|
|
|
|
*/
|
|
|
|
|
public long getTimeSlotId(long timeMilli) {
|
|
|
|
|
return (System.currentTimeMillis() / INTERVAL) * INTERVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2020-12-25 10:34:59 +08:00
|
|
|
* Increment concurrent request counter of the specified resource
|
2020-12-22 11:05:52 +08:00
|
|
|
*
|
2020-12-25 10:34:59 +08:00
|
|
|
* @param resourceId Resource ID
|
2020-12-22 11:05:52 +08:00
|
|
|
*/
|
2020-12-25 10:34:59 +08:00
|
|
|
public void incrConcurrentRequest(String resourceId) {
|
|
|
|
|
ResourceStat ResourceStat = getResourceStat(resourceId);
|
|
|
|
|
ResourceStat allResourceStat = getResourceStat(ALL_RESOURCES);
|
|
|
|
|
ResourceStat.incrConcurrentRequest();
|
|
|
|
|
allResourceStat.incrConcurrentRequest();
|
2020-12-22 11:05:52 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2020-12-25 10:34:59 +08:00
|
|
|
* Returns the current concurrent requests of the specified resource<br/>
|
2020-12-22 11:05:52 +08:00
|
|
|
* <br/>
|
2020-12-25 10:34:59 +08:00
|
|
|
* Returns the current concurrent connections of all resources:<br/>
|
|
|
|
|
* getConnection(FlowStat.ALL_RESOURCES)
|
2020-12-22 11:05:52 +08:00
|
|
|
*
|
2020-12-25 10:34:59 +08:00
|
|
|
* @param resourceId Resource ID
|
2020-12-22 11:05:52 +08:00
|
|
|
*/
|
2020-12-25 10:34:59 +08:00
|
|
|
public int getConcurrentRequests(String resourceId) {
|
|
|
|
|
ResourceStat ResourceStat = getResourceStat(resourceId);
|
|
|
|
|
return ResourceStat.getConcurrentRequests().get();
|
2020-12-22 11:05:52 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Add request to current time slot and decrement concurrent connection counter
|
|
|
|
|
*
|
2020-12-25 10:34:59 +08:00
|
|
|
* @param resourceId Resource ID
|
|
|
|
|
* @param rt Response time of request
|
|
|
|
|
* @param isSuccess Whether the request is success or not
|
2020-12-22 11:05:52 +08:00
|
|
|
*/
|
2020-12-25 10:34:59 +08:00
|
|
|
public void incrRequest(String resourceId, long rt, boolean isSuccess) {
|
|
|
|
|
incrRequestToTimeSlot(resourceId, currentTimeSlotId(), rt, isSuccess);
|
2020-12-22 11:05:52 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Add request to the specified time slot and decrement concurrent connection
|
|
|
|
|
* counter
|
|
|
|
|
*
|
2020-12-25 10:34:59 +08:00
|
|
|
* @param resourceId Resource ID
|
2020-12-22 11:05:52 +08:00
|
|
|
* @param timeSlotId TimeSlot ID
|
|
|
|
|
* @param rt Response time of request
|
|
|
|
|
* @param isSuccess Whether the request is success or not
|
|
|
|
|
* @return
|
|
|
|
|
*/
|
2020-12-25 10:34:59 +08:00
|
|
|
public void incrRequestToTimeSlot(String resourceId, long timeSlotId, long rt, boolean isSuccess) {
|
|
|
|
|
if (resourceId == null) {
|
2020-12-22 11:05:52 +08:00
|
|
|
return;
|
|
|
|
|
}
|
2020-12-25 10:34:59 +08:00
|
|
|
ResourceStat ResourceStat = getResourceStat(resourceId);
|
|
|
|
|
ResourceStat allResourceStat = getResourceStat(ALL_RESOURCES);
|
|
|
|
|
ResourceStat.incrRequestToTimeSlot(timeSlotId, rt, isSuccess);
|
|
|
|
|
allResourceStat.incrRequestToTimeSlot(timeSlotId, rt, isSuccess);
|
2020-12-22 11:05:52 +08:00
|
|
|
}
|
|
|
|
|
|
2020-12-25 10:34:59 +08:00
|
|
|
private ResourceStat getResourceStat(String resourceId) {
|
|
|
|
|
ResourceStat ResourceStat = null;
|
|
|
|
|
if (resourceStats.containsKey(resourceId)) {
|
|
|
|
|
ResourceStat = resourceStats.get(resourceId);
|
2020-12-22 11:05:52 +08:00
|
|
|
} else {
|
2020-12-25 10:34:59 +08:00
|
|
|
ResourceStat = new ResourceStat(resourceId);
|
|
|
|
|
ResourceStat rs = resourceStats.putIfAbsent(resourceId, ResourceStat);
|
2020-12-22 11:05:52 +08:00
|
|
|
if (rs != null) {
|
2020-12-25 10:34:59 +08:00
|
|
|
ResourceStat = rs;
|
2020-12-22 11:05:52 +08:00
|
|
|
}
|
|
|
|
|
}
|
2020-12-25 10:34:59 +08:00
|
|
|
return ResourceStat;
|
2020-12-22 11:05:52 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2020-12-25 10:34:59 +08:00
|
|
|
* Returns current TimeWindowStat of the specified resource
|
2020-12-22 11:05:52 +08:00
|
|
|
*
|
2020-12-25 10:34:59 +08:00
|
|
|
* @param resourceId
|
2020-12-22 11:05:52 +08:00
|
|
|
* @return
|
|
|
|
|
*/
|
2020-12-25 10:34:59 +08:00
|
|
|
public TimeWindowStat getCurrentTimeWindowStat(String resourceId) {
|
2020-12-22 11:05:52 +08:00
|
|
|
long startTimeMilli = currentTimeSlotId();
|
2020-12-25 10:34:59 +08:00
|
|
|
return getTimeWindowStat(resourceId, startTimeMilli, startTimeMilli + 1000);
|
2020-12-22 11:05:52 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns the TimeWindowStat of previous second
|
|
|
|
|
*
|
|
|
|
|
* @param timeMilli
|
|
|
|
|
* @return
|
|
|
|
|
*/
|
2020-12-25 10:34:59 +08:00
|
|
|
public TimeWindowStat getPreviousSecondStat(String resourceId, long timeMilli) {
|
2020-12-22 11:05:52 +08:00
|
|
|
long endTimeMilli = (timeMilli / INTERVAL) * INTERVAL;
|
2020-12-25 10:34:59 +08:00
|
|
|
return getTimeWindowStat(resourceId, endTimeMilli - 1000, endTimeMilli);
|
2020-12-22 11:05:52 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2020-12-25 10:34:59 +08:00
|
|
|
* Returns the timeWindowStat of the specific resource in the specified time
|
|
|
|
|
* window [startTimeMilli, endTimeMilli)
|
2020-12-22 11:05:52 +08:00
|
|
|
*
|
|
|
|
|
* @param startTimeMilli included
|
|
|
|
|
* @param endTimemilli excluded
|
|
|
|
|
* @return
|
|
|
|
|
*/
|
2020-12-25 10:34:59 +08:00
|
|
|
public TimeWindowStat getTimeWindowStat(String resourceId, long startTimeMilli, long endTimeMilli) {
|
2020-12-22 11:05:52 +08:00
|
|
|
long startSlotId = (startTimeMilli / INTERVAL) * INTERVAL;
|
|
|
|
|
long endSlotId = (endTimeMilli / INTERVAL) * INTERVAL;
|
|
|
|
|
|
|
|
|
|
if (startSlotId == endSlotId) {
|
|
|
|
|
endSlotId = endSlotId + INTERVAL;
|
|
|
|
|
}
|
2020-12-25 10:34:59 +08:00
|
|
|
if (resourceStats.containsKey(resourceId)) {
|
|
|
|
|
ResourceStat ResourceStat = resourceStats.get(resourceId);
|
|
|
|
|
return ResourceStat.getTimeWindowStat(startSlotId, endSlotId);
|
2020-12-22 11:05:52 +08:00
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2020-12-25 10:34:59 +08:00
|
|
|
* Returns the ResourceTimeWindowStat list in the specified time window
|
2020-12-22 11:05:52 +08:00
|
|
|
* [startTimeMilli, endTimeMilli), The time slot unit is one second
|
|
|
|
|
*
|
2020-12-25 10:34:59 +08:00
|
|
|
* @param resourceId optional, returns ResourceSlot list of all resources
|
|
|
|
|
* while resourceId is null
|
2020-12-22 11:05:52 +08:00
|
|
|
* @param startTimeMilli
|
|
|
|
|
* @param endTimeMilli
|
|
|
|
|
* @return
|
|
|
|
|
*/
|
|
|
|
|
@SuppressWarnings("unused")
|
2020-12-25 10:34:59 +08:00
|
|
|
public List<ResourceTimeWindowStat> getResourceTimeWindowStats(String resourceId, long startTimeMilli,
|
|
|
|
|
long endTimeMilli) {
|
|
|
|
|
return this.getResourceTimeWindowStats(resourceId, startTimeMilli, endTimeMilli, 1);
|
2020-12-22 11:05:52 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2020-12-25 10:34:59 +08:00
|
|
|
* Returns the ResourceTimeWindow list in the specified time window
|
2020-12-22 11:05:52 +08:00
|
|
|
* [startTimeMilli, endTimeMilli)
|
|
|
|
|
*
|
2020-12-25 10:34:59 +08:00
|
|
|
* @param resourceId optional, returns ResourceTimeWindowStat list of all
|
|
|
|
|
* resources while resourceId is null
|
2020-12-22 11:05:52 +08:00
|
|
|
* @param startTimeMilli
|
|
|
|
|
* @param endTimeMilli
|
|
|
|
|
* @param slotIntervalInSec interval of custom time slot in millisecond, such as
|
|
|
|
|
* 60 for 1 minutes
|
|
|
|
|
* @return
|
|
|
|
|
*/
|
|
|
|
|
@SuppressWarnings("unused")
|
2020-12-25 10:34:59 +08:00
|
|
|
public List<ResourceTimeWindowStat> getResourceTimeWindowStats(String resourceId, long startTimeMilli,
|
|
|
|
|
long endTimeMilli, long slotIntervalInSec) {
|
|
|
|
|
List<ResourceTimeWindowStat> list = new ArrayList<>();
|
2020-12-22 11:05:52 +08:00
|
|
|
long startSlotId = (startTimeMilli / INTERVAL) * INTERVAL;
|
|
|
|
|
long endSlotId = (endTimeMilli / INTERVAL) * INTERVAL;
|
|
|
|
|
|
|
|
|
|
if (startSlotId == endSlotId) {
|
|
|
|
|
endSlotId = endSlotId + INTERVAL;
|
|
|
|
|
}
|
|
|
|
|
if (slotIntervalInSec < 1 || (endSlotId - startSlotId) / 1000 < slotIntervalInSec) {
|
|
|
|
|
return list;
|
|
|
|
|
}
|
|
|
|
|
long slotInterval = slotIntervalInSec * 1000;
|
|
|
|
|
|
2020-12-25 10:34:59 +08:00
|
|
|
if (resourceId == null) {
|
|
|
|
|
Set<Map.Entry<String, ResourceStat>> entrys = resourceStats.entrySet();
|
|
|
|
|
for (Entry<String, ResourceStat> entry : entrys) {
|
2020-12-22 11:05:52 +08:00
|
|
|
String rid = entry.getKey();
|
2020-12-25 10:34:59 +08:00
|
|
|
ResourceTimeWindowStat resourceWin = new ResourceTimeWindowStat(rid);
|
2020-12-22 11:05:52 +08:00
|
|
|
for (long i = startSlotId; i < endSlotId;) {
|
2020-12-25 10:34:59 +08:00
|
|
|
TimeWindowStat tws = getTimeWindowStat(resourceId, startSlotId, endSlotId);
|
2020-12-22 11:05:52 +08:00
|
|
|
if (tws != null) {
|
2020-12-25 10:34:59 +08:00
|
|
|
resourceWin.getWindows().add(tws);
|
2020-12-22 11:05:52 +08:00
|
|
|
}
|
|
|
|
|
i = i + slotInterval;
|
|
|
|
|
}
|
2020-12-25 10:34:59 +08:00
|
|
|
if (resourceWin.getWindows().size() > 0) {
|
|
|
|
|
list.add(resourceWin);
|
2020-12-22 11:05:52 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2020-12-25 10:34:59 +08:00
|
|
|
ResourceTimeWindowStat resourceWin = new ResourceTimeWindowStat(resourceId);
|
2020-12-22 11:05:52 +08:00
|
|
|
for (long i = startSlotId; i < endSlotId;) {
|
2020-12-25 10:34:59 +08:00
|
|
|
TimeWindowStat tws = getTimeWindowStat(resourceId, startSlotId, endSlotId);
|
2020-12-22 11:05:52 +08:00
|
|
|
if (tws != null) {
|
2020-12-25 10:34:59 +08:00
|
|
|
resourceWin.getWindows().add(tws);
|
2020-12-22 11:05:52 +08:00
|
|
|
}
|
|
|
|
|
i = i + slotInterval;
|
|
|
|
|
}
|
2020-12-25 10:34:59 +08:00
|
|
|
if (resourceWin.getWindows().size() > 0) {
|
|
|
|
|
list.add(resourceWin);
|
2020-12-22 11:05:52 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return list;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class HousekeepJob implements Runnable {
|
|
|
|
|
|
|
|
|
|
private FlowStat stat;
|
|
|
|
|
|
|
|
|
|
public HousekeepJob(FlowStat stat) {
|
|
|
|
|
this.stat = stat;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void run() {
|
|
|
|
|
long n = 2 * 60 * 60 * 1000 / FlowStat.INTERVAL * FlowStat.INTERVAL;
|
|
|
|
|
long lastSlotId = stat.currentTimeSlotId() - n;
|
|
|
|
|
while (true) {
|
|
|
|
|
log.debug("housekeeping start");
|
|
|
|
|
long slotId = stat.currentTimeSlotId() - n;
|
|
|
|
|
for (long i = lastSlotId; i < slotId;) {
|
2020-12-25 10:34:59 +08:00
|
|
|
Set<Map.Entry<String, ResourceStat>> entrys = stat.resourceStats.entrySet();
|
|
|
|
|
for (Entry<String, ResourceStat> entry : entrys) {
|
|
|
|
|
String resourceId = entry.getKey();
|
2020-12-22 11:05:52 +08:00
|
|
|
ConcurrentMap<Long, TimeSlot> timeSlots = entry.getValue().getTimeSlots();
|
2020-12-25 10:34:59 +08:00
|
|
|
log.debug("housekeeping remove slot: resourceId={} slotId=={}", resourceId, i);
|
2020-12-22 11:05:52 +08:00
|
|
|
timeSlots.remove(i);
|
|
|
|
|
}
|
|
|
|
|
i = i + FlowStat.INTERVAL;
|
|
|
|
|
}
|
|
|
|
|
lastSlotId = slotId;
|
|
|
|
|
log.debug("housekeeping done");
|
|
|
|
|
try {
|
|
|
|
|
Thread.sleep(60 * 1000);
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|