github.com/gogf/gf@v1.16.9/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/gogf/gf.
     6  
     7  package gtcp
     8  
     9  import (
    10  	"time"
    11  
    12  	"github.com/gogf/gf/container/gmap"
    13  	"github.com/gogf/gf/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 (
    25  	defaultPoolExpire = 10 * time.Second // Default TTL for connection in the pool.
    26  	connStatusUnknown = 0                // Means it is unknown it's connective or not.
    27  	connStatusActive  = 1                // Means it is now connective.
    28  	connStatusError   = 2                // Means it should be closed and removed from pool.
    29  )
    30  
    31  var (
    32  	// addressPoolMap is a mapping for address to its pool object.
    33  	addressPoolMap = gmap.NewStrAnyMap(true)
    34  )
    35  
    36  // NewPoolConn creates and returns a connection with pool feature.
    37  func NewPoolConn(addr string, timeout ...time.Duration) (*PoolConn, error) {
    38  	v := addressPoolMap.GetOrSetFuncLock(addr, func() interface{} {
    39  		var pool *gpool.Pool
    40  		pool = gpool.New(defaultPoolExpire, func() (interface{}, error) {
    41  			if conn, err := NewConn(addr, timeout...); err == nil {
    42  				return &PoolConn{conn, pool, connStatusActive}, nil
    43  			} else {
    44  				return nil, err
    45  			}
    46  		})
    47  		return pool
    48  	})
    49  	if v, err := v.(*gpool.Pool).Get(); err == nil {
    50  		return v.(*PoolConn), nil
    51  	} else {
    52  		return nil, err
    53  	}
    54  }
    55  
    56  // Close puts back the connection to the pool if it's active,
    57  // or closes the connection if it's not active.
    58  //
    59  // Note that, if <c> calls Close function closing itself, <c> can not
    60  // be used again.
    61  func (c *PoolConn) Close() error {
    62  	if c.pool != nil && c.status == connStatusActive {
    63  		c.status = connStatusUnknown
    64  		c.pool.Put(c)
    65  	} else {
    66  		return c.Conn.Close()
    67  	}
    68  	return nil
    69  }
    70  
    71  // Send writes data to the connection. It retrieves a new connection from its pool if it fails
    72  // writing data.
    73  func (c *PoolConn) Send(data []byte, retry ...Retry) error {
    74  	err := c.Conn.Send(data, retry...)
    75  	if err != nil && c.status == connStatusUnknown {
    76  		if v, e := c.pool.Get(); e == nil {
    77  			c.Conn = v.(*PoolConn).Conn
    78  			err = c.Send(data, retry...)
    79  		} else {
    80  			err = e
    81  		}
    82  	}
    83  	if err != nil {
    84  		c.status = connStatusError
    85  	} else {
    86  		c.status = connStatusActive
    87  	}
    88  	return err
    89  }
    90  
    91  // Recv receives data from the connection.
    92  func (c *PoolConn) Recv(length int, retry ...Retry) ([]byte, error) {
    93  	data, err := c.Conn.Recv(length, retry...)
    94  	if err != nil {
    95  		c.status = connStatusError
    96  	} else {
    97  		c.status = connStatusActive
    98  	}
    99  	return data, err
   100  }
   101  
   102  // RecvLine reads data from the connection until reads char '\n'.
   103  // Note that the returned result does not contain the last char '\n'.
   104  func (c *PoolConn) RecvLine(retry ...Retry) ([]byte, error) {
   105  	data, err := c.Conn.RecvLine(retry...)
   106  	if err != nil {
   107  		c.status = connStatusError
   108  	} else {
   109  		c.status = connStatusActive
   110  	}
   111  	return data, err
   112  }
   113  
   114  // RecvTil reads data from the connection until reads bytes <til>.
   115  // Note that the returned result contains the last bytes <til>.
   116  func (c *PoolConn) RecvTil(til []byte, retry ...Retry) ([]byte, error) {
   117  	data, err := c.Conn.RecvTil(til, retry...)
   118  	if err != nil {
   119  		c.status = connStatusError
   120  	} else {
   121  		c.status = connStatusActive
   122  	}
   123  	return data, err
   124  }
   125  
   126  // RecvWithTimeout reads data from the connection with timeout.
   127  func (c *PoolConn) RecvWithTimeout(length int, timeout time.Duration, retry ...Retry) (data []byte, err error) {
   128  	if err := c.SetreceiveDeadline(time.Now().Add(timeout)); err != nil {
   129  		return nil, err
   130  	}
   131  	defer c.SetreceiveDeadline(time.Time{})
   132  	data, err = c.Recv(length, retry...)
   133  	return
   134  }
   135  
   136  // SendWithTimeout writes data to the connection with timeout.
   137  func (c *PoolConn) SendWithTimeout(data []byte, timeout time.Duration, retry ...Retry) (err error) {
   138  	if err := c.SetSendDeadline(time.Now().Add(timeout)); err != nil {
   139  		return err
   140  	}
   141  	defer c.SetSendDeadline(time.Time{})
   142  	err = c.Send(data, retry...)
   143  	return
   144  }
   145  
   146  // SendRecv writes data to the connection and blocks reading response.
   147  func (c *PoolConn) SendRecv(data []byte, receive int, retry ...Retry) ([]byte, error) {
   148  	if err := c.Send(data, retry...); err == nil {
   149  		return c.Recv(receive, retry...)
   150  	} else {
   151  		return nil, err
   152  	}
   153  }
   154  
   155  // SendRecvWithTimeout writes data to the connection and reads response with timeout.
   156  func (c *PoolConn) SendRecvWithTimeout(data []byte, receive int, timeout time.Duration, retry ...Retry) ([]byte, error) {
   157  	if err := c.Send(data, retry...); err == nil {
   158  		return c.RecvWithTimeout(receive, timeout, retry...)
   159  	} else {
   160  		return nil, err
   161  	}
   162  }