channel.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. package nsqclient
  2. import (
  3. "errors"
  4. "fmt"
  5. "sync"
  6. nsq "github.com/nsqio/go-nsq"
  7. )
  8. // channelPool implements the Pool interface based on buffered channels.
  9. type channelPool struct {
  10. // storage for our net.Conn connections
  11. mu sync.Mutex
  12. conns chan *nsq.Producer
  13. // net.Conn generator
  14. factory Factory
  15. }
  16. // Factory is a function to create new connections.
  17. type Factory func() (*nsq.Producer, error)
  18. // NewChannelPool returns a new pool based on buffered channels with an initial
  19. // capacity and maximum capacity. Factory is used when initial capacity is
  20. // greater than zero to fill the pool. A zero initialCap doesn't fill the Pool
  21. // until a new Get() is called. During a Get(), If there is no new connection
  22. // available in the pool, a new connection will be created via the Factory()
  23. // method.
  24. func NewChannelPool(initialCap, maxCap int, factory Factory) (Pool, error) {
  25. if initialCap < 0 || maxCap <= 0 || initialCap > maxCap {
  26. return nil, errors.New("invalid capacity settings")
  27. }
  28. c := &channelPool{
  29. conns: make(chan *nsq.Producer, maxCap),
  30. factory: factory,
  31. }
  32. // create initial connections, if something goes wrong,
  33. // just close the pool error out.
  34. for i := 0; i < initialCap; i++ {
  35. conn, err := factory()
  36. if err != nil {
  37. c.Close()
  38. return nil, fmt.Errorf("factory is not able to fill the pool: %s", err)
  39. }
  40. c.conns <- conn
  41. }
  42. return c, nil
  43. }
  44. func (c *channelPool) getConns() chan *nsq.Producer {
  45. c.mu.Lock()
  46. conns := c.conns
  47. c.mu.Unlock()
  48. return conns
  49. }
  50. // Get implements the Pool interfaces Get() method. If there is no new
  51. // connection available in the pool, a new connection will be created via the
  52. // Factory() method.
  53. func (c *channelPool) Get() (*PoolConn, error) {
  54. conns := c.getConns()
  55. if conns == nil {
  56. return nil, ErrClosed
  57. }
  58. // wrap our connections with out custom net.Conn implementation (wrapConn
  59. // method) that puts the connection back to the pool if it's closed.
  60. select {
  61. case conn := <-conns:
  62. if conn == nil {
  63. return nil, ErrClosed
  64. }
  65. return c.wrapConn(conn), nil
  66. default:
  67. conn, err := c.factory()
  68. if err != nil {
  69. return nil, err
  70. }
  71. return c.wrapConn(conn), nil
  72. }
  73. }
  74. // put puts the connection back to the pool. If the pool is full or closed,
  75. // conn is simply closed. A nil conn will be rejected.
  76. func (c *channelPool) put(conn *nsq.Producer) error {
  77. if conn == nil {
  78. return errors.New("connection is nil. rejecting")
  79. }
  80. c.mu.Lock()
  81. defer c.mu.Unlock()
  82. if c.conns == nil {
  83. // pool is closed, close passed connection
  84. conn.Stop()
  85. return nil
  86. }
  87. // put the resource back into the pool. If the pool is full, this will
  88. // block and the default case will be executed.
  89. select {
  90. case c.conns <- conn:
  91. return nil
  92. default:
  93. // pool is full, close passed connection
  94. conn.Stop()
  95. return nil
  96. }
  97. }
  98. func (c *channelPool) Close() {
  99. c.mu.Lock()
  100. conns := c.conns
  101. c.conns = nil
  102. c.factory = nil
  103. c.mu.Unlock()
  104. if conns == nil {
  105. return
  106. }
  107. close(conns)
  108. for conn := range conns {
  109. conn.Stop()
  110. }
  111. }
  112. func (c *channelPool) Len() int { return len(c.getConns()) }