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  }