原文首发链接:Swoole 实践篇之结合 WebSocket 实现心跳检测机制

大家好,我是码农先森。

引言

前段时间在 Swoole 的交流群里,有群友提问:“如何判断用户端是否在线”。我给予的答案是:“通过在客户端实现心跳包” 来实时记录用户端的心跳数据,最终作为用户是否实时在线的依据。

结合我之前的经验,实现一个简单基于 Swoole 的 WebScoket 服务的心跳检测机制。在用户端会每间隔 5s 上报一次心跳数据,在管理端会每间隔 10 s 获取一次心跳数据,用于实时展示用户的在线状态。

技术实现

heartbeat.html 用户端页面主要是上报用户的心跳包,当用户在线时会每间隔 5s 上报一次数据,如果关闭掉该页面则会断开连接不再上报数据。

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>WebSocket Heartbeat Example</title>
</head>
<body>
<script>
var socket = new WebSocket('ws://127.0.0.1:9502');
var user_id = getQueryVariable("user_id") socket.onopen = function() {
console.log('WebSocket 已连接');
setInterval(function() {
if (socket.readyState === WebSocket.OPEN) {
socket.send(JSON.stringify({type: 'SetHeartbeat', user_id: user_id, user_name: "码农先森"+"(" + user_id + ")", "timestamp": Math.floor(Date.now() / 1000)}));
}
}, 5000); // 每隔5秒发送一次心跳数据
}; socket.onerror = function(error) {
console.error('WebSocket 错误:' + error);
}; socket.onclose = function(event) {
console.log('WebSocket 连接已关闭:' + event.code + ', ' + event.reason);
}; function getQueryVariable(variable)
{
var query = window.location.search.substring(1);
var vars = query.split("&");
for (var i=0;i<vars.length;i++) {
var pair = vars[i].split("=");
if(pair[0] == variable){return pair[1];}
}
return(false);
}
</script>
</body>
</html>

admin.html 管理端页面主要是展示用户的在线状态,每间隔 10s 会获取一次心跳数据包,用于实时显示用户的状态状态。

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>WebSocket Admin Example</title>
<style>
#json-data{
text-align: center;
border: 1px solid blue;
width: 30%;
}
</style>
</head>
<div id="json-data"></div>
<body>
<script>
var socket = new WebSocket('ws://127.0.0.1:9502'); socket.onopen = function() {
console.log('WebSocket 已连接');
setInterval(function() {
if (socket.readyState === WebSocket.OPEN) {
socket.send(JSON.stringify({type: 'GetHeartbeat', user: "admin"}));
}
}, 10 * 1000); // 定时每10s获取一次心跳数据
}; socket.onmessage = function(e) {
const jsonData = JSON.parse(e.data);
const container = document.getElementById('json-data');
while (container.firstChild) {
container.removeChild(container.firstChild);
}
jsonData.forEach(item => {
const div = document.createElement('div');
div.innerHTML = `<p>用户ID: ${item.user_id}, 用户名称: ${item.user_name}, 状态: ${item.status}</p>`;
container.appendChild(div);
});
}; socket.onerror = function(error) {
console.error('WebSocket 错误:' + error);
}; socket.onclose = function(event) {
console.log('WebSocket 连接已关闭:' + event.code + ', ' + event.reason);
};
</script>
</body>
</html>

websocket_server.php 服务主要是用于接收用户端上报的心跳数据,以及推送用户的心跳数据到管理端页面;心跳数据会存储到 Redis 缓存中,便于更新数据,在推送数据时会判断用户是否超过 30s 没有更新心跳数据,如果是则会判定为离线状态。

<?php

Swoole\Runtime::enableCoroutine($flags = SWOOLE_HOOK_ALL);

// 创建 WebSocket 服务
$server = new Swoole\WebSocket\Server("0.0.0.0", 9502); // 监听 WebSocket 连接事件
$server->on('open', function (Swoole\WebSocket\Server $server, $request) {
echo "新的客户端连接: {$request->fd}\n";
}); // 监听 WebSocket 消息事件
$server->on('message', function (Swoole\WebSocket\Server $server, $frame) {
$data = json_decode($frame->data, true);
if ($data["type"] == "SetHeartbeat") {
echo "接收到了用户[{$data["user_name"]}]的心跳包\n";
setHeartbeatCache($data);
} if ($data["type"] == "GetHeartbeat") {
$data = getHeartbeatCache();
$results = [];
foreach($data as $val) {
$val = json_decode($val, true);
$resutl["user_id"] = $val["user_id"];
$resutl["user_name"] = $val["user_name"];
$resutl["status"] = "在线";
// 超过 30 秒没有心跳包, 则离线
if (time() - $val["timestamp"] > 30) {
$resutl["status"] = "离线";
}
$results[] = $resutl;
}
$server->push($frame->fd, json_encode($results));
}
}); // 监听 WebSocket 关闭事件
$server->on('close', function ($ser, $fd) {
echo "客户端 {$fd} 关闭连接\n";
}); // 启 WebSocket 服务
$server->start(); // 设置缓存
function setHeartbeatCache($data) {
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$redis->hSet('heartbeat', $data["user_id"], json_encode($data));
$redis->close();
} // 获取缓存
function getHeartbeatCache() {
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$data = $redis->hGetAll('heartbeat');
$redis->close();
return $data;
}

总结

这里实现的心跳检测机制是一个基础版的,心跳包的主要作用是用于检测用户端是否存活,有助于我们及时判断用户端是否存在断线的问题。在我之前开发过的项目中,有一个基于物联网在线直播抓娃娃的项目,其中就有需要实时监控设备在线状态的需求,该需求就是使用心跳包来实现的。实际上心跳检测技术,应用更广泛的是实时通信、或设备管理的场景偏多。

