1、
为什么要设置心跳连接?
因为每当有一个用户登陆时,服务端都要新开启一个websocket服务,这会占用系统资源,因此当浏览器关闭、长时间无反应时,应及时关闭websocket服务解约资源
前端代码:
//在用户登陆后,进行初始化工作
created: function () {
this.loadfriends();
this.loadcommunitys();
this.loaddoutures();
//开启心跳链接,每10s执行heartbeat函数
setInterval(this.heartbeat, 10 * 1000);
var user = userInfo()
//初始化websocket
this.initwebsocket()
this.initUser();
},
* setInterval(func,int)
* - 定时调用
* - 可以将一个函数,每隔一段时间执行一次
* - 参数:
* 1.回调函数,该函数会每隔一段时间被调用一次
* 2.每次调用间隔的时间,单位是毫秒
*
* - 返回值:
* 返回一个Number类型的数据
* 这个数字用来作为定时器的唯一标识
//心跳函数
heartbeat() {
//判断是否已经断开连接
if (this.webSocket.readyState == 1) { //失去连接 3
var msg = this.createmsgcontext();
msg.Media = -1;
//3为心跳消息,帮助后端判断消息类型
msg.Type = 3
msg.Content = "心跳";
//通过webSocket推送心跳连接消息
this.webSocket.send(JSON.stringify(msg))
}
//消息结构体
msgcontext: {
TargetId: -1,
Type: -1,
CreateTime: new Date().getTime(),
userId: userId()
},
后端代码:
//启动服务器时,初始化定时器,开启心跳连接
func InitTimer() {
utils.Timer(time.Duration(viper.GetInt("timeout.DelayHeartbeat"))*time.Second, time.Duration(viper.GetInt("timeout.HeartbeatHz"))*time.Second, models.CleanConnection, "")
}
//定时配置
timeout:
DelayHeartbeat: 3 #延迟心跳时间(服务器开启后3s开启定时任务) 单位秒
HeartbeatHz: 30 #每隔多少秒心跳时间
HeartbeatMaxTime: 30000 #最大心跳时间 ,超过此就下线
RedisOnlineTime: 4 #缓存的在线用户时长 单位H
func Timer(delay, tick time.Duration, fun TimerFunc, param interface{}) {
go func() {
if fun == nil {
return
}
t := time.NewTimer(delay)
for {
select {
case <-t.C:
if fun(param) == false {
return
}
t.Reset(tick)
}
}
}()
}
// 清理超时连接
func CleanConnection(param interface{}) (result bool) {
result = true
defer func() {
if r := recover(); r != nil {
fmt.Println("cleanConnection err", r)
}
}()
//fmt.Println("定时任务,清理超时连接 ", param)
//node.IsHeartbeatTimeOut()
currentTime := uint64(time.Now().Unix())
//轮询每个连接,如果超市则断开
for i := range clientMap {
node := clientMap[i]
if node.IsHeartbeatTimeOut(currentTime) {
fmt.Println("心跳超时..... 关闭连接:", node)
node.Conn.Close()
}
}
return result
}
// 用户心跳是否超时
func (node *Node) IsHeartbeatTimeOut(currentTime uint64) (timeout bool) {
//判断当前时间是否超过最后一次更新心跳时间10s超过则讲连接断开
if node.HeartbeatTime+viper.GetUint64("timeout.HeartbeatMaxTime") <= currentTime {
fmt.Println("心跳超时。。。自动下线", node)
timeout = true
}
return
}
//阻塞接收websocket消息,如果消息type为3说明为心跳消息,更新心跳时间
func recvProc(node *Node) {
for {
_, data, err := node.Conn.ReadMessage()
if err != nil {
fmt.Println(err)
return
}
msg := Message{}
err = json.Unmarshal(data, &msg)
if err != nil {
fmt.Println(err)
}
//心跳检测 msg.Media == -1 || msg.Type == 3
if msg.Type == 3 {
currentTime := uint64(time.Now().Unix())
node.Heartbeat(currentTime)
} else {
dispatch(data)
// broadMsg(data) //todo 将消息广播到局域网
fmt.Println("[ws] recvProc <<<<< ", string(data))
}
}
}
// 更新用户心跳
func (node *Node) Heartbeat(currentTime uint64) {
node.HeartbeatTime = currentTime
return
}