基于 Kafka 与时间轮实现高性能延迟消息

1.整体链路

Kafka → RocksDB → SystemTimer → TimingWheel → Kafka

Java 复制代码
public void sendDelay(long delayMs, String topic, String message) {
    try {
        DelayMessage delayMessage = new DelayMessage(delayMs, topic, message);
        rocksDB.put(delayMessage.getKey(), delayMessage.toJson().getBytes());
        systemTimer.add(delayMessage);
    } catch (Exception e) {
        log.error("sendDelayMessage error delayMs:{}, topic:{}, message:{}", delayMs, topic, message, e);
        throw new RuntimeException(e);
    }
}
  • Kafka 生产者调用自定义延迟消息方法
  • 延迟消息入库(RocksDB)
  • 封装任务加入时间轮
  • 时间轮触发
  • run方法执行,将消息发送kafka,清除RocksDB

2.DelayMessage:延迟任务的最小数据模型

抽象类 TimerTask 的一种任务实现

Java 复制代码
private final String message; // 消息体
private final long expireTime; // 绝对延迟时间 currentTimeMillis + delayMs
private final String topic; // 主题
private final Long partitionKey; // 分区
private final long delayMs; // 相对延迟时间

3.RocksDB:消息持久化组件

  • 服务恢复后重新加载延迟任务
  • 删除超出延迟上限的任务
    • 4.SystemTimer:延迟任务调度入口

1.systemTimer.add(delayMessage)

将 DelayMessage/TimerTask 封装为 TimerTaskEntry,

也就是环形数组中,每个桶下面的链表中的一个节点对象

2.advanceClock() 触发 bucket

定时任务线程池,每个500ms检查一次 delayQueue

如果队列中有链表到期(桶的左边界),则推进时间轮,并执行槽内任务

5.TimerTask / TimerTaskEntry:任务包装结构

  • TimerTask:最终执行逻辑(调用 KafkaTemplate 发送)
  • TimerTaskEntry:环形双向链表节点 + expirationMs(具体的延迟时间)

TimerTask 与 TimerTaskEntry 双向绑定

TimerTask 持有 TimerTaskEntry 的引用

TimerTaskEntry 持有 TimerTask 的引用

1.取消任务

TimerTask 取消任务时,会将持有的 TimerTaskEntry 任务条目从链表中移除,并将引用置为空,

判断是否取消任务时,会根据 TimerTask 持有的引用是否 == this 来判断

2.加入时间轮与时间轮降级

bucket.add(timerTaskEntry) 时,需要先移除当前任务条目在链表中的旧引用,防止重复执行

3.保证线程安全

setTimerTaskEntry 设置条目时,synchronized 加锁,保证一个任务只属于一个条目

6.TimerTaskList:槽位结构与过期时间机制

时间轮(TimingWheel)是一个 存储定时任务的环形队列,

底层采用数组实现,数组中的每个元素可以存放一个定时任务列表(TimerTaskList)

TimerTaskList 是一个环形的双向链表,

链表中的每一项表示的都是定时任务条目(TimerTaskEntry),其中封装了真正的定时任务 TimerTask。

ReentrantReadWriteLock 读写锁

✅添加任务时,加读锁,禁止并发写(flush)干扰。

✅推进时间轮时,flush执行槽内任务时,加写锁,禁止并发读(add)写(flush)修改结构。

7.TimingWheel:时间轮核心实现

基本属性组成

C++ 复制代码
private Long tickMs; // 时间轮每一格的时间间隔(基本时间跨度)
private Integer wheelSize; // 时间轮的格子数
private Long interval; // 时间轮一圈的总体时间跨度
private Long startMs; // 时间轮的起始时间
private AtomicInteger taskCounter; // 全局待执行任务计数器
private DelayQueue<TimerTaskList> queue; // 多层时间轮任务共享队列
private Long currentTime; // 时间轮当前指针指向的时间,是 tickMs 的整数倍
private volatile TimingWheel overflowWheel; // 上一轮(基本时间跨度是当前轮的总体时间跨度)
private TimerTaskList[] buckets; // 环形数组

