Swoole 实践篇之结合 WebSocket 实现心跳检测机制
原文首发链接: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 实现心跳检测机制的更多相关文章
- netty心跳检测机制
既然是网络通信那么心跳检测肯定是离不开的,netty心跳检测分为读.写.全局 bootstrap.childHandler(new ChannelInitializer<SocketChanne ...
- 面试官:Netty心跳检测机制是什么,怎么自定义检测间隔时间?
哈喽!大家好,我是小奇,一位热爱分享的程序员 小奇打算以轻松幽默的对话方式来分享一些技术,如果你觉得通过小奇的文章学到了东西,那就给小奇一个赞吧 文章持续更新 一.前言 书接上回,昨天在地里干了一天的 ...
- javascript websocket 心跳检测机制介绍
====测试代码: ==index.html <!DOCTYPE html> <html lang="en"> <head> <meta ...
- Netty 中的心跳检测机制
心跳检测一般存在于建立长连接 或者 需要保活的场景. 心跳的使用场景 长连接的应用场景非常的广泛,比如监控系统,IM系统,即时报价系统,推送服务等等.像这些场景都是比较注重实时性,如果每次发送数据都要 ...
- 分析dubbo心跳检测机制
目的: 维持provider和consumer之间的长连接 实现: dubbo心跳时间heartbeat默认是60s,超过heartbeat时间没有收到消息,就发送心跳消息(provider,cons ...
- Netty — 心跳检测和断线重连
一.前言 由于在通信层的网络连接的不可靠性,比如:网络闪断,网络抖动等,经常会出现连接断开.这样对于使用长连接的应用而言,当突然高流量冲击势必会造成进行网络连接,从而产生网络堵塞,应用响应速度下降,延 ...
- [Swoole系列入门教程 3] 心跳检测
一.Swoole 的4大知识点: 1.TCP/UDP服务器 2.微服务 3.协程 二.同步与异步: 同步买奶茶:小明点单交钱,然后等着拿奶茶: 异步买奶茶:小明点单交钱,店员给小明一个小票,等小明奶茶 ...
- Swoole 心跳检测
Swoole的心跳检测特别简单,只需要配置 heartbeat_check_interval,heartbeat_idle_time就可以了. heartbeat_check_interval:表示服 ...
- WebSocket心跳检测和重连机制
1. 心跳重连原由 心跳和重连的目的用一句话概括就是客户端和服务端保证彼此还活着,避免丢包发生. websocket连接断开有以下两证情况: 前端断开 在使用websocket过程中,可能会出现网络断 ...
- 161114、websocket实现心跳重连
心跳重连缘由 在使用websocket过程中,可能会出现网络断开的情况,比如信号不好,或者网络临时性关闭,这时候websocket的连接已经断开, 而浏览器不会执行websocket 的 onclos ...
随机推荐
- openGauss内存引擎中的索引
一.索引 索引是一种用于快速查询和检索数据的数据结构.常见的索引结构有: B 树, B+树和 Hash. 索引的作用就相当于目录的作用.打个比方: 我们在查字典的时候,如果没有目录,那我们就只能一页一 ...
- .netcore 使用Quartz定时任务
这是一个使用 .NET Core 和 Quartz.NET 实现定时任务的完整示例.首先确保已经安装了 .NET Core SDK.接下来按照以下步骤创建一个新的控制台应用程序并设置定时任务: 创建一 ...
- 【笔记】go语言--字符与字符串处理
[笔记]go语言--字符与字符串处理 rune相当于go的char 使用range遍历pos,rune对(遍历出来是不连续的) 使用utf8.RuneCountInString获得字符数量 使用len ...
- 如何使用 Serverless Devs 部署静态网站到函数计算
简介:手把手教你:如何使用 Serverless Devs 部署静态网站到函数计算. 前言 公司经常有一些网站需要发布上线,对比了几款不同的产品后,决定使用阿里云的函数计算(FC)来托管构建出来的静 ...
- Apsara Stack 2.0技术百科(9宫格)
原文链接 本文为阿里云原创内容,未经允许不得转载.
- 干掉讨厌的 CPU 限流,让容器跑得更快
简介: 让人讨厌的 CPU 限流影响容器运行,有时人们不得不牺牲容器部署密度来避免 CPU 限流出现.本文介绍的 CPU Burst 技术可以帮助您既能保证容器运行服务质量,又不降低容器部署密度.文 ...
- 京东:Flink SQL 优化实战
简介: 本文着重从 shuffle.join 方式的选择.对象重用.UDF 重用等方面介绍了京东在 Flink SQL 任务方面做的优化措施. 本文作者为京东算法服务部的张颖和段学浩,并由 Apach ...
- [FE] iframe 相关选项 x-frame-options: 设置 meta 标签无效 & helmet
The X-Frame-Options HTTP 响应头是用来给浏览器 指示允许一个页面 可否在 <frame>, <iframe>, <embed> 或者 < ...
- WPF 将控件放入到 UserControl 里获取 HwndSource 为空的情况
本文记录将 WPF 控件放入到 UserControl 里,如果此 UserControl 没有被设置 Visibility 为可见过,那么放在此 UserControl 内的控件将获取不到 Hwnd ...
- c++图像处理程序编译成.so动态链接库供Java调用(包含jni文件与引用第三方动态链接库opencv)
一.linux编译so文件需要准备的环境 1.安装JDK(注意:不能安装openjdk,因为openjdk没有include目录,编译时需要用到include目录的头文件) 2.安装gcc和g++ ...