简单的WebSocket fd与uid 缓存管理

缓存管理方法

/**
     * 维护fd=uid的有序集合
     * @param string $model
     * @param int $fd 
     * @param int $uid
     * @param bool $isDel 清理fd 此时必须传fd
     * @return bool|string|void
     * @throws \EasySwoole\Redis\Exception\RedisException
     */
    static function fdList($fd = 0, $uid = 0, $isDel = false)
    {
        $key = RedisKey::WEB_SOCKET_SSETS;
        if (!$redis = RedisKey::getRedis()) {
            return;
        }
        if ($isDel && $fd) {
            return $redis->zRemRangeByScore($key, $fd, $fd);
        }
        if ($fd && $uid) {
            return $redis->zAdd($key, $fd, $uid);
        }

        if ($fd) {
            return $redis->zRangeByScore($key, $fd, $fd, ['withScores' => false, 'limit' => array(0, 1)]);
        }

        if ($uid) {
            return $redis->zScore($key, $uid);
        }
    }

建立连接时调用

protected function onRequest(?string $actionName): bool
    {

        if (parent::onRequest($actionName) === false) {
            return false;
        }

        $this->param = $this->caller()->getArgs();
        $this->fd = null;
        if (!empty($this->param['old_fd'])) {
            $server = ServerManager::getInstance()->getSwooleServer();
            $info = $server->getClientInfo($this->param['old_fd'] * 1);
            if ($info && $info['websocket_status'] === WEBSOCKET_STATUS_FRAME) {
                $this->fd = $this->param['old_fd'];
            } else {
                $uid = WebSocketService::fdList($this->param['old_fd']);
                empty($uid[0]) || $this->param['uid'] = $uid[0];
            }
        }


        if (!empty($this->fd)) {
            empty($this->param['uid']) || WebSocketService::fdList($this->fd, $this->param['uid']);
            return false;
        }

        $this->fd = $this->caller()->getClient()->getFd();
        empty($this->param['uid']) || WebSocketService::fdList($this->fd, $this->param['uid']);
        $this->data = ['fd' => $this->fd, 'msg' => '请刷新FD'];
        return true;
    }

关闭连接时清理

 /**
     * 关闭事件
     * @param \swoole_server $server
     * @param int $fd
     * @param int $reactorId
     * @throws \EasySwoole\Redis\Exception\RedisException
     */
    public function onClose(\swoole_server $server, int $fd, int $reactorId)
    {
        /** @var array $info */
        $info = $server->getClientInfo($fd);
//        var_dump('ws_close', $fd, $info, $reactorId);
        /**
         * 判断此fd 是否是一个有效的 websocket 连接
         * 参见 https://wiki.swoole.com/wiki/page/490.html
         */
        if ($info && $info['websocket_status'] === WEBSOCKET_STATUS_FRAME) {
            /**
             * 判断连接是否是 server 主动关闭
             * 参见 https://wiki.swoole.com/wiki/page/p-event/onClose.html
             */
            if ($reactorId < 0) {
                echo "server close \n";
            }
        }
        WebSocketService::fdList($fd, 0, true);
    }

前端vuex代码

import { login, logout, getInfo } from '@/api/user'
import { getToken, setToken, removeToken } from '@/utils/auth'
import router, { resetRouter } from '@/router'

const state = {
  token: getToken(),
  name: '',
  avatar: '',
  introduction: '',
  roles: [],
  ws: null,
  fd: null,
  uid: 0
  // roles_group_ids: []
}
const onmsgFunList = {}
const mutations = {
  WS_INIT: (state, ws) => {
    state.ws = ws
  },
  SET_FD: (state, fd) => {
    state.fd = fd
  },
  SET_TOKEN: (state, token) => {
    state.token = token
  },
  SET_TOKEN_BY_COOKIE: (state, token) => {
    state.token = token
    setToken(token)
  },
  SET_INTRODUCTION: (state, introduction) => {
    state.introduction = introduction
  },
  SET_NAME: (state, name) => {
    state.name = name
  },
  SET_UID: (state, uid) => {
    state.uid = uid
  },
  SET_AVATAR: (state, avatar) => {
    state.avatar = avatar
  },
  SET_ROLES: (state, roles) => {
    state.roles = roles
  }
  // SET_ROLES_GROUP_IDS: (state, roles) => {
  //   state.roles_group_ids = roles
  // }
}

