github.com/wangyougui/gf/v2@v2.6.5/net/gtcp/gtcp_pool.go (about) 1 // Copyright GoFrame Author(https://goframe.org). All Rights Reserved. 2 // 3 // This Source Code Form is subject to the terms of the MIT License. 4 // If a copy of the MIT was not distributed with this file, 5 // You can obtain one at https://github.com/wangyougui/gf. 6 7 package gtcp 8 9 import ( 10 "time" 11 12 "github.com/wangyougui/gf/v2/container/gmap" 13 "github.com/wangyougui/gf/v2/container/gpool" 14 ) 15 16 // PoolConn is a connection with pool feature for TCP. 17 // Note that it is NOT a pool or connection manager, it is just a TCP connection object. 18 type PoolConn struct { 19 *Conn // Underlying connection object. 20 pool *gpool.Pool // Connection pool, which is not a real connection pool, but a connection reusable pool. 21 status int // Status of current connection, which is used to mark this connection usable or not. 22 } 23 24 const defaultPoolExpire = 10 * time.Second // Default TTL for connection in the pool. 25 26 const ( 27 connStatusUnknown = iota // Means it is unknown it's connective or not. 28 connStatusActive // Means it is now connective. 29 connStatusError // Means it should be closed and removed from pool. 30 ) 31 32 var ( 33 // addressPoolMap is a mapping for address to its pool object. 34 addressPoolMap = gmap.NewStrAnyMap(true) 35 ) 36 37 // NewPoolConn creates and returns a connection with pool feature. 38 func NewPoolConn(addr string, timeout ...time.Duration) (*PoolConn, error) { 39 v := addressPoolMap.GetOrSetFuncLock(addr, func() interface{} { 40 var pool *gpool.Pool 41 pool = gpool.New(defaultPoolExpire, func() (interface{}, error) { 42 if conn, err := NewConn(addr, timeout...); err == nil { 43 return &PoolConn{conn, pool, connStatusActive}, nil 44 } else { 45 return nil, err 46 } 47 }) 48 return pool 49 }) 50 value, err := v.(*gpool.Pool).Get() 51 if err != nil { 52 return nil, err 53 } 54 return value.(*PoolConn), nil 55 } 56 57 // Close puts back the connection to the pool if it's active, 58 // or closes the connection if it's not active. 59 // 60 // Note that, if `c` calls Close function closing itself, `c` can not 61 // be used again. 62 func (c *PoolConn) Close() error { 63 if c.pool != nil && c.status == connStatusActive { 64 c.status = connStatusUnknown 65 return c.pool.Put(c) 66 } 67 return c.Conn.Close() 68 } 69 70 // Send writes data to the connection. It retrieves a new connection from its pool if it fails 71 // writing data. 72 func (c *PoolConn) Send(data []byte, retry ...Retry) error { 73 err := c.Conn.Send(data, retry...) 74 if err != nil && c.status == connStatusUnknown { 75 if v, e := c.pool.Get(); e == nil { 76 c.Conn = v.(*PoolConn).Conn 77 err = c.Send(data, retry...) 78 } else { 79 err = e 80 } 81 } 82 if err != nil { 83 c.status = connStatusError 84 } else { 85 c.status = connStatusActive 86 } 87 return err 88 } 89 90 // Recv receives data from the connection. 91 func (c *PoolConn) Recv(length int, retry ...Retry) ([]byte, error) { 92 data, err := c.Conn.Recv(length, retry...) 93 if err != nil { 94 c.status = connStatusError 95 } else { 96 c.status = connStatusActive 97 } 98 return data, err 99 } 100 101 // RecvLine reads data from the connection until reads char '\n'. 102 // Note that the returned result does not contain the last char '\n'. 103 func (c *PoolConn) RecvLine(retry ...Retry) ([]byte, error) { 104 data, err := c.Conn.RecvLine(retry...) 105 if err != nil { 106 c.status = connStatusError 107 } else { 108 c.status = connStatusActive 109 } 110 return data, err 111 } 112 113 // RecvTill reads data from the connection until reads bytes `til`. 114 // Note that the returned result contains the last bytes `til`. 115 func (c *PoolConn) RecvTill(til []byte, retry ...Retry) ([]byte, error) { 116 data, err := c.Conn.RecvTill(til, retry...) 117 if err != nil { 118 c.status = connStatusError 119 } else { 120 c.status = connStatusActive 121 } 122 return data, err 123 } 124 125 // RecvWithTimeout reads data from the connection with timeout. 126 func (c *PoolConn) RecvWithTimeout(length int, timeout time.Duration, retry ...Retry) (data []byte, err error) { 127 if err := c.SetDeadlineRecv(time.Now().Add(timeout)); err != nil { 128 return nil, err 129 } 130 defer func() { 131 _ = c.SetDeadlineRecv(time.Time{}) 132 }() 133 data, err = c.Recv(length, retry...) 134 return 135 } 136 137 // SendWithTimeout writes data to the connection with timeout. 138 func (c *PoolConn) SendWithTimeout(data []byte, timeout time.Duration, retry ...Retry) (err error) { 139 if err := c.SetDeadlineSend(time.Now().Add(timeout)); err != nil { 140 return err 141 } 142 defer func() { 143 _ = c.SetDeadlineSend(time.Time{}) 144 }() 145 err = c.Send(data, retry...) 146 return 147 } 148 149 // SendRecv writes data to the connection and blocks reading response. 150 func (c *PoolConn) SendRecv(data []byte, receive int, retry ...Retry) ([]byte, error) { 151 if err := c.Send(data, retry...); err == nil { 152 return c.Recv(receive, retry...) 153 } else { 154 return nil, err 155 } 156 } 157 158 // SendRecvWithTimeout writes data to the connection and reads response with timeout. 159 func (c *PoolConn) SendRecvWithTimeout(data []byte, receive int, timeout time.Duration, retry ...Retry) ([]byte, error) { 160 if err := c.Send(data, retry...); err == nil { 161 return c.RecvWithTimeout(receive, timeout, retry...) 162 } else { 163 return nil, err 164 } 165 }