第六章节:WebSocket断线自动重连

前言

上一章节我们完成了一个单机IM服务该具备的基本功能,但是还存在一些问题,比如当server因为其他原因重启或者宕机时,client端还持有一个已经断开的连接,这个连接无法收发消息,所以client端需要检测当前连接是否可用,当发现不可用时需要重连。

检查连接

gorilla-websocket提供的websocket.Conn相关ReadXX方法中,当连接被关闭时会返回websocket.CloseError这个错误,所以client可以通过返回的错误是websocket.CloseError时进行重连。

首先将连接WS server的方法抽取出来, 在client.go中添加connect方法

go 复制代码
// 连接到WS server,并带有retry
func connect(url string, header http.Header) (*websocket.Conn, error) {
    var (
        conn *websocket.Conn
        err  error
    )
    maxRetries := 5
    for attempt := 0; attempt < maxRetries; attempt++ {
        conn, _, err = websocket.DefaultDialer.Dial(url, header)
        if err == nil {
            log.Println("connected to server ...")
            return conn, nil
        }
        if attempt < maxRetries {
            // 失败后的延迟
            time.Sleep(time.Second)
        }
    }
    return nil, err
}

client.go中添加判断连接断开

go 复制代码
func isClosed(err error) bool {
    _, ok := err.(*websocket.CloseError)
    return ok
}

修改client.go的main方法

go 复制代码
func main() {
    ... // 省略 
    u := model.User{
        ID:   userID,
        Name: args[1],
    }
    token := u.GenToken()
    header := http.Header{
        "X-TOKEN": []string{token},
    }
    conn, err := connect(uri, header)
    if err != nil {
        log.Fatal("dial failed:", err)
    }
    defer conn.Close()

    go func() {
        for {
            m := &model.Message{}
            err = conn.ReadJSON(m)
            if err != nil {
                // server shutdown or socket closed
                if isClosed(err) {
                    // reconnect
                    log.Println("Trying reconnect ...")
                    conn, err = connect(uri, header)
                    if err != nil {
                        log.Fatalln("Reconnect Failed...", err)
                        return
                    }
                    continue
                }
                log.Println("Read WS message failed:", err)
                continue
            }
            log.Printf("Received Message %v From %v \n", m.Data, m.FromUserID)
            err = markMsgRead(*m, token)
            if err != nil {
                log.Println("MarkRead failed:", err)
            }
        }
    }()
    ... // 省略
}

调试

首先分别在2个Terminal中启动server和client, 然后停止server,观察client端的日志,5秒内立即重启server,观察server和client的日志,可以在server端看到client连接的日志,然后再新开一个Terminal用另一个用户登录,可以看到用户间的收发消息功能正常。

Client日志

Server日志

小结

本章节中我们在客户端通过识别错误类型判断连接是否可用,并在连接不可用时进行重连,这样当server端短暂重启或因网络波动导致连接断开时可以自动重新进行连接,提高了服务的可靠性。但是随着客户端(用户)变多,要提高服务的高可用性,服务端必须采用多实例部署方案,也就是分布式系统,下一章节我们将探讨服务端如何在分布式环境下共享连接状态、通信。

相关推荐
27669582924 小时前
京东e卡滑块 分析
java·javascript·python·node.js·go·滑块·京东
一丝晨光1 天前
Java、PHP、ASP、JSP、Kotlin、.NET、Go
java·kotlin·go·php·.net·jsp·asp
zaim11 天前
计算机的错误计算(一百一十四)
java·c++·python·rust·go·c·多项式
百里守约学编程2 天前
70. 爬楼梯
算法·leetcode·go
拾伍廿肆2 天前
python - websocket
python·websocket·django
Sun_Sherry2 天前
FastAPI: websocket的用法及举例
websocket·网络协议·fastapi
貂蝉空大3 天前
uni-app 封装websocket 心跳检测,开箱即用
websocket·网络协议·uni-app
a_ran3 天前
一些 Go Web 开发笔记
后端·golang·go·编程·web·网站
影灵衣丶3 天前
go进阶编程:设计模式之适配器模式
后端·go
白鹭float.3 天前
【Unity AI】基于 WebSocket 和 讯飞星火大模型
人工智能·websocket·unity