帮助文档
专业提供香港服务器、香港云服务器、香港高防服务器租用、香港云主机、台湾服务器、美国服务器、美国云服务器vps租用、韩国高防服务器租用、新加坡服务器、日本服务器租用 一站式全球网络解决方案提供商!专业运营维护IDC数据中心,提供高质量的服务器托管,服务器机房租用,服务器机柜租用,IDC机房机柜租用等服务,稳定、安全、高性能的云端计算服务,实时满足您的多样性业务需求。 香港大带宽稳定可靠,高级工程师提供基于服务器硬件、操作系统、网络、应用环境、安全的免费技术支持。
服务器资讯 / 香港服务器租用 / 香港VPS租用 / 香港云服务器 / 美国服务器租用 / 台湾服务器租用 / 日本服务器租用 / 官方公告 / 帮助文档
浅谈PHP结合JavaScript SSE(Server Sent Events)实现服务器实时推送功能
发布时间:2024-03-08 08:48:10   分类:帮助文档
浅谈PHP结合JavaScript SSE(Server Sent Events)实现服务器实时推送功能 说明 如配置后Nginx遇到502/504的,请参考这两篇文章的解决方案 PHP-FPM与Nginx通信报 502 Bad Gateway或504 Gateway Timeout终极解决方案(适用于PHP执行耗时任务情况下的报错) Linux系统下配置Nginx使部分URL使用多套自定义的PHP-FPM配置 简介 SSE 的全称是 Server Sent Events,即服务器推送事件。它是一种基于 HTTP 的服务器到客户端的单向(半双工)通信机制,使服务器能够主动将实时数据推送给客户端,而不需要客户端多次发起请求。 官方文档:https://developer.mozilla.org/en-US/docs/Web/API/EventSource 解决了什么问题 常规的HTTP请求响应流程无法做到服务器主动推送数据到客户端,SSE可以解决此问题。 适用场景 实时更新订阅数据、实时通知、实时日志监控、实时数据统计、简单的文本数据传输。 示例代码 服务端 // 这行代码用于关闭输出缓冲。关闭后,脚本的输出将立即发送到浏览器,而不是等待缓冲区填满或脚本执行完毕。 ini_set('output_buffering', 'off'); // 这行代码禁用了 zlib 压缩。通常情况下,启用 zlib 压缩可以减小发送到浏览器的数据量,但对于服务器发送事件来说,实时性更重要,因此需要禁用压缩。 ini_set('zlib.output_compression', false); // 这行代码使用循环来清空所有当前激活的输出缓冲区。ob_end_flush() 函数会刷新并关闭最内层的输出缓冲区,@ 符号用于抑制可能出现的错误或警告。 while (@ob_end_flush()) {} // 这行代码设置 HTTP 响应的 Content-Type 为 text/event-stream,这是服务器发送事件(SSE)的 MIME 类型。 header('Content-Type: text/event-stream'); // 这行代码设置 HTTP 响应的 Cache-Control 为 no-cache,告诉浏览器不要缓存此响应。 header('Cache-Control: no-cache'); // 这行代码设置 HTTP 响应的 Connection 为 keep-alive,保持长连接,以便服务器可以持续发送事件到客户端。 header('Connection: keep-alive'); // 这行代码设置 HTTP 响应的自定义头部 X-Accel-Buffering 为 no,用于禁用某些代理或 Web 服务器(如 Nginx)的缓冲。这有助于确保服务器发送事件在传输过程中不会受到缓冲影响 header('X-Accel-Buffering: no'); / * @function 封装sse格式的数据 * @param $data string * @return string */ function sse($data) { //data:\n\n不能少,sse固定格式 return "data:{$data}\n\n"; } // 开启输出缓冲 ob_start(); while (true) { $json = json_encode(['data' => ['time' => date('Y-m-d H:i:s')]], JSON_UNESCAPED_UNICODE); echo sse($json); //刷新缓冲区 ob_flush(); //将输出缓冲区的内容立即发送到客户端 flush(); sleep(1); } 客户端 Document 服务端对客户端单向通信是实时了,可服务端数据发生变化时,怎么及时同步到SSE模块呢?依客户端显示通知数量为需求做个简单示例 方案1:纯粹轮询模式 做法:不停对数据库做查询。 优点:实现简单。 缺点:很不优雅的方案,性能消耗大。 场景:数据量不大且赶工时,可作为临时方案。 示例: ob_start(); $user_id = 1; //假设用户id为1,实际可传参获取。 while (true) { $notice_count = DB::table('notice')->where('user_id', $user_id)->count(); echo sse(json_encode(['notice_num' => notice_count])); ob_flush(); flush(); sleep(1); } 方案2:基于事件触发,用消息队列做订阅发布模式 做法:对要实时获取的数据,先赋一个初始值的实际值,传递给客户端,当数据发生变化时,触发生产消息的通知,SSE模块不停的消费消息。 优点:避免了轮询模式疯狂查询。 缺点:仍旧需要消耗一些资源,实现稍微繁琐。 场景:方法优雅,适用于订阅端根据消息做更复杂的业务逻辑操作时使用。 示例: 暂时用redis队列简单实现:技术选型可根据实际情况做高可用或更复杂的设计。 //例如要实现一个通知数量实时变更的功能: //发布端: $redis = new Redis(); $redis->connect('127.0.0.1', 6379); //假设用户id为1 $user_id = 1; //执行完其它的针对notice表写操作的业务逻辑代码...,然后向队列丢一个任务 $redis->lPush('add_one_notice_task:'. $user_id, 1); //------------------------------------------------------------------------------------------------------------------ //订阅端 $redis = new Redis(); $redis->connect('127.0.0.1', 6379); //先查询数据库通知数量 $user_id = 1; //假设用户id为1,实际可传参获取。 $notice_count = DB::table('notice')->where('user_id', $user_id)->count(); $redis->set('user_notice_num:'. $user_id, $notice_count); while (true) { //若检测到有自增一个通知数量的任务,则消费时触发一个增加数量的动作。 $add_one_notice_task = $redis->rPop('add_one_notice_task:'. $user_id); if($add_one_notice_task) { $redis->incr('user_notice_num:'. $user_id); } echo sse(json_encode(['notice_num' => $redis->get('user_notice_num:' . $user_id) ?? 0])); ob_flush(); flush(); sleep(1); } 方案3:基于事件触发的轮询 做法:触发端直接一步到位,修改好数据后缓存,监听端不停的监听缓存的值。 优点:实现起来比订阅发布简单,又避免轮询频繁查库,通过缓存解耦,避免了方案1的性能问题,又能保证缓存一致性。 缺点:终究还是轮询,仍旧需要消耗一些资源。 场景:相对简单的,不需要在监听端做业务处理,只做纯粹返回数据的场景。 示例: //触发端 $redis = new Redis(); $redis->connect('127.0.0.1', 6379); //假设用户id为1,实际可传参获取。 $user_id = 1; //执行完其它的针对notice表写操作的业务逻辑代码... $notice_count = DB::table('notice')->where('user_id', $user_id)->count(); $redis->set('user_notice_num:'. $user_id, $notice_count); //------------------------------------------------------------------------------------------------------------------ //监听端 $redis = new Redis(); $redis->connect('127.0.0.1', 6379); $user_id = 1; //假设用户id为1,实际可传参获取。 while (true) { echo sse(json_encode(['notice_num' => $redis->get('user_notice_num:' . $user_id) ?? 0])); ob_flush(); flush(); sleep(1); } 在实战项目中的封装 / * @function 与客户端server send event通信方式 * @param $callback callable 回调,若返回数组代表要输出json,返回null代表本次循环不进行输出 * @param $millisecond int 数据分发间隔,单位:毫秒 * @return string * @other void */ function sse($callback, $millisecond = 1000) { set_time_limit(0); ini_set('output_buffering', 'off'); ini_set('zlib.output_compression', false); while (@ob_end_flush()) {} header('Content-Type: text/event-stream; Charset=UTF-8'); header('Cache-Control: no-cache'); header('Connection: keep-alive'); header('X-Accel-Buffering: no'); header("Access-Control-Allow-Origin: *"); header("Access-Control-Allow-Credentials: true"); header('Access-Control-Allow-Methods: *'); header('Access-Control-Allow-Headers: *'); ob_start(); while (true) { $callback_res = $callback(); if($callback_res !== null) { $data = json_encode($callback_res, JSON_UNESCAPED_UNICODE); echo "data:{$data}\n\n"; } ob_flush(); flush(); usleep($millisecond * 1000); } } //调用 sse(function() { if('业务逻辑数据存在') { return ['k' => 'v']; } return null; }, 1000); SSE优点 实现简单易用。有断线重连的能力,即使网络中断,SSE仍旧会尝试每隔几秒自动重试的机制。避免了客户端使用短轮询造成请求量过大的问题,避免在项目中因需要一个实时的通信小模块就需要另外搭建WebSocket的问题,得不偿失。 SSE缺点 完全不兼容IE浏览器。SSE是一种半双工通信,因为数据只能在一个方向上流动,即从服务器到客户端。与之相比,全双工通信(例如WebSocket)允许数据在两个方向上同时流动,允许双向的数据传输。为了避免滥用和资源占用,一些浏览器可能会限制单个域名下的SSE连接数,例如同时最多打开6个连接。而另一些浏览器可能会限制整个浏览器实例中的SSE连接总数,这个限制不是由JavaScript语言本身所设定的,而是由浏览器实现所定义的。 SSE对比WebSocket 协议区别 协议:SSE是基于HTTP协议,而WebSocket则是独立的协议,它们都可以在浏览器和服务器之间建立持久的连接。 数据格式 SSE通过HTTP协议传输的数据格式是文本(通常是JSON格式),因此它适合用于传输简单的文本数据或者事件。而WebSocket可以传输文本和二进制数据,在处理音频、视频等大型数据时更有优势。 通信方式 SSE基于半双工模式,服务器可以通过发送事件流(event stream)来主动推送数据给客户端。客户端通过监听这些事件来接收数据。而WebSocket是全双工通信协议,客户端和服务器可以随时发送和接收数据。 兼容性 IE10及以上支持 WebSocket。但IE都不兼容SSE,并且不同浏览器对SSE兼容性不一样,可通过Polyfill解决,官网:https://developer.mozilla.org/en-US/docs/Glossary/Polyfill。
香港云服务器租用推荐
服务器租用资讯
·广东云服务有限公司怎么样
·广东云服务器怎么样
·广东锐讯网络有限公司怎么样
·广东佛山的蜗牛怎么那么大
·广东单位电话主机号怎么填写
·管家婆 花生壳怎么用
·官网域名过期要怎么办
·官网邮箱一般怎么命名
·官网网站被篡改怎么办
服务器租用推荐
·美国服务器租用
·台湾服务器租用
·香港云服务器租用
·香港裸金属服务器
·香港高防服务器租用
·香港服务器租用特价