add 方法

expiration:任务的绝对到期时间 (delayMs + Time.getHiresClockMs())

1.expiration < currentTime + tickMs:任务到期,如果没有取消,交由线程池执行

2.expiration < currentTime + interval:任务落到主轮,未到期,加入时间轮,设置到期时间,加入队列

3.expiration >= currentTime + interval:任务超过主轮覆盖范围,加入上一轮(递归调用 add)

advanceClock 方法

尝试推进时间轮,当 timeMs >= currentTime + tickMs 时,

任务条目绝对到期时间 >= 当前时间 + 基本时间跨度,更新时间轮,

如果有父轮,递归尝试推进父轮,父轮的一格,通常等于主轮的一圈。

8.多层时间轮:任务降级流程

假设有三层时间轮

主轮:tickMs(1s)wheelSize(20s)interval(20s)

父轮:tickMs(20s)wheelSize(20s)interval(400s)

爷轮:tickMs(400s)wheelSize(20s)interval(8000s)

📌第一次插入

  • expireTime = 450s
  • 主轮覆盖 0~20s → 放不下
  • 调父轮(20~400s)仍放不下
  • 调爷轮(400~8000s)命中
  • 爷轮 slot = 400 (覆盖 400~800)
  • 插入成功

📌到 400s 时触发 flush

  • 链表销毁重建,从爷轮回溯到父轮
  • 任务剩余:450 - 400 = 50s

📌重新 add → 回溯至父轮

  • currentTime = 400s,父轮覆盖 400~800
  • 剩余 50s 落入父轮
  • slot 覆盖范围 40~60s
  • 插入成功

📌到 440s 时触发 flush

  • 任务剩余:50 - 40 = 10s
  • 剩余 10s 落入主轮
  • Slot 覆盖范围 9~10s
  • 插入成功

📌主轮在 450s 触发 flush

  • 执行 TimerTask
  • 通过 IrcpKafkaTemplate 发送 Kafka

9.服务重启恢复机制:RocksDB → SystemTimer

  • RocksIterator 扫描所有 DelayMessage
  • 判定是否超出最大延迟
  • 加入时间轮
  • 删除过期 Key

10.代码实现

IrcpKafkaTemplate (自定义kafka工具类)

Java 复制代码
@Service
@Slf4j
public class IrcpKafkaTemplate {

    private static KafkaTemplate<String, Object> staticKafkaTemplate;
    private static RocksDB rocksDB;
    private final RocksDBManager rocksdbManager;
    private final SystemTimer systemTimer;
    private final ScheduledExecutorService executorService;
    
    @Value("${delay.mq.max.delay.time:86400000}")
    private long delayMqMaxDelayTime; // 消息从rocksdb中恢复时,若过期时间超过了配置,则会丢弃
    @Resource
    private KafkaTemplate<String, Object> kafkaTemplate;
    @Autowired
    public IrcpKafkaTemplate(RocksDBManager rocksdbManager) {
        this.rocksdbManager = rocksdbManager;
        this.systemTimer = new SystemTimer("kafka-delay-timer");
        this.executorService = Executors.newSingleThreadScheduledExecutor();
    }

