导读:本期聚焦于小伙伴创作的《如何利用 Netty HashedWheelTimer 算法实现百万级并发连接的心跳检测优化》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《如何利用 Netty HashedWheelTimer 算法实现百万级并发连接的心跳检测优化》有用,将其分享出去将是对创作者最好的鼓励。

在百万级并发连接的网络服务中,心跳检测是维持连接可用性、及时清理无效连接的核心机制。如果采用传统的定时任务调度方案,每一条连接都需要创建一个独立的定时任务,会占用大量线程和内存资源,很容易导致系统性能下降甚至崩溃。Netty内置的HashedWheelTimer基于时间轮算法实现,能够以极低的资源消耗高效管理海量定时任务,非常适合用于此类场景的心跳检测优化。

如何利用 Netty HashedWheelTimer 算法实现百万级并发连接的心跳检测优化

HashedWheelTimer 核心原理

HashedWheelTimer的核心是时间轮结构,时间轮可以理解为一个环形数组,每个数组元素(槽位)对应一个时间间隔,每个槽位中存放的是到期时间在该槽位对应的时间范围内的定时任务。时间轮会以固定的频率转动,每次转动到对应的槽位时,就执行该槽位中的所有到期任务。

时间轮的转动由一个工作线程驱动,所有定时任务的添加、执行都在该线程中完成,避免了多线程竞争带来的开销。同时时间轮采用哈希的方式将任务分配到不同的槽位,任务添加和取消的时间复杂度都是O(1),非常适合高并发场景。

时间轮的核心参数

  • tickDuration:时间轮每个槽位的时间间隔,也就是时间轮转动一次的时间
  • ticksPerWheel:时间轮的槽位数量,决定了时间轮的精度和可容纳的最大时间范围
  • leakDetection:是否开启内存泄漏检测,生产环境建议开启

传统心跳检测方案的痛点

在百万级连接场景下,常见的传统心跳检测方案有两种:

  1. 为每个连接创建一个定时任务,使用JDK自带的ScheduledExecutorService调度,这种方式会创建大量定时任务对象,线程池也会创建大量线程处理任务,内存和CPU开销极大。
  2. 使用一个定时任务扫描所有连接的心跳时间,这种方式在连接数量很大时,每次扫描都会遍历所有连接,耗时很长,而且如果有连接心跳到期,处理不及时会影响整体性能。

而HashedWheelTimer只需要一个工作线程,就可以管理所有连接的心跳定时任务,所有任务的添加和取消都是O(1)复杂度,不会随着连接数增加而带来明显的性能下降。

基于 HashedWheelTimer 实现心跳检测的步骤

1. 创建 HashedWheelTimer 实例

首先需要根据业务场景初始化HashedWheelTimer,合理设置槽位数量和每个槽位的时间间隔,示例代码如下:

import io.netty.util.HashedWheelTimer;
import io.netty.util.Timer;
import java.util.concurrent.TimeUnit;

public class HeartbeatTimerManager {
    // 时间轮每个槽位100毫秒,总共512个槽位,最大可表示的时间范围是512*100ms=51.2秒
    // 如果心跳间隔超过这个时间范围,时间轮会自动扩展,将任务放到后续的轮次中
    private static final Timer HEARTBEAT_TIMER = new HashedWheelTimer(
        r -> {
            Thread t = new Thread(r, "heartbeat-timer-thread");
            t.setDaemon(true);
            return t;
        },
        100,
        TimeUnit.MILLISECONDS,
        512
    );

    public static Timer getHeartbeatTimer() {
        return HEARTBEAT_TIMER;
    }
}

2. 连接建立时添加心跳定时任务

当客户端和服务器建立连接后,为该连接添加一个心跳检测定时任务,任务的逻辑是检查连接上一次心跳的时间,如果超过阈值就关闭连接。示例代码如下:

import io.netty.channel.Channel;
import io.netty.util.Timer;
import io.netty.util.TimerTask;
import java.util.concurrent.TimeUnit;

public class HeartbeatHandler {
    // 心跳间隔是10秒,超过15秒没有收到心跳就关闭连接
    private static final long HEARTBEAT_INTERVAL = 10;
    private static final long HEARTBEAT_TIMEOUT = 15;

    public static void addHeartbeatTask(Channel channel) {
        Timer timer = HeartbeatTimerManager.getHeartbeatTimer();
        // 初始延迟10秒后执行第一次心跳检查,之后每隔10秒执行一次
        timer.newTimeout(new HeartbeatTimeoutTask(channel), HEARTBEAT_INTERVAL, TimeUnit.SECONDS);
    }

    static class HeartbeatTimeoutTask implements TimerTask {
        private final Channel channel;

        public HeartbeatTimeoutTask(Channel channel) {
            this.channel = channel;
        }