Swoole 实践篇之结合 WebSocket 实现心跳检测机制的更多相关文章

  1. netty心跳检测机制

    既然是网络通信那么心跳检测肯定是离不开的,netty心跳检测分为读.写.全局 bootstrap.childHandler(new ChannelInitializer<SocketChanne ...

  2. 面试官:Netty心跳检测机制是什么,怎么自定义检测间隔时间?

    哈喽!大家好,我是小奇,一位热爱分享的程序员 小奇打算以轻松幽默的对话方式来分享一些技术,如果你觉得通过小奇的文章学到了东西,那就给小奇一个赞吧 文章持续更新 一.前言 书接上回,昨天在地里干了一天的 ...

  3. javascript websocket 心跳检测机制介绍

    ====测试代码: ==index.html <!DOCTYPE html> <html lang="en"> <head> <meta ...

  4. Netty 中的心跳检测机制

    心跳检测一般存在于建立长连接 或者 需要保活的场景. 心跳的使用场景 长连接的应用场景非常的广泛,比如监控系统,IM系统,即时报价系统,推送服务等等.像这些场景都是比较注重实时性,如果每次发送数据都要 ...

  5. 分析dubbo心跳检测机制

    目的: 维持provider和consumer之间的长连接 实现: dubbo心跳时间heartbeat默认是60s,超过heartbeat时间没有收到消息,就发送心跳消息(provider,cons ...

  6. Netty — 心跳检测和断线重连

    一.前言 由于在通信层的网络连接的不可靠性,比如:网络闪断,网络抖动等,经常会出现连接断开.这样对于使用长连接的应用而言,当突然高流量冲击势必会造成进行网络连接,从而产生网络堵塞,应用响应速度下降,延 ...

  7. [Swoole系列入门教程 3] 心跳检测

    一.Swoole 的4大知识点: 1.TCP/UDP服务器 2.微服务 3.协程 二.同步与异步: 同步买奶茶:小明点单交钱,然后等着拿奶茶: 异步买奶茶:小明点单交钱,店员给小明一个小票,等小明奶茶 ...

  8. Swoole 心跳检测

    Swoole的心跳检测特别简单,只需要配置 heartbeat_check_interval,heartbeat_idle_time就可以了. heartbeat_check_interval:表示服 ...

  9. WebSocket心跳检测和重连机制

    1. 心跳重连原由 心跳和重连的目的用一句话概括就是客户端和服务端保证彼此还活着,避免丢包发生. websocket连接断开有以下两证情况: 前端断开 在使用websocket过程中,可能会出现网络断 ...

  10. 161114、websocket实现心跳重连

    心跳重连缘由 在使用websocket过程中,可能会出现网络断开的情况,比如信号不好,或者网络临时性关闭,这时候websocket的连接已经断开, 而浏览器不会执行websocket 的 onclos ...

随机推荐

  1. Python读写json文件--json

    import json # 将数据写入json文件 def json_write_file(): data={'name':'张三','age':12} with open('json.json',' ...

  2. Lattice高速下载器HW-USBN-2B 如何申请 license

    如果用的芯片不是停产老旧芯片,Diamond programmer 是不需要 license 绑定支持的. 但是有些需要编程老旧的芯片.需要安装 Diamond programmer stand-al ...

  3. 禅道统计BUG解决时长过滤节假日和跨天问题

    之前发过禅道的各种数据统计报表,使用过程中优化了一些,反映最多的是项目bug的解决时长统计问题: 1.比如当天下班左右提交的bug,研发第二天来解决,晚上这段时间应该去掉,不应计算在内 2.节假日.周 ...

  4. Spring JDBCTemplet通用RowMapper帮助类

    1 import java.lang.reflect.Method; 2 import java.math.BigDecimal; 3 import java.math.BigInteger; 4 i ...

  5. Node 中的 Process 理解,有哪些常用方法?

    一.是什么 process 对象是一个全局变量,提供了有关当前 Node.js进程的信息并对其进行控制,作为一个全局变量 我们都知道,进程计算机系统进行资源分配和调度的基本单位,是操作系统结构的基础, ...

  6. 第四課-Channel Study File Reader & File Writer

    示例描述:从数据库中读取数据并过滤转换为HL7并存放到指定目录;然后读取目录中的HL7文件转换为txt文本并存放到指定目录. 首先在F:\MirthConnect\Test目录下创建Out目录存放输出 ...

  7. 第 10 章 使用pyecharts 进行数据展示

    第 10 章 使用pyecharts 进行数据展示 10.1 安装 pyecharts pyecharts 是一个用于生成 Echarts 图表的类库, Echarts 是百度开源的一个数据可视化JS ...

  8. 第五章:SQL高级处理

    第五章:SQL高级处理 5.1 窗口函数 5.1.1 窗口函数概念及基本的使用方法 窗口函数也称为OLAP函数.OLAP 是 OnLine AnalyticalProcessing 的简称,意思是对数 ...

  9. 模拟IDC spark读写MaxCompute实践

    简介: 现有湖仓一体架构是以 MaxCompute 为中心读写 Hadoop 集群数据,有些线下 IDC 场景,客户不愿意对公网暴露集群内部信息,需要从 Hadoop 集群发起访问云上的数据.本文以 ...

  10. Quick BI产品核心功能大图(五)移动端:让数据在更多业务场景中流通

    ​简介:将数据更好的融入日常工作中,一个重要的前提条件就是多端多渠道的数据触达和办公协同能力. Quick BI凭借移动端交互体验,帮助用户随时随地便捷查看报表,并通过在线协同方式,追踪策略的执行落地 ...