    @PostConstruct
    public void init() throws RocksDBException {
        rocksDB = rocksdbManager.getRocksDB();
        staticKafkaTemplate = kafkaTemplate;

        // 服务启动时,从RocksDB恢复数据到时间轮
        List<byte[]> keysToDelete = new ArrayList<>();
        try (RocksIterator it = rocksDB.newIterator()) {
            long l = System.currentTimeMillis();
            for (it.seekToFirst(); it.isValid(); it.next()) {
                String json = new String(it.value());
                DelayMessage delayMessage = DelayMessage.fromJson(json);
                if (delayMessage.getExpireTime() - l > delayMqMaxDelayTime) {
                    log.warn("recover from rocksdb will discard. expireTime:{} - currentTimeMillis:{} > configMaxDelayTime:{}, delayMessage:{}", delayMessage.getExpireTime(), l, delayMqMaxDelayTime, delayMessage.toJson());
                    keysToDelete.add(delayMessage.getKey());
                    continue;
                }
                log.info("recover from rocksdb delayMessage:{}", delayMessage.toJson());
                systemTimer.add(delayMessage);
            }
        }
        keysToDelete.forEach(key -> {
            try {
                rocksDB.delete(key);
            } catch (RocksDBException e) {
                log.error("failed to delete key from RocksDB. key:{}", key, e);
            }
        });

        executorService.scheduleAtFixedRate(() -> {
            try {
                systemTimer.advanceClock(200);
            } catch (Exception e) {
                log.error("advanceClock error", e);
            }
        }, 5000, 500, TimeUnit.MILLISECONDS);
    }

    public static KafkaTemplate<String, Object> getKafkaTemplate() {
        return staticKafkaTemplate;
    }

