前端微服务 websocket的简易封装,以及iframe如
一、功能解读
- 主应用封装 ws, 子应用共享;
- 心跳检测、自动重连;
iframe
子应用 订阅、推送(onPubSocket 、onSubSocket
);
二、代码片段
类型自行忽略
import _ from 'lodash'
import store from '../store'
// import { Notice } from 'view-design'
import Notification from '../components/Notification'
import { Callback, eventsMessageItem, wsOptions, resultType } from '../model/websocket'
class Websocket {
private websocket: any // ws 实体
private timer: any // 心跳检测定时器
private socketUrl: string // ws url
private timeout: number // 心跳检测时间间隔
private serverPort: number // ws 端口号
private isConnect: boolean = false // ws 是否连接
private eventsQueue: Record<string, Callback[]> = {} // pud sub 消息订阅推送队列
public eventsMessage: eventsMessageItem[] = [] // 保存的消息 只保存最新的20条
constructor (wsOpt: wsOptions) {
const { socketUrl, serverPort, timeout } = wsOpt
this.socketUrl = socketUrl
if (serverPort) this.serverPort = serverPort
if (timeout) this.timeout = timeout
}
private createWebSocket = () => {
if( typeof WebSocket === 'undefined' ) return console.error("您的浏览器不支持Websocket通信协议!")
if (this.websocket) return
this.initWebSocket()
}
// 初始化websocket
public initWebSocket = () => {
// noticeOpen()
this.websocket = new WebSocket(this.socketUrl)
// 连接成功
this.websocket.onopen = (e) => {
this.isConnect = true
this.openStatus(e)
}
// 连接关闭
this.websocket.onclose = (e) => {
this.closeStatus(e)
}
// 接收信息
this.websocket.onmessage = (e) => {
this.onmessage(e)
}
// 连接发生错误的回调方法
this.websocket.onerror = (e) => {
console.log(`ws 连接错误:${e}`)
}
}
// 重连
private reconnect = () => {
let cTimer: any = null
cTimer && clearTimeout(cTimer)
cTimer = setTimeout(() => {
this.initWebSocket()
}, 5000)
}
// 心跳检测
private socketHeartbeat = () => {
if (!this.isConnect) return
this.timer && clearTimeout(this.timer)
this.timer = setTimeout(() => {
if (this.websocket.readyState === 1) { // 连接正常 发送心跳包
console.log(`ws: 开启心跳检测 --- 发送心跳包`)
this.websocket.send(JSON.stringify({
type: 'heartbeat',
msg: 'heartbeat message'
}));
} else { // 否则重连
this.isConnect = false
this.reconnect();
}
}, this.timeout)
}
// 重置心跳
private onReset = () => {
console.log(`ws: 重置心跳`)
clearTimeout(this.timer)
// 重启心跳
this.socketHeartbeat()
}
// 数据接收
private onmessage = (e) => {
_.debounce(() => this.socketHeartbeat(), 2000)()
this.handleMessage(e)
}
// 消息处理
private handleMessage = (e) => {
const res = JSON.parse(e.data)
const { type, data } = res
switch (type) {
case 'heartbeat': // 心跳
console.info(`ws: 心跳正常`)
break;
case 'resource': // 资源数据
setDashboard(data)
break;
case 'new-message': // 消息通知
store.commit(`Launcher/updateNemMsg`, data)
break;
case 'task-total': // 任务
store.commit(`Launcher/updateIsTask`, data)
break;
case 'app-update-total': // 应用数据
this.onSubSocket('app-update-total', 'ws: message')
break;
default: // 其他通知
const length = this.eventsMessage.length
if (length >= 20) {
this.eventsMessage.shift()
this.eventsMessage.push(res)
}
// this.onSubSocket('notify', 'ws: message')
}
}
// 发布
public onPubSocket = (eventName: string, cb: Callback) => {
const query: Callback[] = this.eventsQueue[eventName] || [] // 获取原队列
query.push(cb) // 队列中追加cb
this.eventsQueue[eventName] = query // 重新赋值事件队列
}
// 订阅
public onSubSocket = (eventName: string, ...args: any[]) => {
const query: Callback[] = this.eventsQueue[eventName] || [] // 获取事件队列
if (query.length < 1) return
query.forEach((t: Callback) => { t(...args) }) // 执行事件队列中的回调函数数组
}
// 打开状态
private openStatus = (e) => {
console.log(`ws 连接成功:${e}`)
}
// 数据发送
public websocketSend = (data) => {
this.websocket.send(data)
}
// 关闭状态
private closeStatus = (e) => {
console.log(`ws 连接关闭:${e}`)
}
// 关闭
public onClose = () => {
this.isConnect = false
clearTimeout(this.timer)
this.timer = null
this.websocket.close()
}
}
const setDashboard = (data: resultType) => {
switch (data.name) {
case 'memory': // 内存 使用率
store.commit(`Launcher/updateDegRam`, data.usage * 1.8)
break;
case 'cpu': // cpu 使用率
store.commit(`Launcher/updateDegCpu`, data.usage * 1.8)
break;
case 'network': // 上传下载速率
store.commit(`Launcher/updateReceive`, data.receive)
store.commit(`Launcher/updateTransmit`, data.transmit)
break;
default:
break;
}
}
export default new Websocket({
socketUrl: 'ws://192.168.24.26:9999/ugreen/v1/desktop/ws',
serverPort: 80,
timeout: 25000
})
三、示例
3.1 主应用初始化
// ws 相关
initWebSocket () {
Websocket.initWebSocket()
Websocket.onPubSocket('state', (data) => {
console.log(`这是监听到 ws 的消息:${data}`)
})
this.$once('hook:beforeDestroy', function () {
Websocket.onClose()
window.removeEventListener("message", () => {})
})
},
3.2 子应用监听看下方使用文档
四、使用文档
#### websocket 使用说明
##### 1. 介绍
- 1.1 ws 的心跳检测时间是 25s, 可自定义
- 1.2 ws 意外断开重连时间是心跳检测时间的基础上 + 5s
- 1.3 launcher 桌面端会实时接收 server 端推送的消息, 并全部记录在 eventsMessage 内
- 1.4 接收消息后会主动调用 onSubSocket 方法通知订阅者
- 1.5 接收者通过 注册 onPubSocket 方法接收
##### 2. 使用
挂在到window: window.customWebsocket = websocket
- 2.1 通知订阅者示例
```js
Websocket.onSubSocket(msgType, 'ws: message')
```
* 2.1 接收者示例
```js
Websocket.onPubSocket(msgType, (msg) => {})
```
##### 3. 其他模块使用
* 3.1 当挂在在全局 window 上 属性为 customWebsocket; msgType需一致
```js
// launcher 应用
Websocket.onSubSocket(msgType, 'ws: message')
// 子应用接收示例
(window.top as any).customWebsocket.onPubSocket(msgType, (msg) => {})
```
* 3.1 当 通过 postMessage 方式接收时
```js
// launcher 应用 msgObj 会包含type 消息类型, 子应用自行过滤
window.top.postMessage(msgObj, '*')
// 子应用接收示例
(top as any).addEventListener('message', (msgObj) => {})
```