Golang 心跳的实现

栏目: Go · 发布时间: 5年前

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

版权声明:本文为博主原创文章,未经博主允许不得转载。 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


以上所述就是小编给大家介绍的《Golang 心跳的实现》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

STL源码剖析

STL源码剖析

侯捷 / 华中科技大学出版社 / 2002-6 / 68.00元

学习编程的人都知道,阅读、剖析名家代码乃是提高水平的捷径。源码之前,了无秘密。大师们的缜密思维、经验结晶、技术思路、独到风格,都原原本本体现在源码之中。 这本书所呈现的源码,使读者看到vector的实现、list的实现、heap的实现、deque的实现、Red Black tree的实现、hash table的实现、set/map的实现;看到各种算法(排序、查找、排列组合、数据移动与复制技术......一起来看看 《STL源码剖析》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换