分享
  1. 首页
  2. 文章

Golang 心跳的实现

lengyuezuixue · · 4549 次点击 · · 开始浏览
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lengyuezuixue/article/details/79235850

在多客户端同时访问服务器的工作模式下,首先要保证服务器的运行正常。因此,Server和Client建立通讯后,确保连接的及时断开就非常重要。否则,多个客户端长时间占用着连接不关闭,是非常可怕的服务器资源浪费。会使得服务器可服务的客户端数量大幅度减少。

因此,针对短链接和长连接,根据业务的需求,配套不同的处理机制。

短连接

一般建立完连接,就立刻传输数据。传输完数据,连接就关闭。服务端根据需要,设定连接的时长。超过时间长度,就算客户端超时。立刻关闭连接。

长连接

建立连接后,传输数据,然后要保持连接,然后再次传输数据。直到连接关闭。

socket读写可以通过 SetDeadline、SetReadDeadline、SetWriteDeadline设置阻塞的时间。

func (*IPConn) SetDeadline 
func (c *IPConn) SetDeadline(t time.Time) error 
 
func (*IPConn) SetReadDeadline 
func (c *IPConn) SetReadDeadline(t time.Time) error 
 
func (*IPConn) SetWriteDeadline 
func (c *IPConn) SetWriteDeadline(t time.Time) error 

如果做短连接,直接在Server端的连接上设置SetReadDeadline。当你设置的时限到达,无论客户端是否还在继续传递消息,服务端都不会再接收。并且已经关闭连接。

func main() {
 server := ":7373"
 netListen, err := net.Listen("tcp", server)
 if err != nil{
 Log("connect error: ", err)
 os.Exit(1)
 }
 Log("Waiting for Client ...")
 for{
 conn, err := netListen.Accept()
 if err != nil{
 Log(conn.RemoteAddr().String(), "Fatal error: ", err)
 continue
 }
 //设置短连接(10秒)
 conn.SetReadDeadline(time.Now().Add(time.Duration(10)*time.Second))
 Log(conn.RemoteAddr().String(), "connect success!")
 ...
 }
}

这就可以了。在这段代码中,每当10秒中的时限一道,连接就终止了。

根据业务需要,客户端可能需要长时间保持连接。但是服务端不能无限制的保持。这就需要一个机制,如果超过某个时间长度,服务端没有获得客户端的数据,就判定客户端已经不需要连接了(比如客户端挂掉了)。

做到这个,需要一个心跳机制。在限定的时间内,客户端给服务端发送一个指定的消息,以便服务端知道客户端还活着。

func sender(conn *net.TCPConn) {
 for i := 0; i < 10; i++{
 words := strconv.Itoa(i)+" Hello I'm MyHeartbeat Client."
 msg, err := conn.Write([]byte(words))
 if err != nil {
 Log(conn.RemoteAddr().String(), "Fatal error: ", err)
 os.Exit(1)
 }
 Log("服务端接收了", msg)
 time.Sleep(2 * time.Second)
 }
 for i := 0; i < 2 ; i++ {
 time.Sleep(12 * time.Second)
 }
 for i := 0; i < 10; i++{
 words := strconv.Itoa(i)+" Hi I'm MyHeartbeat Client."
 msg, err := conn.Write([]byte(words))
 if err != nil {
 Log(conn.RemoteAddr().String(), "Fatal error: ", err)
 os.Exit(1)
 }
 Log("服务端接收了", msg)
 time.Sleep(2 * time.Second)
 }
}

这段客户端代码,实现了两个相同的信息发送频率给服务端。两个频率中间,我们让运行休息了12秒。然后,我们在服务端的对应机制是这样的。

func HeartBeating(conn net.Conn, bytes chan byte, timeout int) {
 select {
 case fk := <- bytes:
 Log(conn.RemoteAddr().String(), "心跳:第", string(fk), "times")
 conn.SetDeadline(time.Now().Add(time.Duration(timeout) * time.Second))
 break
 case <- time.After(5 * time.Second):
 Log("conn dead now")
 conn.Close()
 }
}

每次接收到心跳数据就 SetDeadline 延长一个时间段 timeout。如果没有接到心跳数据,5秒后连接关闭。

服务端完整代码示例

