github.com/qxnw/lib4go@v0.0.0-20180426074627-c80c7e84b925/pool/pool.go (about)

     1  package pool
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"sync"
     7  	"time"
     8  )
     9  
    10  //pool 存放链接信息
    11  type pool struct {
    12  	mu          sync.Mutex
    13  	conns       chan *idleConn
    14  	factory     func() (interface{}, error)
    15  	close       func(interface{}) error
    16  	done        bool
    17  	idleTimeout time.Duration
    18  }
    19  
    20  type idleConn struct {
    21  	conn interface{}
    22  	t    time.Time
    23  }
    24  
    25  //Len 连接池中已有的连接
    26  func (c *pool) Len() int {
    27  	return len(c.getConns())
    28  }
    29  
    30  var (
    31  	//ErrClosed 连接池已经关闭Error
    32  	ErrClosed = errors.New("pool is closed")
    33  
    34  	// AutoReleaseTime 定时自动清除
    35  	TimeOut time.Duration
    36  )
    37  
    38  //IPool 基本方法
    39  type IPool interface {
    40  	Get() (interface{}, error)
    41  
    42  	Put(interface{}) error
    43  
    44  	Close(interface{}) error
    45  
    46  	Release()
    47  
    48  	Len() int
    49  
    50  	AutoReleaseStart()
    51  }
    52  
    53  //PoolConfigOptions 连接池相关配置
    54  type PoolConfigOptions struct {
    55  	//连接池中拥有的最小连接数
    56  	InitialCap int
    57  	//连接池中拥有的最大的连接数
    58  	MaxCap int
    59  	//生成连接的方法
    60  	Factory func() (interface{}, error)
    61  	//关闭链接的方法
    62  	Close func(interface{}) error
    63  	//链接最大空闲时间,超过该时间则将失效
    64  	IdleTimeout time.Duration
    65  }
    66  
    67  //New 初始化链接
    68  func New(config *PoolConfigOptions) (IPool, error) {
    69  	if config.InitialCap < 0 || config.MaxCap <= 0 || config.InitialCap > config.MaxCap {
    70  		return nil, errors.New("invalid capacity settings")
    71  	}
    72  
    73  	/*add by champly 2016年12月12日14:06:14*/
    74  	if config.Factory == nil || config.Close == nil {
    75  		return nil, errors.New("invalid function settings")
    76  	}
    77  
    78  	// 添加设置自动清理超时连接
    79  	TimeOut = config.IdleTimeout
    80  	if TimeOut == 0 {
    81  		TimeOut = time.Hour * 30
    82  	}
    83  	/*end*/
    84  
    85  	c := &pool{
    86  		conns:       make(chan *idleConn, config.MaxCap),
    87  		factory:     config.Factory,
    88  		close:       config.Close,
    89  		idleTimeout: config.IdleTimeout,
    90  	}
    91  	for i := 0; i < config.InitialCap; i++ {
    92  		conn, err := c.factory()
    93  		if err != nil {
    94  			c.Release()
    95  			return nil, fmt.Errorf("factory is not able to fill the pool: %s", err)
    96  		}
    97  		c.conns <- &idleConn{conn: conn, t: time.Now()}
    98  	}
    99  
   100  	return c, nil
   101  }
   102  
   103  //getConns 获取所有连接
   104  func (c *pool) getConns() chan *idleConn {
   105  	c.mu.Lock()
   106  	conns := c.conns
   107  	c.mu.Unlock()
   108  	return conns
   109  }
   110  
   111  //Get 从pool中取一个连接
   112  func (c *pool) Get() (interface{}, error) {
   113  	conns := c.getConns()
   114  	if conns == nil || c.done {
   115  		return nil, ErrClosed
   116  	}
   117  	for {
   118  		select {
   119  		case wrapConn := <-conns:
   120  			if wrapConn == nil {
   121  				return nil, ErrClosed
   122  			}
   123  			// 判断是否超时,超时则丢弃
   124  			if timeout := c.idleTimeout; timeout > 0 {
   125  				if wrapConn.t.Add(timeout).Before(time.Now()) {
   126  					//丢弃并关闭该链接
   127  					c.Close(wrapConn.conn)
   128  					continue
   129  				}
   130  			}
   131  
   132  			return wrapConn.conn, nil
   133  		default:
   134  			conn, err := c.factory()
   135  			if err != nil {
   136  				return nil, err
   137  			}
   138  
   139  			return conn, nil
   140  		}
   141  	}
   142  }
   143  
   144  //Put 将连接放回pool中
   145  func (c *pool) Put(conn interface{}) error {
   146  	if conn == nil {
   147  		return errors.New("connection is nil. rejecting")
   148  	}
   149  
   150  	c.mu.Lock()
   151  	defer c.mu.Unlock()
   152  
   153  	if c.conns == nil || c.done {
   154  		return c.Close(conn)
   155  	}
   156  
   157  	select {
   158  	case c.conns <- &idleConn{conn: conn, t: time.Now()}:
   159  		return nil
   160  	default:
   161  		return c.Close(conn)
   162  	}
   163  }
   164  
   165  //Close 关闭单条连接
   166  func (c *pool) Close(conn interface{}) error {
   167  	if conn == nil {
   168  		return errors.New("connection is nil. rejecting")
   169  	}
   170  	return c.close(conn)
   171  }
   172  
   173  //Release 释放连接池中所有链接
   174  func (c *pool) Release() {
   175  	c.mu.Lock()
   176  	conns := c.conns
   177  	c.conns = nil
   178  	c.factory = nil
   179  	closeFun := c.close
   180  	c.close = nil
   181  	c.mu.Unlock()
   182  
   183  	if conns == nil {
   184  		return
   185  	}
   186  
   187  	close(conns)
   188  	for wrapConn := range conns {
   189  		closeFun(wrapConn.conn)
   190  	}
   191  }
   192  
   193  /*add by champly 2016年12月12日16:24:35*/
   194  // AutoReleaseTimeout 自动清除超时的连接
   195  func (c *pool) AutoReleaseStart() {
   196  	go func() {
   197  		for {
   198  			select {
   199  			case <-time.After(TimeOut):
   200  				c.clear()
   201  				if c.done {
   202  					return
   203  				}
   204  			}
   205  		}
   206  	}()
   207  }
   208  
   209  func (c *pool) clear() {
   210  	start := time.Now()
   211  	if c.Len() > 0 {
   212  		c.mu.Lock()
   213  		conns := c.conns
   214  		defer c.mu.Unlock()
   215  		if conns == nil {
   216  			return
   217  		}
   218  
   219  		fmt.Println("pool中总连接数:", len(c.conns))
   220  		length := len(c.conns)
   221  		for i := 0; i < length; i++ {
   222  			wrapConn, ok := <-conns
   223  			if ok {
   224  				if wrapConn == nil {
   225  					return
   226  				}
   227  				// 如果超时
   228  				if timeout := c.idleTimeout; timeout > 0 {
   229  					if wrapConn.t.Add(timeout).Before(time.Now()) {
   230  						c.Close(wrapConn.conn)
   231  						continue
   232  					}
   233  				}
   234  				// 如果没有超时, 写回idleConn
   235  				c.conns <- &idleConn{conn: wrapConn.conn, t: time.Now()}
   236  			}
   237  		}
   238  	}
   239  
   240  	fmt.Println("总共耗时:", time.Now().Sub(start), " 现在连接数:", len(c.conns))
   241  }
   242  
   243  /*end*/