com.github.edgar615.util.vertx.wheel.TimeWheelImpl Maven / Gradle / Ivy
The newest version!
package com.github.edgar615.util.vertx.wheel;
import io.vertx.core.Vertx;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
/**
* Created by edgar on 17-3-19.
*/
class TimeWheelImpl implements TimeWheel {
/**
* 事件轮的周期,一小时
*/
private final int interval;
/**
* 每次检测的间隔时间,一秒
*/
private final int period = 1000;
//游标
private final AtomicInteger cursor = new AtomicInteger(0);
/**
* 环形队列
*/
private final Map> bucket = new ConcurrentHashMap<>();
/**
* 记录任务在桶中的位置
*/
private final Map location = new ConcurrentHashMap<>();
private final long timer;
private final Vertx vertx;
/**
* 设备掉线的事件地址
*/
private final String announceAddress;
TimeWheelImpl(Vertx vertx, TimerWheelOptions options) {
this.vertx = vertx;
this.interval = options.getInterval();
//初始化时间轮
for (int i = 0; i < interval; i++) {
bucket.put(i, new CopyOnWriteArraySet<>());
}
this.announceAddress = options.getAnnounceAddress();
timer = vertx.setPeriodic(period, l -> {
List tasks = forward();
List taskIds = tasks.stream()
.map(t -> t.id())
.collect(Collectors.toList());
if (tasks.size() > 0) {
vertx.eventBus().publish(announceAddress,
new JsonObject()
.put("tasks", new JsonArray(new ArrayList(taskIds)))
.put("time", Instant.now().getEpochSecond()));
}
});
}
/**
* 游标向前移动一格
*
* @return
*/
private synchronized List forward() {
int expiredSolt = cursor
.getAndAccumulate(1, (left, right) -> left + right >= interval ? 0 : left + right);
Set tasks = bucket.get(expiredSolt);
tasks.forEach(t -> t.dec());
List expiredTasks = tasks.stream()
.filter(t -> t.cycleNum() < 0)
.collect(Collectors.toList());
tasks.removeIf(t -> t.cycleNum() < 0);
expiredTasks.forEach(t -> location.remove(t.id()));
//周期性任务重新入队
expiredTasks.stream().filter(t -> t.periodTask()).forEach(t -> {
addTask(t.id(), t.period(), t.period());
});
return expiredTasks;
}
@Override
public synchronized void addTaskAt(int taskId, long unixTimestamp) {
addTask(taskId, unixTimestamp - Instant.now().getEpochSecond());
}
@Override
public synchronized void addTaskAt(int taskId, long unixTimestamp, long period) {
addTask(taskId, unixTimestamp - Instant.now().getEpochSecond(), period);
}
@Override
public synchronized void addTask(int taskId, long delay) {
if (delay <= 0) {
throw new IllegalArgumentException(delay + " < 0");
} else {
int cycle = calCycle(delay);
int solt = calSolt(delay);
reLocate(taskId, solt);
ScheduledTask task = new ScheduledTask(taskId, cycle);
Set tasks = bucket.get(solt);
tasks.add(task);
}
}
@Override
public synchronized void addTask(int taskId, long delay, long period) {
if (delay <= 0) {
throw new IllegalArgumentException(delay + " < 0");
} else {
int cycle = calCycle(delay);
int solt = calSolt(delay);
reLocate(taskId, solt);
ScheduledTask task = new ScheduledTask(taskId, cycle, period);
Set tasks = bucket.get(solt);
tasks.add(task);
}
}
@Override
public int size() {
int size = 0;
for (Map.Entry> tasks : bucket.entrySet()) {
size += tasks.getValue().size();
}
return size;
}
@Override
public boolean close() {
return vertx.cancelTimer(timer);
}
private int calSolt(long delay) {return (int) ((delay + cursor.get()) % interval);}
private int calCycle(long delay) {return (int) (delay / interval);}
private void reLocate(int taskId, int solt) {
Integer oldLocaction = location.put(taskId, solt);
if (oldLocaction != null) {
bucket.get(oldLocaction).remove(new ScheduledTask(taskId, solt));
}
}
}