    /**
     * 消息延迟发送
     *
     * @param delayMs 延迟时间(毫秒), 延迟时间最低1000ms,建议一般 >=5s
     * @param topic   需要发送的主题
     * @param message 需要发送的消息
     */
    public void sendDelay(long delayMs, String topic, String message) {
        try {
            DelayMessage delayMessage = new DelayMessage(delayMs, topic, message);
            rocksDB.put(delayMessage.getKey(), delayMessage.toJson().getBytes());
            // 多态
            systemTimer.add(delayMessage);
        } catch (Exception e) {
            log.error("sendDelayMessage error delayMs:{}, topic:{}, message:{}", delayMs, topic, message, e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 消息延迟发送 且 指定分区
     *
     * @param delayMs      延迟时间, 延迟时间最低1000ms,建议一般 >=5s
     * @param topic        需要发送的主题
     * @param partitionKey 根据key计算分区
     * @param message      需要发送的消息
     */
    public void sendDelay(long delayMs, String topic, Long partitionKey, String message) {
        try {
            DelayMessage delayMessage = new DelayMessage(delayMs, topic, partitionKey, message);
            rocksDB.put(delayMessage.getKey(), delayMessage.toJson().getBytes());
            systemTimer.add(delayMessage);
        } catch (Exception e) {
            log.error("sendDelayMessage error delayMs:{}, topic:{}, partitionKey:{}, message:{}", delayMs, topic, partitionKey, message, e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 消息正常发送
     *
     * @param topic   主题
     * @param message 消息
     */
    public void send(String topic, String message) {
        kafkaTemplate.send(topic, message);
    }

    /**
     * 消息正常发送 且 指定分区
     *
     * @param topic        主题
     * @param partitionKey 根据key计算分区
     * @param message      消息
     */
    public void send(String topic, Long partitionKey, String message) {
        kafkaTemplate.send(topic, String.valueOf(partitionKey), message);
    }

    public static void removeFromRocksdb(DelayMessage delayMessage) throws RocksDBException {
        rocksDB.delete(delayMessage.getKey());
    }

    @PreDestroy
    public void stop() {
        rocksdbManager.close();
        executorService.shutdown();
        try {
            if (!executorService.awaitTermination(5, TimeUnit.SECONDS)) {
                executorService.shutdownNow();
            }
        } catch (InterruptedException e) {
            log.error("stop executorService error", e);
            executorService.shutdownNow();
        }
    }
}

DelayMessage

Java 复制代码
@Slf4j
public class DelayMessage extends TimerTask {

    public static final String EXPIRE_TIME = "expireTime";
    public static final String PARTITION_KEY = "partitionKey";
    public static final String TOPIC = "topic";
    public static final String MESSAGE = "message";
    public static final String DELAY_MS = "delayMs";

    // 延迟任务信息
    private final String message;
    private final long expireTime;
    private final String topic;
    private final Long partitionKey;
    private final long delayMs;

    public DelayMessage(long delay, String topic, String message) {
        this.delayMs = delay < 1000 ? 1000 : delay;
        this.expireTime = System.currentTimeMillis() + delayMs;
        this.topic = topic;
        this.message = message;
        this.partitionKey = null;
    }

    public DelayMessage(long delay, String topic, Long partitionKey, String message) {
        this.delayMs = delay < 1000 ? 1000 : delay;
        this.expireTime = System.currentTimeMillis() + delayMs;
        this.topic = topic;
        this.partitionKey = partitionKey;
        this.message = message;
    }

    public DelayMessage(long delay, long expireTime, String topic, Long partitionKey, String message) {
        this.delayMs = delay;
        this.expireTime = expireTime;
        this.topic = topic;
        this.partitionKey = partitionKey;
        this.message = message;
    }

    public String getTopic() {
        return topic;
    }

    public String getMessage() {
        return message;
    }

    public byte[] getKey() {
        return MD5Util.getMd5Str(toJson()).getBytes(StandardCharsets.UTF_8);
    }

    public byte[] getValue() {
        return message.getBytes(StandardCharsets.UTF_8);
    }

    public long getExpireTime() {
        return expireTime;
    }

    @Override
    public Long getDelayMs() {
        return delayMs;
    }

    @Override
    public void run() {
        log.info("sendToKafka delayMessage: {}", toJson());
        try {
            if (partitionKey == null) {
                IrcpKafkaTemplate.getKafkaTemplate().send(topic, message);
            } else {
                IrcpKafkaTemplate.getKafkaTemplate().send(topic, String.valueOf(partitionKey), message);
            }

            try {
                IrcpKafkaTemplate.removeFromRocksdb(this);
            } catch (Exception e) {
                log.error("after send kafka, remove from rocksdb error message:{}", toJson(), e);
            }

        } catch (Exception e) {
            log.error("sendToKafka error delayMessage: {}", toJson(), e);
        }
    }

    public String toJson() {
        Map<String, String> map = new HashMap<>();
        map.put(MESSAGE, message);
        map.put(TOPIC, topic);
        if (partitionKey != null) {
            map.put(PARTITION_KEY, String.valueOf(partitionKey));
        }
        map.put(EXPIRE_TIME, String.valueOf(expireTime));
        map.put(DELAY_MS, String.valueOf(delayMs));
        return JSONUtil.toJsonStr(map);
    }

    public static DelayMessage fromJson(String json) {
        Map<String, String> map = JSONUtil.toBean(json, Map.class);
        long delayMs = Long.parseLong(map.get(DELAY_MS));
        long expireTime = Long.parseLong(map.get(EXPIRE_TIME));
        String topic = map.get(TOPIC);
        String message = map.get(MESSAGE);
        Long partitionKey = map.get(PARTITION_KEY) == null ? null : Long.parseLong(map.get(PARTITION_KEY));
        return new DelayMessage(delayMs, expireTime, topic, partitionKey, message);
    }
}

SystemTimer

Java 复制代码
@Slf4j
public class SystemTimer implements Timer, Function<TimerTaskEntry, Void> {

    private ExecutorService taskExecutor; // 执行任务线程池
    private Long tickMs; // 基本时间跨度
    private Integer wheelSize; // 时间轮的格子数
    private Long startMs; // 时间轮的起始时间
    private DelayQueue<TimerTaskList> delayQueue = new DelayQueue<>();
    private AtomicInteger taskCounter = new AtomicInteger(0); // 记录待执行任务的数量
    private TimingWheel timingWheel; // 时间轮对象(管理所有任务的分配和延迟触发)

    private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    // 读锁用于添加任务
    private final ReentrantReadWriteLock.ReadLock readLock = readWriteLock.readLock();
    // 写锁用于推进时间轮,flush执行任务
    private final ReentrantReadWriteLock.WriteLock writeLock = readWriteLock.writeLock();

    public SystemTimer(String executeName) {
        // 每格1s,总共60格
        tickMs = 1000L;
        wheelSize = 60;
        startMs = Time.getHiresClockMs();
        taskExecutor = new ThreadPoolExecutor(10, 20,
                0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<>(Integer.MAX_VALUE), r -> new Thread(r, executeName));
        timingWheel = new TimingWheel(tickMs, wheelSize, startMs, taskCounter, delayQueue);

        // 每30s打印一次当前待执行任务数量
        Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(() -> {
            log.info("delay message for number of tasks pending execution: {}", size());
        }, 0, 30, TimeUnit.SECONDS);
    }

    @Override
    public void add(TimerTask timerTask) {
        readLock.lock();
        try {
            // 将 timerTask 封装为 TimerTaskEntry,记录到期时间
            addTimerTaskEntry(new TimerTaskEntry(timerTask, timerTask.getDelayMs() + Time.getHiresClockMs()));
        } finally {
            readLock.unlock();
        }
    }

    @Override
    public boolean advanceClock(long timeoutMs) {
        try {
            TimerTaskList bucket = delayQueue.poll(timeoutMs, TimeUnit.MILLISECONDS);
            if (bucket != null) {
                // 推进时间轮,不允许写
                writeLock.lock();
                try {
                    while (bucket != null) {
                        timingWheel.advanceClock(bucket.getExpiration());
                        // 执行槽内任务
                        bucket.flush(this);
                        bucket = delayQueue.poll();
                    }
                } finally {
                    writeLock.unlock();
                }
                return true;
            } else {
                return false;
            }
        } catch (InterruptedException e) {
            log.error("advanceClock interrupted", e);
        }
        return false;
    }

    @Override
    public int size() {
        return taskCounter.get();
    }

    @Override
    public void shutdown() {
        taskExecutor.shutdown();
    }

    private void addTimerTaskEntry(TimerTaskEntry timerTaskEntry) {
        if (!timingWheel.add(timerTaskEntry)) {
            // 任务没被取消,直接提交线程池执行
            if (!timerTaskEntry.cancelled()) {
                taskExecutor.submit(timerTaskEntry.getTimerTask());
            }
        }
    }

    @Override
    public Void apply(TimerTaskEntry timerTaskEntry) {
        addTimerTaskEntry(timerTaskEntry);
        return null;
    }
}

TimingWheel

Java 复制代码
public class TimingWheel {
    private Long tickMs; // 时间轮每一格的时间间隔(基本时间跨度)
    private Integer wheelSize; // 时间轮的格子数
    private Long interval; // 时间轮一圈的总体时间跨度
    private Long startMs; // 时间轮的起始时间
    private AtomicInteger taskCounter; // 全局待执行任务计数器
    private DelayQueue<TimerTaskList> queue; // 多层时间轮任务共享队列
    private Long currentTime; // 时间轮当前指针指向的时间,是 tickMs 的整数倍
    private volatile TimingWheel overflowWheel; // 上一轮(基本时间跨度是当前轮的总体时间跨度)
    private TimerTaskList[] buckets; // 环形数组

    public TimingWheel(Long tickMs, Integer wheelSize, Long startMs, AtomicInteger taskCounter, DelayQueue<TimerTaskList> queue) {
        this.tickMs = tickMs;
        this.wheelSize = wheelSize;
        this.startMs = startMs;
        this.taskCounter = taskCounter;
        this.queue = queue;
        this.interval = tickMs * wheelSize;
        this.currentTime = startMs - (startMs % tickMs);
        this.buckets = new TimerTaskList[wheelSize];
        for (int i = 0; i < buckets.length; i++) {
            buckets[i] = new TimerTaskList(taskCounter);
        }
    }

    /**
     * 将一个任务加入时间轮
     */
    public boolean add(TimerTaskEntry timerTaskEntry) {
        long expiration = timerTaskEntry.getExpirationMs();

        // 如果任务已经取消,则不加入
        if (timerTaskEntry.cancelled()) {
            // Cancelled
            return false;
        }
        // 任务到期(到期时间 < 当前轮+下一格时间)
        else if (expiration < currentTime + tickMs) {
            // Already expired
            return false;
        } else if (expiration < currentTime + interval) {
            // 计算任务应该落入哪一个槽位
            long virtualId = expiration / tickMs;
            TimerTaskList bucket = buckets[(int) (virtualId % wheelSize)];
            bucket.add(timerTaskEntry);

            // 设置槽位的到期时间
            if (bucket.setExpiration(virtualId * tickMs)) {
                queue.offer(bucket);
            }
            return true;
        }
        // 任务超出当前轮覆盖范围,放入父轮
        else {
            // Out of the interval. Put it into the parent timer
            if (overflowWheel == null) {
                // 创建父轮,一格时间 = 主轮一圈时间
                addOverflowWheel();
            }
            return overflowWheel.add(timerTaskEntry);
        }
    }

    /**
     * 尝试推进时间轮的时钟(更新时间轮当前的时钟)
     */
    public void advanceClock(Long timeMs) {
        if (timeMs >= currentTime + tickMs) {
            // 更新为目标格子的左边界
            currentTime = timeMs - (timeMs % tickMs);

            if (overflowWheel != null) {
                // 推进父轮
                overflowWheel.advanceClock(currentTime);
            }
        }
    }

    private void addOverflowWheel() {
        synchronized (this) {
            if (overflowWheel == null) {
                overflowWheel = new TimingWheel(interval, wheelSize, currentTime, taskCounter, queue);
            }
        }
    }
}

TimerTask

Java 复制代码
public abstract class TimerTask implements Runnable {

    protected Long delayMs = 3000L;
    protected TimerTaskEntry timerTaskEntry; // 当前任务所在链表节点

    public void cancel() {
        synchronized (this) {
            if (timerTaskEntry != null) {
                timerTaskEntry.remove(); // 从时间轮桶移除
            }
            timerTaskEntry = null;
        }
    }

    /**
     * 双向绑定
     * TimerTask 持有 TimerTaskEntry 的引用
     * TimerTaskEntry 持有 TimerTask 的引用
     * <p>
     *     TimerTaskEntry:封装了 TimerTask 和它的调度信息(到期时间 expirationMs、所在桶 list 等).
     *     TimerTask:实际要执行的任务,可能是 DelayMessage、DelayMessage2 等.
     * </p>
     * 作用:
     * 1.取消任务 cancelled() ,通过引用指向判断是否被取消
     * 2.重入时间轮 SystemTimer#apply(),需要根据 timerTask.remove() --> timerTask.getTimerTaskEntry() 找到旧条目,从原桶中移除,然后再把任务加入新的桶中
     * 3.保证线程安全,set 方法锁 this,保证一个任务同时只属于一个条目
     */
    public void setTimerTaskEntry(TimerTaskEntry entry) {
        synchronized (this) {
            if (timerTaskEntry != null && timerTaskEntry != entry) {
                timerTaskEntry.remove();
            }
            timerTaskEntry = entry;
        }
    }

    @Override
    public void run() {}

    public TimerTaskEntry getTimerTaskEntry() {
        return timerTaskEntry;
    }

    public Long getDelayMs() {
        return delayMs;
    }
}

TimerTaskEntry

Java 复制代码
public class TimerTaskEntry implements Comparable<TimerTaskEntry> {

    private volatile TimerTaskList list; // 当前所在桶
    public TimerTaskEntry next; // 前驱
    public TimerTaskEntry prev; // 后继
    private TimerTask timerTask; // 包含的任务
    private Long expirationMs; // 到期时间,用于排序

    public TimerTaskEntry() {}

    public TimerTaskEntry(TimerTask timerTask, Long expirationMs) {
        if (timerTask != null) {
            timerTask.setTimerTaskEntry(this);
        }
        this.timerTask = timerTask;
        this.expirationMs = expirationMs;

    }

    /**
     * 每个 timerTask 内部会持有一个引用 timerTaskEntry,表示当前任务被哪个条目管理
     * 如果当前任务被取消,则 timerTaskEntry 会被设置为 null,或者 timerTaskEntry 会被设置为其他 timerTaskEntry
     * @return
     */
    public boolean cancelled() {
        return timerTask.getTimerTaskEntry() != this;
    }

    public void remove() {
        TimerTaskList currentList = list;
        while (currentList != null) {
            currentList.remove(this);
            currentList = list;
        }
    }

    @Override
    public int compareTo(TimerTaskEntry that) {
        if (that == null) {
            throw new NullPointerException("TimerTaskEntry is null");
        }
        Long expirationMs1 = this.expirationMs;
        Long expirationMs2 = that.expirationMs;
        if (expirationMs1 < expirationMs2) {
            return -1;
        }
        if (expirationMs1 > expirationMs2) {
            return 1;
        }
        return 0;
    }


    public Long getExpirationMs() {
        return expirationMs;
    }

    public TimerTask getTimerTask() {
        return timerTask;
    }

    public TimerTaskList getList() {
        return list;
    }

    public void setList(TimerTaskList list) {
        this.list = list;
    }
}

TimerTaskList

Java 复制代码
class TimerTaskList implements Delayed {
    
    private AtomicInteger taskCounter; // 桶内任务数量计数器
    private TimerTaskEntry root; // 哨兵节点,形成循环双向链表
    private AtomicLong expiration; // 桶的过期时间(每个格子的左边界)
    
    public TimerTaskList() {}

    public TimerTaskList(AtomicInteger taskCounter) {
        this.taskCounter = taskCounter;
        this.root = new TimerTaskEntry(null, -1L);
        this.root.next = root;
        this.root.prev = root;
        this.expiration = new AtomicLong(-1L);
    }

    public boolean setExpiration(Long expirationMs) {
        return expiration.getAndSet(expirationMs) != expirationMs;
    }

    public Long getExpiration() {
        return expiration.get();
    }

    public synchronized void foreach(Function<TimerTask, Void> f) {
        TimerTaskEntry entry = root.next;
        // 遍历循环链表
        while (entry != root) {
            TimerTaskEntry nextEntry = entry.next;
            // 任务未被取消
            if (!entry.cancelled()) {
                f.apply(entry.getTimerTask());
            }

            entry = nextEntry;
        }
    }

    public void add(TimerTaskEntry timerTaskEntry) {
        boolean done = false;
        // 循环直到成功加入链表(主要是为了处理并发情况下的重试)
        while (!done) {
            // 先移除任务在链表中旧的引用,防止重复执行
            timerTaskEntry.remove();
            // 锁桶
            synchronized (this) {
                // 锁住任务条目本身,防止多线程操作统一条目
                synchronized (timerTaskEntry) {
                    // 检查任务是否已经绑定到桶
                    if (timerTaskEntry.getList() == null) {
                        // 插入到链表尾部(root.prev 指向尾)
                        TimerTaskEntry tail = root.prev;
                        timerTaskEntry.next = root;
                        timerTaskEntry.prev = tail;
                        // 绑定任务和当前桶
                        timerTaskEntry.setList(this);
                        tail.next = timerTaskEntry;
                        root.prev = timerTaskEntry;

                        // 任务数 + 1
                        taskCounter.incrementAndGet();
                        done = true;
                    }
                }
            }
        }
    }

    public void remove(TimerTaskEntry timerTaskEntry) {
        synchronized (this) {
            synchronized (timerTaskEntry) {
                if (timerTaskEntry.getList() == this) {
                    timerTaskEntry.next.prev = timerTaskEntry.prev;
                    timerTaskEntry.prev.next = timerTaskEntry.next;
                    timerTaskEntry.next = null;
                    timerTaskEntry.prev = null;
                    timerTaskEntry.setList(null);
                    taskCounter.decrementAndGet();
                }
            }
        }
    }

    /**
     * slot【销毁-->重建】
     * 循环链表判断每个节点是否可以执行
     * 如果不能执行,降级到下一层时间轮,重新加入链表,否则提交线程池执行
     */
    public void flush(Function<TimerTaskEntry, Void> f) {
        synchronized (this) {
            TimerTaskEntry head = root.next;
            while (head != root) {
                remove(head);
                f.apply(head);
                head = root.next;
            }
            // 重置桶过期时间
            expiration.set(-1L);
        }
    }

    @Override
    public long getDelay(TimeUnit unit) {
        return unit.convert(Long.max(getExpiration() - Time.getHiresClockMs(), 0), TimeUnit.MILLISECONDS);
    }

    /**
     * 按桶的到期时间排序
     */
    @Override
    public int compareTo(Delayed d) {
        TimerTaskList other;
        if (d instanceof TimerTaskList) {
            other = (TimerTaskList) d;
        } else {
            throw new ClassCastException("can not cast to TimerTaskList");
        }

        if (getExpiration() < other.getExpiration()) {
            return -1;
        } else if (getExpiration() > other.getExpiration()) {
            return 1;
        } else {
            return 0;
        }
    }
}

RocksDBManager(管理 RocksDB 实例)

Java 复制代码
@Component
@Slf4j
public class RocksDBManager {

    private final RocksDB rocksDB;

    public RocksDBManager(@Value("${rocksdb.path}") String rocksPath) throws RocksDBException {
        RocksDB.loadLibrary();
        Options options = new Options();
        options.setCreateIfMissing(true);
        String dbPath = rocksPath;
        log.info("RocksDBManager get dbPath:{}", dbPath);
        if (dbPath == null) {
            dbPath = "rocksdb";
        }
        File dbFile = new File(dbPath);
        if (!dbFile.exists()) {
            if (!dbFile.mkdirs()) {
                throw new RuntimeException("Failed to create rocksdb.path directory: " + dbPath);
            }
        }
        rocksDB = RocksDB.open(options, dbPath);
    }

    public RocksDB getRocksDB() {
        return rocksDB;
    }

    public void close() {
        try {
            rocksDB.close();
            log.info("rocksDB closed.");
        } catch (Exception e) {
            log.error("close RocksDB error", e);
        }
    }
}

Timer

Java 复制代码
public interface Timer {

    void add(TimerTask timerTask);
    
    boolean advanceClock(long timeoutMs);

    int size();

    void shutdown();
}

Time

Java 复制代码
public class Time {

    public static Long getHiresClockMs() {
        return TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
    }
}
相关推荐
a***13142 小时前
python的sql解析库-sqlparse
android·前端·后端
s***55812 小时前
Skywalking介绍,Skywalking 9.4 安装,SpringBoot集成Skywalking
spring boot·后端·skywalking
IT_陈寒3 小时前
Redis深度优化:10个让你的QPS提升50%的关键配置解析
前端·人工智能·后端
武子康3 小时前
大数据-157 Apache Kylin 全面指南:MOLAP 架构、Hive/Kafka 实战与实时 OLAP 落地
大数据·后端·apache kylin
ssshooter3 小时前
传参优于外部变量
前端·后端·面试
qq_22589174663 小时前
基于Python+Django餐饮评论大数据分析与智能推荐系统 毕业论文
开发语言·后端·python·信息可视化·数据分析·django
bcbnb4 小时前
网络调试与API测试必修课 Fiddler抓包工具使用教程、代理配置与HTTPS抓包技巧全解析
后端
华仔啊4 小时前
解决 XXL-Job 定时任务时间偏差8小时的问题
后端
南山安4 小时前
让 LLM 与外界对话:使用 Function Calling 实现天气查询工具
人工智能·后端·python