什么是心跳
心跳(Heartbeat) 是在计算机网络、分布式系统和嵌入式系统中常见的一种机制,用于检测系统或组件的可用性、存活状态以及维持连接。
1. 心跳的作用
✅ 检测存活状态
- 确保服务器、客户端、微服务或设备仍然在线。
- 适用于 分布式系统、集群、高可用(HA)系统。
✅ 网络连接保活
- 防止 TCP 连接超时、NAT 断连。
- 常见于 WebSocket、MQTT、数据库连接池。
✅ 故障检测与恢复
- 当某个节点不响应心跳,则触发 自动故障转移(Failover)。
- 适用于 主从复制(如 Redis Sentinel、MySQL Replication)。
2. 心跳的工作原理
(1) 定期发送心跳包
- 主动方(Client 或 Master) 定期发送“心跳”数据包。
- 被动方(Server 或 Slave) 接收并返回确认包。
(2) 监测心跳超时
- 设定 心跳超时时间(Timeout),如果在规定时间内没有响应,则认为对方掉线。
(3) 触发故障处理
- 重新连接、切换主节点、报警通知等。
3. 心跳的常见应用
🔹 网络协议
- TCP Keepalive:维持长连接,防止 NAT 超时。
- WebSocket Ping/Pong:检测客户端是否断开。
- MQTT 心跳(Keep Alive):保证 IoT 设备在线。
🔹 分布式 & 高可用
- ZooKeeper:Leader 监测 Follower 存活。
- Redis Sentinel:监测主从服务器状态,故障转移。
- Kubernetes Liveness Probe:检测容器是否存活。
🔹 设备监控
- 物联网(IoT):智能设备定期汇报在线状态。
- 服务器健康检查:Nginx、负载均衡(如 HAProxy)使用心跳检测后端服务器可用性。
4. 心跳机制的实现方式
| 类型 | 方式 | 特点 |
|---|---|---|
| 主动轮询 | 客户端定期请求服务器 | 实现简单,但开销大 |
| 被动监听 | 服务器定期向客户端发送心跳 | 适合长连接,如 WebSocket |
| TCP Keepalive | TCP 内置机制,自动探测断连 | 适用于长连接,减少应用层处理 |
| 定时心跳包 | 业务层实现,UDP/TCP 发送心跳 | 灵活,适用于分布式系统 |
5. 设计心跳时的注意点
✅ 心跳间隔(Interval)
- 过短:网络开销大,影响性能。
- 过长:无法及时发现故障。
✅ 超时时间(Timeout)
- 超时 = 3~5 次心跳间隔 比较合适。
- 需要根据网络环境和业务需求调整。
✅ 重试机制(Retry)
- 多次心跳丢失 后才认为对方掉线,避免误判。
✅ 负载优化
- 使用 指数退避算法(Exponential Backoff) 逐步增加心跳间隔,减少无效流量。
💡 总结
- 心跳 = 定期发送信号,检测系统/设备是否在线。
- 用于网络保活、故障检测、负载均衡等场景。
- 合理设置心跳间隔、超时和重试策略,优化系统稳定性。
在 Java 中实现心跳机制的方式有多种,通常使用 Socket(TCP/WebSocket)、Netty、ScheduledExecutorService 等方式实现。下面介绍几种常见的 Java 心跳机制实现方法:
方法 1:使用 TCP Socket 实现心跳(适用于长连接)
客户端 定期向 服务器 发送心跳包,服务器如果在超时时间内没有收到心跳,就认为客户端掉线。
🔹 服务器端(Server)
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;public class HeartbeatServer {public static void main(String[] args) throws IOException {ServerSocket serverSocket = new ServerSocket(8080);System.out.println("服务器启动,等待客户端连接...");while (true) {Socket socket = serverSocket.accept();System.out.println("客户端连接成功:" + socket.getInetAddress());new Thread(() -> {try {BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));while (true) {String message = reader.readLine();if (message == null) break;System.out.println("收到心跳: " + message);}} catch (IOException e) {System.out.println("客户端断开连接!");}}).start();}}
}
🔹 客户端(Client)
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;public class HeartbeatClient {public static void main(String[] args) {try (Socket socket = new Socket("127.0.0.1", 8080);PrintWriter writer = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()), true)) {while (true) {writer.println("HEARTBEAT");System.out.println("发送心跳包...");Thread.sleep(5000); // 每 5 秒发送一次}} catch (Exception e) {System.out.println("服务器断开连接!");}}
}
📝 说明:
- 服务器监听端口 8080,接收 心跳消息。
- 客户端每 5 秒 发送一次
"HEARTBEAT"。 - 服务器如果 超时未收到 客户端的心跳,就可以认为 客户端掉线。
方法 2:使用 ScheduledExecutorService 定时发送心跳
如果你使用的是 Java NIO / Netty / WebSocket,可以使用 ScheduledExecutorService 定时发送心跳。
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;public class HeartbeatScheduler {public static void main(String[] args) {ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);Runnable heartbeatTask = () -> {System.out.println("发送心跳包...");// 这里可以调用 TCP/WebSocket 发送心跳消息};// 每 5 秒执行一次心跳scheduler.scheduleAtFixedRate(heartbeatTask, 0, 5, TimeUnit.SECONDS);}
}
📝 说明:
- 适用于 多线程环境,避免
Thread.sleep()阻塞线程。 scheduleAtFixedRate()定时执行心跳。
方法 3:使用 Netty 实现心跳(适用于高并发场景)
Netty 是高性能的异步网络通信框架,适用于 WebSocket、TCP 长连接 场景。
🔹 服务器(Server)
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;public class NettyHeartbeatServer {public static void main(String[] args) throws InterruptedException {EventLoopGroup bossGroup = new NioEventLoopGroup();EventLoopGroup workerGroup = new NioEventLoopGroup();try {ServerBootstrap bootstrap = new ServerBootstrap();bootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) {ch.pipeline().addLast(new IdleStateHandler(10, 0, 0)); // 10秒没收到消息触发事件ch.pipeline().addLast(new SimpleChannelInboundHandler<String>() {@Overridepublic void channelRead0(ChannelHandlerContext ctx, String msg) {System.out.println("收到心跳:" + msg);}@Overridepublic void userEventTriggered(ChannelHandlerContext ctx, Object evt) {if (evt instanceof IdleStateEvent) {IdleStateEvent event = (IdleStateEvent) evt;if (event.state() == IdleState.READER_IDLE) {System.out.println("客户端超时,关闭连接!");ctx.close();}}}});}});ChannelFuture future = bootstrap.bind(8080).sync();System.out.println("Netty 服务器启动...");future.channel().closeFuture().sync();} finally {bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}}
}
🔹 客户端(Client)
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.timeout.IdleStateHandler;
import java.util.concurrent.TimeUnit;public class NettyHeartbeatClient {public static void main(String[] args) throws InterruptedException {EventLoopGroup group = new NioEventLoopGroup();try {Bootstrap bootstrap = new Bootstrap();bootstrap.group(group).channel(NioSocketChannel.class).handler(new ChannelInitializer<NioSocketChannel>() {@Overrideprotected void initChannel(NioSocketChannel ch) {ch.pipeline().addLast(new IdleStateHandler(0, 5, 0, TimeUnit.SECONDS)); // 5秒发送心跳ch.pipeline().addLast(new SimpleChannelInboundHandler<String>() {@Overridepublic void channelRead0(ChannelHandlerContext ctx, String msg) {System.out.println("服务器响应:" + msg);}@Overridepublic void userEventTriggered(ChannelHandlerContext ctx, Object evt) {if (evt instanceof IdleStateEvent) {System.out.println("发送心跳包...");ctx.writeAndFlush("HEARTBEAT");}}});}});ChannelFuture future = bootstrap.connect("127.0.0.1", 8080).sync();future.channel().closeFuture().sync();} finally {group.shutdownGracefully();}}
}
📝 说明:
- Netty 服务器:使用
IdleStateHandler检测 10 秒 无消息自动关闭连接。 - Netty 客户端:使用
IdleStateHandler5 秒 发送一次心跳。 - 适用于高并发系统,如 IM、WebSocket、分布式服务。
总结
| 方法 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| TCP Socket | 普通长连接 | 实现简单 | 适用于小型应用 |
| ScheduledExecutorService | 多线程任务 | 线程管理灵活 | 需手动实现超时检测 |
| Netty | 高并发服务器 | 高性能,自动检测超时 | 学习成本较高 |
如果你是初学者,建议 使用 TCP Socket;如果需要高并发,推荐 Netty! 🚀
相关文章:
什么是心跳
心跳(Heartbeat) 是在计算机网络、分布式系统和嵌入式系统中常见的一种机制,用于检测系统或组件的可用性、存活状态以及维持连接。 1. 心跳的作用 ✅ 检测存活状态 确保服务器、客户端、微服务或设备仍然在线。适用于 分布式系统、集群、高…...
有限元分析学习——Anasys Workbanch第一阶段笔记梳理
第一阶段笔记主要源自于哔哩哔哩《ANSYS-workbench 有限元分析应用基础教程》 张晔 主要内容导图: 笔记导航如下: Anasys Workbanch第一阶段笔记(1)基本信息与结果解读_有限元分析变形比例-CSDN博客 Anasys Workbanch第一阶段笔记(2)网格单元与应力奇…...
目标跟踪之sort算法(3)
这里写目录标题 1 流程1 预处理2 跟踪 2 代码 参考:sort代码 https://github.com/abewley/sort 1 流程 1 预处理 1.1 获取离线检测数据。1.2 实例化跟踪器。2 跟踪 2.1 轨迹处理。根据上一帧的轨迹预测当前帧的轨迹,剔除到当前轨迹中为空的轨迹得到当前…...
uniapp 地图添加,删除,编辑标记,在地图中根据屏幕范围中呈现标记
前言 小程序实现新功能,在地图中选取位置添加标记,并在地图中呈现添加的标记,(呈现的是根据当前屏幕范围内的标记),并对标记进行分享,删除,编辑,导航,并从分…...
第 5 章:声音与音乐系统
5.1 声音效果的应用 在游戏中,声音效果是增强游戏沉浸感和趣味性的重要元素。Pygame 提供了强大的音频处理功能,使得添加各种声音效果变得相对简单。声音效果可以包括角色的动作音效,如跳跃、攻击、受伤时的声音;环境音效&#x…...
SOME/IP服务接口
本系列文章将分享我在学习 SOME/IP 过程中积累的一些感悟,并结合 SOME/IP 的理论知识进行讲解。主要内容是对相关知识的梳理,并结合实际代码展示 SOME/IP 的使用,旨在自我复习并与大家交流。文中引用了一些例图,但由于未能找到原作…...
NeetCode刷题第17天(2025.1.27)
文章目录 086 Course Schedule II 课程安排二087 Graph Valid Tree 图有效树088 Number of Connected Components in an Undirected Graph 无向图中的连接组件数量 086 Course Schedule II 课程安排二 您将获得一个数组 prerequisites ,其中 prerequisites[i] [a,…...
c++学习第十四天
提示:以下是本篇文章正文内容,下面案例可供参考。 //力扣代码 class Solution {const char* numStrArr[10]{"","","abc","def","ghi","jkl","mno","pqrs","tuv&q…...
遗传算法【Genetic Algorithm(GA)】求解函数最大值(MATLAB and Python实现)
一、遗传算法基础知识 来自B站视频的笔记: 【超容易理解】手把手逐句带你解读并实现遗传算法的MATLAB编程(结合理论基础)_哔哩哔哩_bilibili 1、遗传算法 使用“适者生存”的原则,在遗传算法的每一代中,…...
MySQL 存储函数:数据库的自定义函数
在数据库开发中,存储函数(Stored Function)是一种非常有用的工具。它允许我们创建自定义的函数,这些函数可以在 SQL 查询中像内置函数一样使用,用于实现特定的逻辑和计算。本文将深入探讨 MySQL 存储函数的概念、与存储…...
【Rust自学】15.6. RefCell与内部可变性:“摆脱”安全性限制
题外话,这篇文章一共4050字,是截止到目前为止最长的文章,如果你能坚持读完并理解,那真的很强! 喜欢的话别忘了点赞、收藏加关注哦(加关注即可阅读全文),对接下来的教程有兴趣的可以…...
Luzmo 专为SaaS公司设计的嵌入式数据分析平台
Luzmo 是一款嵌入式数据分析平台,专为 SaaS 公司设计,旨在通过直观的可视化和快速开发流程简化数据驱动决策。以下是关于 Luzmo 的详细介绍: 1. 背景与定位 Luzmo 前身为 Cumul.io ,专注于为 SaaS 公司提供嵌入式分析解决方案。…...
数组at()方法:负索引的救赎与JavaScript标准化之路
数组at()方法:负索引的救赎与JavaScript标准化之路 从一次代码评审说起 在某次团队代码评审中,小白注意到有同事写下了这样的代码: const lastItem arr[arr.length - 1];这让我回想起自己早期开发时被负索引问题困扰的经历。今天…...
HTML<label>标签
例子 三个带标签的单选按钮: <form action"/action_page.php"> <input type"radio" id"html" name"fav_language" value"HTML"> <label for"html">HTML</label><br&…...
约瑟夫问题(信息学奥赛一本通-2037)
【题目描述】 N个人围成一圈,从第一个人开始报数,数到M的人出圈;再由下一个人开始报数,数到M 的人出圈;…输出依次出圈的人的编号。 【输入】 输入N和M。 【输出】 输出一行,依次出圈的人的编号。 【输入样…...
二分查找题目:寻找两个正序数组的中位数
文章目录 题目标题和出处难度题目描述要求示例数据范围 解法一思路和算法代码复杂度分析 解法二思路和算法代码复杂度分析 题目 标题和出处 标题:寻找两个正序数组的中位数 出处:4. 寻找两个正序数组的中位数 难度 8 级 题目描述 要求 给定两个大…...
【技术洞察】2024科技绘卷:浪潮、突破、未来
涌动与突破 2024年,科技的浪潮汹涌澎湃,人工智能、量子计算、脑机接口等前沿技术如同璀璨星辰,方便了大家的日常生活,也照亮了人类未来的道路。这一年,科技的突破与创新不断刷新着人们对未来的想象。那么回顾2024年的科…...
【Linux】gdb——Linux调试器
gdb使用背景 程序的发布方式有两种,debug模式和release模式 Linux gcc/g出来的二进制程序,默认是release模式 要使用gdb调试,必须在源代码生成二进制程序的时候, 加上 -g 选项 gdb使用方法 首先进入gdb gdb test_glist显示代码 断点 b 行…...
fpga系列 HDL:XILINX Vivado Vitis 高层次综合(HLS) 实现 EBAZ板LED控制(上)
目录 创建工程创建源文件并编写C代码C仿真综合仿真导出RTL CG导出RTL错误处理: 创建工程 创建源文件并编写C代码 创建源文件(Souces下的hlsv.h和hlsv.cpp,Test Bench下的test_hlsv1.cpp): hlsv1.h #ifndef HLSV1 #define HLSV1 #include &l…...
卡特兰数学习
1,概念 卡特兰数(英语:Catalan number),又称卡塔兰数,明安图数。是组合数学中一种常出现于各种计数问题中的数列。它在不同的计数问题中频繁出现。 2,公式 卡特兰数的递推公式为:f(…...
度小满Java开发面试题及参考答案 (上)
String 是基本类型吗?String、StringBuffer、StringBuilder 的区别是什么?拼接字符串有哪些做法? String 不是基本类型,它是 Java 中的一个类,属于引用类型。 下面来看看 String、StringBuffer、StringBuilder 的区别: 类型可变性线程安全性性能适用场景String不可变线程…...
Python-基于PyQt5,json和playsound的通用闹钟
前言:刚刚结束2024年秋季学期的学习,接下来我们继续来学习PyQt5。由于之前我们已经学习了PyQt5以及PyUIC,Pyrcc和QtDesigner的安装,配置。所以接下来我们一起深入PyQt5,学习如何利用PyQt5进行实际开发-基于PyQt5,json和…...
关于数字地DGND和模拟地AGND隔离
文章目录 前言一、1、为什么要进行数字地和模拟地隔离二、隔离元件1.①0Ω电阻:2.②磁珠:3.电容:4.④电感: 三、隔离方法①单点接地②数字地与模拟地分开布线,最后再PCB板上一点接到电源。③电源隔离④、其他隔离方法 …...
小识Java死锁是否会造成CPU100%?
死锁或者大量的死锁不一定会直接导致CPU占用率达到100%。以下是详细分析: 一、死锁对CPU的影响 资源占用:死锁是指两个或多个线程(或进程)在相互等待对方释放资源,导致所有涉及的线程都无法继续执行。在死锁状态下&a…...
DeepSeek R1学习
0.回顾: https://blog.csdn.net/Together_CZ/article/details/144431432?ops_request_misc%257B%2522request%255Fid%2522%253A%25226574a586f0850d0329fbb720e5b8d5a9%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id…...
激光线扫相机无2D图像的标定方案
方案一:基于运动控制平台的标定 适用场景:若激光线扫相机安装在可控运动平台(如机械臂、平移台、旋转台)上,且平台的运动精度已知(例如通过编码器或高精度步进电机控制)。 步骤: 标…...
12 款开源OCR发 PDF 识别框架
2024 年 12 款开源文档解析框架的选型对比评测:PDF解析、OCR识别功能解读、应用场景分析及优缺点比较 这是该系列的第二篇文章,聚焦于智能文档处理(特别是 PDF 解析)。无论是在模型预训练的数据收集阶段,还是基于 RAG…...
【反悔堆】【hard】力扣871. 最低加油次数
汽车从起点出发驶向目的地,该目的地位于出发位置东面 target 英里处。 沿途有加油站,用数组 stations 表示。其中 stations[i] [positioni, fueli] 表示第 i 个加油站位于出发位置东面 positioni 英里处,并且有 fueli 升汽油。 假设汽车油…...
为什么应用程序是特定于操作系统的?[计算机原理]
你把WINDOWS程序复制到MAC上使用,会发现无法运行。你可能会说,MAC是arm处理器,而WINDWOS是X86 处理器。但是在2019年,那时候MAC电脑还全是Intel处理器,在同样的X86芯片上,运行MAC和WINDOWS 程序还是无法互相…...
多项日常使用测试,带你了解如何选择AI工具 Deepseek VS ChatGpt VS Claude
多项日常使用测试,带你了解如何选择AI工具 Deepseek VS ChatGpt VS Claude 注:因为考虑到绝大部分人的使用,我这里所用的模型均为免费模型。官方可访问的。ChatGPT这里用的是4o Ai对话,编程一直以来都是人们所讨论的话题。Ai的出现…...