/**
* MyHeartbeatServer
* @Author: Jian Junbo
* @Email: junbojian@qq.com
* @Create: 2017年9月16日 14:02
* Copyright (c) 2017 Jian Junbo All rights reserved.
*
* Description: 
*/
package main
import (
 "net"
 "fmt"
 "os"
 "time"
)
func main() {
 server := ":7373"
 netListen, err := net.Listen("tcp", server)
 if err != nil{
 Log("connect error: ", err)
 os.Exit(1)
 }
 Log("Waiting for Client ...")
 for{
 conn, err := netListen.Accept()
 if err != nil{
 Log(conn.RemoteAddr().String(), "Fatal error: ", err)
 continue
 }
 //设置短连接(10秒)
 conn.SetReadDeadline(time.Now().Add(time.Duration(10)*time.Second))
 Log(conn.RemoteAddr().String(), "connect success!")
 go handleConnection(conn)
 }
}
func handleConnection(conn net.Conn) {
 buffer := make([]byte, 1024)
 for {
 n, err := conn.Read(buffer)
 if err != nil {
 Log(conn.RemoteAddr().String(), " Fatal error: ", err)
 return
 }
 Data := buffer[:n]
 message := make(chan byte)
 //心跳计时
 go HeartBeating(conn, message, 4)
 //检测每次是否有数据传入
 go GravelChannel(Data, message)
 Log(time.Now().Format("2006年01月02日 15:04:05.0000000"), conn.RemoteAddr().String(), string(buffer[:n]))
 }
 defer conn.Close()
}
func GravelChannel(bytes []byte, mess chan byte) {
 for _, v := range bytes{
 mess <- v
 }
 close(mess)
}
func HeartBeating(conn net.Conn, bytes chan byte, timeout int) {
 select {
 case fk := <- bytes:
 Log(conn.RemoteAddr().String(), "心跳:第", string(fk), "times")
 conn.SetDeadline(time.Now().Add(time.Duration(timeout) * time.Second))
 break
 case <- time.After(5 * time.Second):
 Log("conn dead now")
 conn.Close()
 }
}
func Log(v ...interface{}) {
 fmt.Println(v...)
 return
}

客户端完整代码示例

/**
* MyHeartbeatClient
* @Author: Jian Junbo
* @Email: junbojian@qq.com
* @Create: 2017年9月16日 14:21
* Copyright (c) 2017 Jian Junbo All rights reserved.
*
* Description: 
*/
package main
import (
 "net"
 "fmt"
 "os"
 "strconv"
 "time"
)
func main() {
 server := "127.0.0.1:7373"
 tcpAddr, err := net.ResolveTCPAddr("tcp4",server)
 if err != nil{
 Log(os.Stderr,"Fatal error:",err.Error())
 os.Exit(1)
 }
 conn, err := net.DialTCP("tcp",nil,tcpAddr)
 if err != nil{
 Log("Fatal error:",err.Error())
 os.Exit(1)
 }
 Log(conn.RemoteAddr().String(), "connection succcess!")
 sender(conn)
 Log("send over")
}
func sender(conn *net.TCPConn) {
 for i := 0; i < 10; i++{
 words := strconv.Itoa(i)+" Hello I'm MyHeartbeat Client."
 msg, err := conn.Write([]byte(words))
 if err != nil {
 Log(conn.RemoteAddr().String(), "Fatal error: ", err)
 os.Exit(1)
 }
 Log("服务端接收了", msg)
 time.Sleep(2 * time.Second)
 }
 for i := 0; i < 2 ; i++ {
 time.Sleep(12 * time.Second)
 }
 for i := 0; i < 10; i++{
 words := strconv.Itoa(i)+" Hi I'm MyHeartbeat Client."
 msg, err := conn.Write([]byte(words))
 if err != nil {
 Log(conn.RemoteAddr().String(), "Fatal error: ", err)
 os.Exit(1)
 }
 Log("服务端接收了", msg)
 time.Sleep(2 * time.Second)
 }
}
func Log(v ...interface{}) {
 fmt.Println(v...)
 return
}

参考:

https://www.yuque.com/docs/share/ef732d9e-f488-4e7e-8d64-43a1c18872ea


有疑问加站长微信联系(非本文作者)

本文来自:CSDN博客

感谢作者:lengyuezuixue

查看原文:Golang 心跳的实现

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

关注微信
4549 次点击
3 回复 | 直到 2020年02月25日 21:46:36
暂无回复
添加一条新回复 (您需要 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)

给该专栏投稿 写篇新文章

每篇文章有总共有 5 次投稿机会

收入到我管理的专栏 新建专栏