原文首发链接: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. Openstack之工作流程

    组件 OpenStack的核心部件即包括Nova(用于计算).Keystone(用于身份服务).Neutron(用于网络和地址管理).Cinder(块存储).Swift(对象存储).Glance(镜像 ...

  2. 深入解析decltype和decltype(auto)

    decltype关键字是C++11新标准引入的关键字,它和关键字auto的功能类似,也可以自动推导出给定表达式的类型,但它和auto的语法有些不同,auto推导的表达式放在"="的 ...

  3. 手把手教你基于gin从零搭建一个属于你自己的go项目(一)

    一.为什么写这个,适合什么人看 原因 因为自己想写点小玩意,本来是打算用egg.js来写服务端的,后来发现了个更好玩的midway,但是后来发现自己手上的服务器都是一核2g的小水管,用node有点难顶 ...

  4. CentOS 5.5编译安装vsftpd-2.3.4配置虚拟用户

    CentOS 5.5编译安装vsftpd-2.3.4配置虚拟用户 2012-02-22 17:18:15|  分类: rhel_vsftp|举报|字号 订阅 centos 5.5编译安装vsftpd- ...

  5. AI数字人互动大屏:如何改变我们的生活?

    随着科技的飞速进步和人工智能技术的日益成熟,智能制造正在成为推动制造业转型升级的核心力量. 互动数字人具有强大的情感分析能力,可以根据观众的表情.语气等实时反馈,作出恰当而富有情感的回应.这不仅让每一 ...

  6. spring boot 配置文件占位符和多环境配置 [七]

    配置文件占位符 person.last-name=zhangsan person.age=${random.int} person.birth=2017/12/15 person.boss=false ...

  7. mysql 重新整理——性能下降的原因[四]

    前言 什么是性能下降? 其实就是代码运行的环境变了,那么环境变化是什么? 比如cpu上升了,内存满了.有或者表中数量增加了,量变了. 其实这些是dba干的,但是呢,我们也需要去了解下,并且优化我们的c ...

  8. Docker 安装,常用命令

    安装Docker 官方所有操作系统安装教程:Install Docker Engine on CentOS | Docker Documentation,其中CentOS安装docker引擎的代码: ...

  9. 如何在golang代码里面解析容器镜像

    ​简介:容器镜像在我们日常的开发工作中占据着极其重要的位置.通常情况下我们是将应用程序打包到容器镜像并上传到镜像仓库中,在生产环境将其拉取下来.然后用 docker/containerd 等容器运行时 ...

  10. [FE] WebStorm, ESLint: Trailing spaces not allowed

    在 WebStorm 中搜索文件 .eslintrc.js 在里面的 rules 项中追加规则: 'no-trailing-spaces' : ['off', { 'skipBlankLines': ...