const actions = {
  loginWx({ commit }, token) {
    commit('SET_TOKEN_BY_COOKIE', token)
  },
  // setGroupIds({ commit }, roles) {
  //   console.log('roles', roles)
  //   commit('SET_ROLES_GROUP_IDS', roles)
  // },
  // user login 用户登录方法
  login({ commit }, userInfo) {
    let sendInfo = null
    if (userInfo.password) {
      sendInfo = { mobile: userInfo.mobile.trim(), password: userInfo.password } // 账号密码登录
    } else if (userInfo.code) {
      sendInfo = { mobile: userInfo.mobile.trim(), code: userInfo.code.toString().trim(), wx_flag: userInfo.wx_flag } // 短息登录
    }
    return new Promise((resolve, reject) => {
      login(sendInfo).then(response => {
        const { token } = response // 获取token
        commit('SET_TOKEN', token) // 存储token
        // setToken(data.token)
        resolve()
      }).catch(error => {
        reject(error)
      })
    })
  },

  // get user info
  getInfo({ commit, state, dispatch }) {
    return new Promise((resolve, reject) => {
      getInfo().then(response => {
        const { data } = response
        if (!data) {
          reject('验证失败,请重新登录。')
        }
        const { name, avatar, introduction, rouls, id } = data

        commit('SET_ROLES', rouls)
        commit('SET_UID', id)
        commit('SET_NAME', name)
        commit('SET_AVATAR', avatar)
        commit('SET_INTRODUCTION', introduction)
        dispatch('sendData', {content: { uid: id }})
        resolve(data)
      }).catch(error => {
        reject(error)
      })
    })
  },
  wsOpen({ commit, state, dispatch }) {
    let ws = new WebSocket('ws' + process.env.VUE_APP_BASE_API)
    console.log('ws',ws.readyState)
    ws.onerror = (evt, e) => {
      // todo 通过ajax 告诉 服务器杀掉fd
      if (ws && state.fd) ws.close()
      console.log('websocket发生错误', evt, e)
      ws = null
    }

    ws.onopen = (evt) => {
      console.log('websocket连接开启', evt)
      const data = { 'model': 'Admin', action: 'index', content: { old_fd: state.fd||0, uid: state.uid }}
      ws.send(JSON.stringify(data))
    }

    ws.onclose = (evt) => {
      console.log('websocket关闭链接', evt)
      commit('SET_FD', 0)
      setTimeout(() => {
        try {
          dispatch('wsOpen')
        } catch (e) {
          console.log('e',e)
        }

      }, 3000)
    }

    ws.onmessage = ({ data }) => {
      data = data ? JSON.parse(data) : null
      if (data['fd']) {
        console.log('SET_FD', data['fd'])
        commit('SET_FD', data['fd'])
      }
      if (data['action'] && onmsgFunList[data['action']]) onmsgFunList[data['action']](data)
      console.log('websocket收到数据', data)
    }

    // 做心跳
    if (ws) {
      setInterval(() => {
        const data = { 'model': 'Admin', action: 'index', content: { old_fd: state.fd||0 }}
        if (state.fd) ws.send(JSON.stringify(data))
        console.log('ws 心跳。。。',state.fd)
      }, 10000)
    }

    commit('WS_INIT', ws)
  },
  sendData({ state }, { content, action, model } ) {
    content['old_fd'] = state.fd
    model = model || 'Admin'
    action=action||'index'
    state.ws.send(JSON.stringify({ model, action , content }))
  },
  addCallBack({ state }, callbackObj) {
    Object.assign(onmsgFunList, callbackObj)
  },
  unsetCallBack({ state }, key) {
    if (!key || !key.length) return
    key.forEach(item => {
      if (onmsgFunList[item]) delete onmsgFunList[item]
    })
  },
  // user logout
  logout({ commit, state, dispatch }) {
    return new Promise((resolve, reject) => {
      logout(state.token).then(() => {
        commit('SET_TOKEN', '')
        commit('SET_ROLES', [])
        commit('SET_NAME', '')
        removeToken()
        resetRouter()

        // reset visited views and cached views
        // to fixed https://github.com/PanJiaChen/vue-element-admin/issues/2485
        dispatch('tagsView/delAllViews', null, { root: true })

        resolve()
      }).catch(error => {
        reject(error)
      })
    })
  },

  // remove token
  resetToken({ commit }) {
    return new Promise(resolve => {
      commit('SET_TOKEN', '')
      commit('SET_ROLES', [])
      removeToken()
      resolve()
    })
  },

  // dynamically modify permissions
  changeRoles({ commit, dispatch }, role) {
    return new Promise(async resolve => {
      const token = role + '-token'

      commit('SET_TOKEN', token)
      // setToken(token)

      const { roles } = await dispatch('getInfo')

      resetRouter()

      // generate accessible routes map based on roles
      const accessRoutes = await dispatch('permission/generateRoutes', roles, { root: true })
      console.log('accessRoutes', accessRoutes)
      // dynamically add accessible routes
      router.addRoutes(accessRoutes)

      // reset visited views and cached views
      dispatch('tagsView/delAllViews', null, { root: true })

      resolve()
    })
  }
}

export default {
  namespaced: true,
  state,
  mutations,
  actions
}

暂作笔记 ,不完整之处还望留言指正

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值