GoFrame TCP組件-連接池特性

2022-04-15 13:56 更新

?gtcp?模塊提供了連接池的特性,由?gtcp.PoolConn?對(duì)象實(shí)現(xiàn),連接池緩存固定存活時(shí)間為600秒,且內(nèi)部實(shí)現(xiàn)了數(shù)據(jù)發(fā)送時(shí)的斷開(kāi)重連機(jī)制。連接池非常適合于頻繁的短鏈接操作且連接并發(fā)量大的場(chǎng)景。我們接下來(lái)使用兩個(gè)示例來(lái)演示一下連接池的作用。

使用方式:

import "github.com/gogf/gf/v2/net/gtcp"

接口文檔:

https://pkg.go.dev/github.com/gogf/gf/v2/net/gtcp

type PoolConn
    func NewPoolConn(addr string, timeout ...int) (*PoolConn, error)
    func (c *PoolConn) Close() error
    func (c *PoolConn) Recv(length int, retry ...Retry) ([]byte, error)
    func (c *PoolConn) RecvLine(retry ...Retry) ([]byte, error)
    func (c *PoolConn) RecvPkg(option ...PkgOption) ([]byte, error)
    func (c *PoolConn) RecvPkgWithTimeout(timeout time.Duration, option ...PkgOption) ([]byte, error)
    func (c *PoolConn) RecvWithTimeout(length int, timeout time.Duration, retry ...Retry) (data []byte, err error)
    func (c *PoolConn) Send(data []byte, retry ...Retry) error
    func (c *PoolConn) SendPkg(data []byte, option ...PkgOption) (err error)
    func (c *PoolConn) SendPkgWithTimeout(data []byte, timeout time.Duration, option ...PkgOption) error
    func (c *PoolConn) SendRecv(data []byte, receive int, retry ...Retry) ([]byte, error)
    func (c *PoolConn) SendRecvPkg(data []byte, option ...PkgOption) ([]byte, error)
    func (c *PoolConn) SendRecvPkgWithTimeout(data []byte, timeout time.Duration, option ...PkgOption) ([]byte, error)
    func (c *PoolConn) SendRecvWithTimeout(data []byte, receive int, timeout time.Duration, retry ...Retry) ([]byte, error)
    func (c *PoolConn) SendWithTimeout(data []byte, timeout time.Duration, retry ...Retry) error

由于?gtcp.PoolConn?繼承于?gtcp.Conn?因此同時(shí)也可以使用?gtcp.Conn?的方法。

示例1,基本使用

package main

import (
    "fmt"
    "time"
    "github.com/gogf/gf/v2/net/gtcp"
    "github.com/gogf/gf/v2/os/glog"
    "github.com/gogf/gf/v2/os/gtime"
)

func main() {
    // Server
    go gtcp.NewServer("127.0.0.1:8999", func(conn *gtcp.Conn) {
        defer conn.Close()
        for {
            data, err := conn.Recv(-1)
            if len(data) > 0 {
                if err := conn.Send(append([]byte("> "), data...)); err != nil {
                    fmt.Println(err)
                }
            }
            if err != nil {
                break
            }
        }
    }).Run()

    time.Sleep(time.Second)

    // Client
    for {
       if conn, err := gtcp.NewPoolConn("127.0.0.1:8999"); err == nil {
           if b, err := conn.SendRecv([]byte(gtime.Datetime()), -1); err == nil {
               fmt.Println(string(b), conn.LocalAddr(), conn.RemoteAddr())
           } else {
               fmt.Println(err)
           }
           conn.Close()
       } else {
           glog.Error(err)
       }
       time.Sleep(time.Second)
    }
}

在這個(gè)示例中,?Server?創(chuàng)建新的?goroutine?異步運(yùn)行,?Client?在?main goroutine?中執(zhí)行。?Server?端是一個(gè)回顯服務(wù)器,?Client?每隔1秒向?Server?端發(fā)送當(dāng)前的時(shí)間,經(jīng)過(guò)?Server?端回顯返回后,在?Client?端打印出雙方的連接端口信息。

執(zhí)行后,結(jié)果如下:

> 2018-07-11 23:29:54 127.0.0.1:55876 127.0.0.1:8999
> 2018-07-11 23:29:55 127.0.0.1:55876 127.0.0.1:8999
> 2018-07-11 23:29:56 127.0.0.1:55876 127.0.0.1:8999
> 2018-07-11 23:29:57 127.0.0.1:55876 127.0.0.1:8999
> 2018-07-11 23:29:58 127.0.0.1:55876 127.0.0.1:8999
...

