发布日期

Laravel 中使用 swoole 项目实战开发案例二 (后端主动分场景给界面推送消息)

最近使用 swoole 做了项目,里面设计推送信息给界面前端,和无登陆用户的状态监控,以下是本人从中获取的一点心得,有改进的地方请留言评论。

需求分析

我们假设有一个需求,我在后端点击按钮1,首页弹出“后端触发了按钮1”。后端点了按钮2,列表页弹出“后端触发了按钮2”。做到根据不同场景推送到不同页面。

代码思路

  • Swoole fd

    客户端浏览器打开或者刷新界面,在swoole服务会生成一个进程句柄 fd ,每次浏览器页面有打开链接websocket的js代码,便会生成,每次刷新的时候,会关闭之前打开的 fd,重新生成一个新的,关闭界面的时候会生成一个新的。swoole的 fd生成规则是从1开始递增。

  • Redis Hash存储 fd

    我们建立一个key为swoole:fds redis哈希类型数据,fd 为hash的字段,每个字段的值我们存储前端websocket请求的url参数信息(根据业务复杂度自己灵活变通,我在项目中会在url带上sessionId)。每次链接打开swoole服务的时候我们存储其信息,每次关闭页面时候我们清除其字段。在redis存储如下

  • 首页

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>swoole首页</title>
      <meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no">
    </head>
    <body>
    <h1>这是首页</h1>
    </body>
    
      var ws;//websocket实例
      var lockReconnect = false;//避免重复连接
      var wsUrl = 'ws://{{$_SERVER["HTTP_HOST"]}}:9502?page=home&token=123456';
    
      function initEventHandle() {
          ws. = function () {
              reconnect(wsUrl);
          };
          ws. = function () {
              reconnect(wsUrl);
          };
          ws.onopen = function () {
              //心跳检测重置
              heartCheck.reset().start();
          };
          ws. = function (event) {
              //如果获取到消息,心跳检测重置
              //拿到任何消息都说明当前连接是正常的
              var data = JSON.parse(event.data);
              if (data.code == 200) {
                  console.log(data.message)
              }
              heartCheck.reset().start();
          }
      }
      createWebSocket(wsUrl);
      /**
       * 创建链接
       * @param url
       */
      function createWebSocket(url) {
          try {
              ws = new WebSocket(url);
              initEventHandle();
          } catch (e) {
              reconnect(url);
          }
      }
      function reconnect(url) {
          if(lockReconnect) return;
          lockReconnect = true;
          //没连接上会一直重连,设置延迟避免请求过多
          setTimeout(function () {
              createWebSocket(url);
              lockReconnect = false;
          }, 2000);
      }
      //心跳检测
      var heartCheck = {
          timeout: 60000,//60秒
          timeoutObj: null,
          serverTimeoutObj: null,
          reset: function(){
              clearTimeout(this.timeoutObj);
              clearTimeout(this.serverTimeoutObj);
              return this;
          },
          start: function(){
              var self = this;
              this.timeoutObj = setTimeout(function(){
                  //这里发送一个心跳,后端收到后,返回一个心跳消息,
                  //拿到返回的心跳就说明连接正常
                  ws.send("heartbeat");
                  self.serverTimeoutObj = setTimeout(function(){//如果超过一定时间还没重置,说明后端主动断开了
                      ws.close();//如果onclose会执行reconnect,我们执行ws.close()就行了.如果直接执行reconnect 会触发onclose导致重连两次
                  }, self.timeout);
              }, this.timeout);
          },
          header:function(url) {
              .href=url
          }
    
      }
    
    </html>
  • 列表页面

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>swoole列表页</title>
      <meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no">
    </head>
    <body>
    <h1>swoole列表页</h1>
    </body>
    
      var ws;//websocket实例
      var lockReconnect = false;//避免重复连接
      var wsUrl = 'ws://{{$_SERVER["HTTP_HOST"]}}:9502?page=list&token=123456';
    
      function initEventHandle() {
          ws. = function () {
              reconnect(wsUrl);
          };
          ws. = function () {
              reconnect(wsUrl);
          };
          ws.onopen = function () {
              //心跳检测重置
              heartCheck.reset().start();
          };
          ws. = function (event) {
              //如果获取到消息,心跳检测重置
              //拿到任何消息都说明当前连接是正常的
              var data = JSON.parse(event.data);
              if (data.code == 200) {
                  console.log(data.message)
              }
              heartCheck.reset().start();
          }
      }
      createWebSocket(wsUrl);
      /**
       * 创建链接
       * @param url
       */
      function createWebSocket(url) {
          try {
              ws = new WebSocket(url);
              initEventHandle();
          } catch (e) {
              reconnect(url);
          }
      }
      function reconnect(url) {
          if(lockReconnect) return;
          lockReconnect = true;
          //没连接上会一直重连,设置延迟避免请求过多
          setTimeout(function () {
              createWebSocket(url);
              lockReconnect = false;
          }, 2000);
      }
      //心跳检测
      var heartCheck = {
          timeout: 60000,//60秒
          timeoutObj: null,
          serverTimeoutObj: null,
          reset: function(){
              clearTimeout(this.timeoutObj);
              clearTimeout(this.serverTimeoutObj);
              return this;
          },
          start: function(){
              var self = this;
              this.timeoutObj = setTimeout(function(){
                  //这里发送一个心跳,后端收到后,返回一个心跳消息,
                  //拿到返回的心跳就说明连接正常
                  ws.send("heartbeat");
                  self.serverTimeoutObj = setTimeout(function(){//如果超过一定时间还没重置,说明后端主动断开了
                      ws.close();//如果onclose会执行reconnect,我们执行ws.close()就行了.如果直接执行reconnect 会触发onclose导致重连两次
                  }, self.timeout);
              }, this.timeout);
          },
          header:function(url) {
              .href=url
          }
    
      }
    
    </html>

    界面效果

    后台控制点击按钮1

    Laravel 中使用 swoole 项目实战开发案例一 (建立 swoole 和前端通信) Laravel 中使用 swoole 项目实战开发案例一 (建立 swoole 和前端通信)

    后端界面点击按钮2

    Laravel 中使用 swoole 项目实战开发案例一 (建立 swoole 和前端通信)

备案号:湘ICP备2020019075号 © 2020 yxx All rights reserved. | my github