page contents

golang IM框架设计

本文讲述了golang IM框架设计!具有很好的参考价值,希望对大家有所帮助。一起跟随六星小编过来看看吧,具体如下:

attachments-2022-03-FUsfW0Lm6226b8607c208.png

本文讲述了golang IM框架设计!具有很好的参考价值,希望对大家有所帮助。一起跟随六星小编过来看看吧,具体如下:

1.1 传输协议的选择

  • 项目现状 目前,常见的IM系统传输报文无外乎使用UDP、TCP以及应用层的HTTP这几种协议。市面上象微信、MSN、陌陌、米聊、环信等大多采用TCP协议,只有QQ比较特殊,采用了UDP协议,应该是历史原因造成的,可能与当时的网络条件和初始资源有关。

  • UDP协议 UDP协议提供了一种不可靠的无连接数据包传输服务。它不提供报文到达的确认、排序、及流量控制等功能。本身设计比较简洁,数据包较小,无需确认等特点,所以传输效率极高,比较适合应于流媒体类型的业务,这些业务对于少量数据包的损失不敏感。但对于IM系统来讲,对数据的完整性要求高、传输有序,直接使用UDP协议就不合适。如要使用就必须在UDP协议基础上再增加校验、重发机制。

  • TCP协议 TCP协议提供了一种可靠的有连接数据包传输服务。它解决了UDP协议对于报文到达的确认、排序、及流量控制等方面的缺陷,能够保证可靠地数据传输。但它的缺点是传输效率要比UDP低,对于服务端大量数据的处理,需要耗费较高的资源,尤其是在长连接情况下,如何保证单机服务器高并发量,如何灵活地进行水平扩展都需要做好设计。

  • HTTP协议 有的IM采用了TCP之上的应用层HTTP协议的传输,但一般对接的是Web客户端。还有的是作为一项辅助功能,提供第三方访问的web接口。

  • 结论 我们的IM产品将采用TCP传输协议收发报文,以后会增加HTTP协议作为开放接口。

1.2 基于TCP协议的长连接测试
1.2.1 编程语言

以下的测试工作完全基于golang代码进行的测试。为什么使用golang而不是c/c++或者java呢?选择golang主要基于以下因素的考虑:

  • golang 内置的协程机制,非常适合于开发高并发系统,协程类似于轻量级的线程,对系统资源消耗极少,使用过程比线程简单。golang还为协程之间的通讯专门设计了channel,以及封装了各种锁和其他同步工具,简化了并发系统开发的难度。golang丰富的内置类型,函数多返回值,defer机制等新的语言特色,也极大地方便快速开发。golang的开发速度、编程难度远低于c/c++。
  • golang不是一种解释性语言,它根据不同的操作系统直接编译为二进制运行代码,所以它的运行效率比java,python等解释性语言要高,但比c/c++二进制代码的运行效率要略低。 基于以上两点的权衡,我们选择了golang语言进行系统开发。

1.2.2 测试说明

对于IMServer来讲,首先满足单服务器能够抗住足够多的并发的Tcp长连接。golang 底层通讯模型,windows默认使用了IOCP机制,linux默认使用了epoll模型,均能抗住高并发。

IMClient根据日常使用场景,设计了每1分钟发送500个字节数据,后接收server推送的500个字节数据,以此循环进行,并保持长连接。IMServer接收数据,并原样回送数据。测试环境中,Server使用了一台低配PC,2核CPU,8G内存,操作系统为centos7/Windows。

每个客户端开启了6000个连接,为了模拟更多的并发,同时开启了8-10个客户端,模拟5万长连接用户使用IM服务,进行压力测试。

当所有连接正常开启以后,用telnet单独连接服务器,发送数据和回送数据依然正常,说明在当前并发情况下,系统正常运行,符合预期。

单台低配服务器达到5万已经足够,无需接受更多的长连接。如果用户超过5万长连接,可以采用水平扩展方式,加入更多的服务器,进行负载均衡即可。

attachments-2022-03-vZ7mpZgm6226b7d02b35c.png

客户端代码 im_client.go

package main

import (
    "fmt"
    "net"
    "time"
    "strconv"
)

const (
    //根据自身情况修改服务器ip地址
    serverAddr = "192.168.1.36:120"
)