可以看到,?Client?的端口一直未變,每一次通過(guò)?gtcp.NewConn("127.0.0.1:8999")?獲得的都是同一個(gè)?gtcp.Conn?對(duì)象,且每一次?conn.Close()?時(shí)并不是真正的關(guān)閉連接,而是將該對(duì)象重新丟回到連接池里循環(huán)使用。

示例2,連接斷開(kāi)情況

這個(gè)例子是為了展示當(dāng)服務(wù)端關(guān)閉連接后,該連接對(duì)象還是否有效的處理。

package main

import (
    "fmt"
    "time"
    "github.com/gogf/gf/v2/net/gtcp"
    "github.com/gogf/gf/v2/os/glog"
    "github.com/gogf/gf/v2/os/gtime"
)

func main() {
    // Server
    go gtcp.NewServer("127.0.0.1:8999", func(conn *gtcp.Conn) {
        defer conn.Close()
        for {
            data, err := conn.Recv(-1)
            if len(data) > 0 {
                if err := conn.Send(append([]byte("> "), data...)); err != nil {
                    fmt.Println(err)
                }
            }
            if err != nil {
                break
            }
            return
        }
    }).Run()

    time.Sleep(time.Second)

    // Client
    for {
       if conn, err := gtcp.NewPoolConn("127.0.0.1:8999"); err == nil {
           if b, err := conn.SendRecv([]byte(gtime.Datetime()), -1); err == nil {
               fmt.Println(string(b), conn.LocalAddr(), conn.RemoteAddr())
           } else {
               fmt.Println(err)
           }
           conn.Close()
       } else {
           glog.Error(err)
       }
       time.Sleep(time.Second)
    }
}

執(zhí)行后,輸出結(jié)果如下:

> 2018-07-20 12:56:15 127.0.0.1:59368 127.0.0.1:8999
EOF
> 2018-07-20 12:56:17 127.0.0.1:59376 127.0.0.1:8999
EOF
> 2018-07-20 12:56:19 127.0.0.1:59378 127.0.0.1:8999
EOF
...

在這個(gè)示例中,?Server?每處理完畢一條請(qǐng)求之后便關(guān)閉鏈接。?Client?在第一條請(qǐng)求發(fā)送完畢后,由于連接池的?IO?復(fù)用特性,下一次獲取到的將是同一個(gè)連接對(duì)象,由于?Server?鏈接已主動(dòng)關(guān)閉,第二次請(qǐng)求寫(xiě)入成功(其實(shí)并未成功發(fā)送到?Server?端,需要通過(guò)下一次的讀取操作才能檢測(cè)到鏈接錯(cuò)誤),但是讀取卻失敗了(?EOF?表示目標(biāo)連接關(guān)閉),因此這個(gè)時(shí)候?Client?執(zhí)行?Close?時(shí)將會(huì)銷(xiāo)毀該連接操作對(duì)象,而不是進(jìn)一步復(fù)用。下一次再通過(guò)?gtcp.NewPoolConn?獲得連接對(duì)象時(shí),?Client?將會(huì)與?Server?創(chuàng)建一個(gè)新的連接進(jìn)行數(shù)據(jù)通信。所以你看到?Client?的端口一直在變化,那是因?yàn)樵?gtcp.Conn?對(duì)象已經(jīng)是一個(gè)新的連接對(duì)象,之前的連接對(duì)象已經(jīng)被銷(xiāo)毀。

連接對(duì)象的?IO?復(fù)用涉及到十分微妙的連接狀態(tài)變化問(wèn)題,由于點(diǎn)對(duì)點(diǎn)網(wǎng)絡(luò)通信本身是比較復(fù)雜的環(huán)境,連接對(duì)象的狀態(tài)隨時(shí)可能被動(dòng)發(fā)生著變化,因此,在使用?gtcp?連接池特性時(shí),需要注意當(dāng)通信錯(cuò)誤產(chǎn)生時(shí)的連接對(duì)象重建機(jī)制,一旦產(chǎn)生錯(cuò)誤,立即丟棄(?Close?)該對(duì)象(?gtcp.PoolConn?)并重建(?gtcp.NewPoolConn?)。


以上內(nèi)容是否對(duì)您有幫助:
在線(xiàn)筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)