缓存管理方法
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;
}
关闭连接时清理
public function onClose(\swoole_server $server, int $fd, int $reactorId)
{
$info = $server->getClientInfo($fd);
if ($info && $info['websocket_status'] === WEBSOCKET_STATUS_FRAME) {
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
}
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
}
}
const actions = {
loginWx({ commit }, token) {
commit('SET_TOKEN_BY_COOKIE', token)
},
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
commit('SET_TOKEN', token)
resolve()
}).catch(error => {
reject(error)
})
})
},
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) => {
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]
})
},
logout({ commit, state, dispatch }) {
return new Promise((resolve, reject) => {
logout(state.token).then(() => {
commit('SET_TOKEN', '')
commit('SET_ROLES', [])
commit('SET_NAME', '')
removeToken()
resetRouter()
dispatch('tagsView/delAllViews', null, { root: true })
resolve()
}).catch(error => {
reject(error)
})
})
},
resetToken({ commit }) {
return new Promise(resolve => {
commit('SET_TOKEN', '')
commit('SET_ROLES', [])
removeToken()
resolve()
})
},
changeRoles({ commit, dispatch }, role) {
return new Promise(async resolve => {
const token = role + '-token'
commit('SET_TOKEN', token)
const { roles } = await dispatch('getInfo')
resetRouter()
const accessRoutes = await dispatch('permission/generateRoutes', roles, { root: true })
console.log('accessRoutes', accessRoutes)
router.addRoutes(accessRoutes)
dispatch('tagsView/delAllViews', null, { root: true })
resolve()
})
}
}
export default {
namespaced: true,
state,
mutations,
actions
}
暂作笔记 ,不完整之处还望留言指正