func main() {
    succCount := 0
    failCount := 0
    for i:=0; i<6000; i++ {
        conn, err := net.Dial("tcp", serverAddr)
        if err != nil {
            failCount++
            fmt.Println("Number of dialing failures:", failCount)
            time.Sleep(time.Second)
            continue
        }
        defer conn.Close()

        fmt.Println("Number of successful connections:", succCount )

        go Client(conn, succCount )

        succCount ++
    }

    //Stop here to prevent the program from exiting
    tick := time.NewTicker(10 * time.Second)
    for {
        select {
            case <-tick.C:
        }    
    }
}

func Client(conn net.Conn, index int) error {
    dataRead := make([]byte, 512)
    dataWrite := strconv.Itoa(index) + 
    "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\n" +
    "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\n" +
    "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\n" +
    "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\n" +
    "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\n"

        for {
            conn.Write([]byte(dataWrite))
            _, err := conn.Read(dataRead)
            if err != nil {
                return err
            }
            time.Sleep(60*time.Second)
    }
    return nil
}

linux下运行前,必须重新设置可打开的最大文件句柄数,默认为1024,也就是最大只能接受1024个连接数。我们设置为65330:

attachments-2022-03-vo3HLoDR6226b7e7521d3.png

服务器端代码 im_server.go

package main

import (
    "net"
    "log"
    "time"
)

const (
    SERVER_IP = ""
    SERVER_PORT = "120"
)

type Server struct {

}

func (server *Server) Start() {

    serverAddr := SERVER_IP + ":" + SERVER_PORT
    listener, err := net.Listen("tcp", serverAddr)
    if err != nil {
        log.Println("Failed to execute function net.Listen: ", err.Error())
        return
    }
    defer listener.Close()

    for {
        conn, err := listener.Accept()
        if err != nil {
            if nerr, ok := err.(net.Error); ok && nerr.Temporary() {
                log.Println("Continue accepting connection because of an temporary error: ", err.Error())
                time.Sleep(1 *time.Second)
                continue
            }

            log.Println("Failed to execute function listener.Accept, exit app because of an error: ", err.Error())
            return
        }

        sess := &Session {
            Conn: conn,
        }
        go sess.Serve()
    }
}

func main() {
    (&Server{}).Start()
}

服务器端代码 im_session.go

package main

import (
    "bufio"
    "net"
    "log"
    "fmt"
)

type Session struct {
    Conn    net.Conn
    Reader    *bufio.Reader
    Writer    *bufio.Writer
}

func (session *Session) Serve() {
    defer session.Close()

    session.Reader = bufio.NewReader(session.Conn)
    session.Writer = bufio.NewWriter(session.Conn)

    for {
        byteData, err := session.Reader.ReadSlice('\n')
        if err != nil {
            log.Println("Error occured in funciton Session.Serve: ",err.Error() )
            return
        }

        line := string(byteData)
        fmt.Println(line)
        session.Send(line)
    }
}

func (session *Session) Close() {
    session.Conn.Close()
}

func (session *Session) write(data string) error {
    if _, err := fmt.Fprint(session.Writer, data); err != nil {
        return err
    }
    return session.Writer.Flush()
}

func (session *Session) Send(data string) error {
    return session.write(data)
}

1.3 后续工作

  • GC调优 golang采用垃圾收集器(GC)自动调度内存垃圾回收工作,目前1.12版本已经做得非常优秀了,垃圾回收过程卡顿不明显,但是作为server需要承载大流量的冲击,我们必须得充分考虑减少GC的次数,降低在堆上反复进行内存的分配和回收,减少内存的使用量。这些工作需要配合golang的调优工具,以及读懂golang的部分源代码。
  • 更多相关技术内容咨询欢迎前往并持续关注六星社区了解详情。

如果你想用Python开辟副业赚钱,但不熟悉爬虫与反爬虫技术,没有接单途径,也缺乏兼职经验
关注下方微信公众号:Python编程学习圈,获取价值999元全套Python入门到进阶的学习资料以及教程,还有Python技术交流群一起交流学习哦。

attachments-2022-06-Dzurl3F762b2da8ac68fb.jpeg

  • 发表于 2022-03-08 09:59
  • 阅读 ( 870 )
  • 分类:Golang

你可能感兴趣的文章

相关问题

0 条评论

请先 登录 后评论
轩辕小不懂
轩辕小不懂

2403 篇文章

作家榜 »

  1. 轩辕小不懂 2403 文章
  2. 小柒 1474 文章
  3. Pack 1135 文章
  4. Nen 576 文章
  5. 王昭君 209 文章
  6. 文双 71 文章
  7. 小威 64 文章
  8. Cara 36 文章