        @Override
        public void run(Timer timer) throws Exception {
            // 获取连接上一次收到心跳的时间,这里需要根据实际存储方式获取,比如存在Channel的属性中
            Long lastHeartbeatTime = channel.attr(ConnectionAttr.LAST_HEARTBEAT_TIME).get();
            long currentTime = System.currentTimeMillis();
            // 如果超过15秒没有收到心跳,关闭连接
            if (lastHeartbeatTime == null || currentTime - lastHeartbeatTime > HEARTBEAT_TIMEOUT * 1000) {
                channel.close();
                return;
            }
            // 如果连接还存活,重新添加一个10秒后的心跳检查任务
            timer.newTimeout(this, HEARTBEAT_INTERVAL, TimeUnit.SECONDS);
        }
    }
}

3. 收到心跳时更新时间戳

当服务器收到客户端的心跳消息时,更新该连接的上一次心跳时间,示例代码如下:

import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;

public class ClientHeartbeatHandler extends SimpleChannelInboundHandler<HeartbeatMessage> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, HeartbeatMessage msg) throws Exception {
        Channel channel = ctx.channel();
        // 更新上一次心跳时间为当前时间
        channel.attr(ConnectionAttr.LAST_HEARTBEAT_TIME).set(System.currentTimeMillis());
        // 返回心跳响应
        ctx.writeAndFlush(new HeartbeatResponseMessage());
    }
}

4. 连接关闭时取消定时任务

HashedWheelTimer的定时任务返回Timeout对象,我们可以通过该对象取消未执行的任务,避免连接关闭后任务仍然执行。修改添加任务的代码如下:

import io.netty.channel.Channel;
import io.netty.util.Timer;
import io.netty.util.Timeout;

public class HeartbeatHandler {
    private static final long HEARTBEAT_INTERVAL = 10;
    private static final long HEARTBEAT_TIMEOUT = 15;

    public static void addHeartbeatTask(Channel channel) {
        Timer timer = HeartbeatTimerManager.getHeartbeatTimer();
        HeartbeatTimeoutTask task = new HeartbeatTimeoutTask(channel);
        // 保存Timeout对象到Channel的属性中
        Timeout timeout = timer.newTimeout(task, HEARTBEAT_INTERVAL, TimeUnit.SECONDS);
        channel.attr(ConnectionAttr.HEARTBEAT_TIMEOUT).set(timeout);
    }

    public static void cancelHeartbeatTask(Channel channel) {
        Timeout timeout = channel.attr(ConnectionAttr.HEARTBEAT_TIMEOUT).get();
        if (timeout != null) {
            timeout.cancel();
        }
    }

    static class HeartbeatTimeoutTask implements TimerTask {
        private final Channel channel;

        public HeartbeatTimeoutTask(Channel channel) {
            this.channel = channel;
        }

        @Override
        public void run(Timer timer) throws Exception {
            Long lastHeartbeatTime = channel.attr(ConnectionAttr.LAST_HEARTBEAT_TIME).get();
            long currentTime = System.currentTimeMillis();
            if (lastHeartbeatTime == null || currentTime - lastHeartbeatTime > HEARTBEAT_TIMEOUT * 1000) {
                channel.close();
                return;
            }
            // 重新添加任务时更新保存的Timeout对象
            Timeout newTimeout = timer.newTimeout(this, HEARTBEAT_INTERVAL, TimeUnit.SECONDS);
            channel.attr(ConnectionAttr.HEARTBEAT_TIMEOUT).set(newTimeout);
        }
    }
}

参数调优建议

要让HashedWheelTimer在百万级连接场景下发挥最佳性能,需要根据业务场景调整参数:

参数调优建议
tickDuration建议设置为心跳间隔的1/10到1/5,比如心跳间隔10秒,可以设置为100-2000毫秒,太小会增加时间轮转动的频率,太大任务执行精度会下降
ticksPerWheel建议设置为时间轮最大可表示时间范围 / tickDuration,最大可表示时间范围建议大于等于心跳超时时间,避免任务被多次哈希到不同轮次

同时需要注意,HashedWheelTimer的工作线程是单线程,所以定时任务中的逻辑不能执行耗时操作,否则会阻塞时间轮的转动,影响其他任务的执行。如果心跳检查逻辑比较复杂,可以提交到业务线程池执行,工作线程只负责判断是否需要执行检查逻辑。

注意事项

  • HashedWheelTimer的定时任务执行是在工作线程中的,不要在任务中执行阻塞操作,否则会影响整个时间轮的调度。
  • 如果心跳间隔超过时间轮的最大可表示时间范围,时间轮会自动将任务放到后续的轮次中,不需要额外处理,但是要注意任务的超时时间计算要准确。
  • 生产环境中建议开启Netty的内存泄漏检测,避免因为任务未正确取消导致的内存泄漏问题。
  • 连接关闭时一定要取消对应的定时任务,避免无效任务继续执行占用资源。

NettyHashedWheelTimer心跳检测百万级并发连接时间轮算法修改时间:2026-06-20 02:18:48

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。