架構學習之路(四)-- IM系統初探

來源:互聯網
上載者:User

之前學習HTTP和TCP請求的時候經常看到一個名詞就是長串連,之前一直很好奇怎麼去實現,最近偶爾看到一篇文章寫的IM系統,想轉載學習一下。

IM系統,那麼必然需要TCP長串連來維持,由於Golang本身的基礎庫和外部依賴庫非常之多,我們可以簡單引用基礎net網路程式庫,來建立TCP server。一般的TCP Server端的模型,可以有一個協程【或者線程】去獨立執行accept,並且是for迴圈一直accept新的串連,如果有新串連過來,那麼建立串連並且執行Connect,由於Golang裡面協程的開銷非常之小,因此,TCP server端還可以一個串連一個goroutine去迴圈讀取各自串連鏈路上的資料並處理。當然, 這個在C++語言的TCP Server模型中,一般會通過EPoll模型來建立server端,這個是和C++的區別之處。
關於讀取資料,Linux系統有recv和send函數來讀取發送資料,在Golang中,內建有io庫,裡面封裝了各種讀寫方法,如io.ReadFull,它會讀取指定位元組長度的資料
為了維護串連和使用者,並且一個串連一個使用者的一一對應的,需要根據串連能夠找到使用者,同時也需要能夠根據使用者找到對應的串連,那麼就需要設計一個很好結構來維護。我們最初採用map來管理,但是發現Map裡面的資料太大,尋找的效能不高,為此,最佳化了資料結構,conn裡麵包含user,user裡麵包含conn,結構如下【只包括重要欄位】。

// 一個使用者對應一個串連type User struct {    uid               int64    conn              *MsgConn    BKicked           bool // 被另外登陸的一方踢下線    BHeartBeatTimeout bool // 心跳逾時}type MsgConn struct {    conn       net.Conn    lastTick   time.Time // 上次接收到包時間    remoteAddr string    // 為每個串連建立一個唯一識別碼    user       *User     // MsgConn與User一一映射}

建立TCP server 程式碼片段如下

func ListenAndServe(network, address string) {    tcpAddr, err := net.ResolveTCPAddr(network, address)    if err != nil {        logger.Fatalf(nil, "ResolveTcpAddr err:%v", err)    }    listener, err = net.ListenTCP(network, tcpAddr)    if err != nil {        logger.Fatalf(nil, "ListenTCP err:%v", err)    }    go accept()}func accept() {    for {        conn, err := listener.AcceptTCP()        if err == nil {            // 包計數,用來限制頻率            //anti-attack, 黑白名單            // 建立一個串連            imconn := NewMsgConn(conn)            // run            imconn.Run()        }     }}func (conn *MsgConn) Run() {    //on connect    conn.onConnect()    go func() {        tickerRecv := time.NewTicker(time.Second * time.Duration(rateStatInterval))        for {            select {            case <-conn.stopChan:                tickerRecv.Stop()                return            case <-tickerRecv.C:                conn.packetsRecv = 0            default:               // 在 conn.parseAndHandlePdu 裡面通過Golang本身的io庫裡面提供的方法讀取資料,如io.ReadFull                conn_closed := conn.parseAndHandlePdu()                if conn_closed {                    tickerRecv.Stop()                    return                }            }        }    }()}// 將 user 和 conn 一一對應起來func (conn *MsgConn) onConnect() *User {    user := &User{conn: conn, durationLevel: 0, startTime: time.Now(), ackWaitMsgIdSet: make(map[int64]struct{})}    conn.user = user    return user}
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.