├── pool.go ├── README.md └── channel.go /pool.go: -------------------------------------------------------------------------------- 1 | package pool 2 | 3 | import "errors" 4 | 5 | var ( 6 | ErrClosed = errors.New("pool is closed") 7 | ) 8 | 9 | type Pool interface { 10 | Get() (interface{}, error) 11 | 12 | Put(interface{}) error 13 | 14 | Close(interface{}) error 15 | 16 | Release() 17 | 18 | Len() int 19 | } 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pool 2 | 3 | common connnect pool, difference with [fatih/pool](github.com/fatih/pool) 4 | 5 | * More adaptation 6 | * Add MaxIdle threshold 7 | * ActiveCheck() 8 | * CurConnCount Lock 9 | 10 | some code from https://github.com/silenceper/pool 11 | 12 | ## Usage 13 | 14 | ``` 15 | factory := func() (interface{}, error) { return net.Dial("tcp", "127.0.0.1:4000") } 16 | 17 | close := func(v interface{}) error { return v.(net.Conn).Close() } 18 | 19 | //ping := func(v interface{}) error { return nil } 20 | 21 | poolConfig := &pool.PoolConfig{ 22 | InitialCap: 5, 23 | MaxActive: 200, // max open conn 24 | MaxIdle: 50, // after idle timeout expired, keep idle conn 25 | Factory: factory, 26 | Close: close, 27 | 28 | IdleTimeout: 30 * time.Second, 29 | CheckInterval: 10 * time.Second, 30 | } 31 | p, err := pool.NewChannelPool(poolConfig) 32 | if err != nil { 33 | fmt.Println("err=", err) 34 | } 35 | 36 | v, err := p.Get() 37 | 38 | //conn=v.(net.Conn) 39 | 40 | p.Put(v) 41 | 42 | p.Release() 43 | 44 | current := p.Len() 45 | ``` 46 | -------------------------------------------------------------------------------- /channel.go: -------------------------------------------------------------------------------- 1 | package pool 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | type PoolConfig struct { 11 | InitialCap int 12 | MaxActive int 13 | MaxIdle int 14 | 15 | Factory func() (interface{}, error) 16 | Close func(interface{}) error 17 | Ping func(interface{}) error 18 | IdleTimeout time.Duration 19 | CheckInterval time.Duration 20 | } 21 | 22 | type channelPool struct { 23 | mu sync.Mutex 24 | conns chan *idleConn 25 | maxActive int 26 | maxIdle int 27 | curConnCount int 28 | factory func() (interface{}, error) 29 | close func(interface{}) error 30 | ping func(interface{}) error 31 | idleTimeout time.Duration 32 | checkInterval time.Duration 33 | } 34 | 35 | type idleConn struct { 36 | conn interface{} 37 | t time.Time 38 | } 39 | 40 | //NewChannelPool init connect pool 41 | func NewChannelPool(poolConfig *PoolConfig) (Pool, error) { 42 | if poolConfig.InitialCap < 0 || poolConfig.MaxActive <= 0 || poolConfig.InitialCap > poolConfig.MaxActive { 43 | return nil, errors.New("invalid capacity settings") 44 | } 45 | if poolConfig.Factory == nil { 46 | return nil, errors.New("invalid factory func settings") 47 | } 48 | if poolConfig.Close == nil { 49 | return nil, errors.New("invalid close func settings") 50 | } 51 | 52 | c := &channelPool{ 53 | conns: make(chan *idleConn, poolConfig.MaxActive), 54 | factory: poolConfig.Factory, 55 | close: poolConfig.Close, 56 | maxActive: poolConfig.MaxActive, 57 | maxIdle: poolConfig.MaxIdle, 58 | idleTimeout: poolConfig.IdleTimeout, 59 | checkInterval: poolConfig.CheckInterval, 60 | } 61 | 62 | if poolConfig.Ping != nil { 63 | c.ping = poolConfig.Ping 64 | } 65 | 66 | for i := 0; i < poolConfig.InitialCap; i++ { 67 | conn, err := c.factory() 68 | if err != nil { 69 | c.Release() 70 | return nil, fmt.Errorf("factory is not able to fill the pool: %s", err) 71 | } 72 | c.curConnCount++ 73 | c.conns <- &idleConn{conn: conn, t: time.Now()} 74 | } 75 | if c.checkInterval > 0 { 76 | go c.Check() 77 | } 78 | 79 | return c, nil 80 | } 81 | 82 | //getConns conn channel 83 | func (c *channelPool) getConns() chan *idleConn { 84 | c.mu.Lock() 85 | conns := c.conns 86 | c.mu.Unlock() 87 | return conns 88 | } 89 | 90 | // cur counter -- 91 | func (c *channelPool) decrCurCount() { 92 | c.mu.Lock() 93 | c.curConnCount-- 94 | c.mu.Unlock() 95 | } 96 | 97 | // cur counter ++ 98 | func (c *channelPool) incrCurCount() { 99 | c.mu.Lock() 100 | c.curConnCount++ 101 | c.mu.Unlock() 102 | } 103 | 104 | func (c *channelPool) Check() { 105 | if c.idleTimeout == 0 { 106 | return 107 | } 108 | 109 | judgeTimeout := func() { 110 | if c.conns == nil { 111 | return 112 | } 113 | 114 | for { 115 | select { 116 | case wrapConn := <-c.conns: 117 | if wrapConn == nil { 118 | break 119 | } 120 | 121 | c.mu.Lock() 122 | if c.curConnCount > c.maxIdle { 123 | killStats := wrapConn.t.Add(c.idleTimeout).Before(time.Now()) 124 | if killStats { 125 | c.Close(wrapConn.conn) 126 | c.curConnCount-- 127 | c.mu.Unlock() 128 | continue 129 | } 130 | } 131 | c.mu.Unlock() 132 | c.conns <- wrapConn 133 | default: 134 | return 135 | } 136 | } 137 | } 138 | 139 | for { 140 | time.Sleep(c.checkInterval) 141 | judgeTimeout() 142 | } 143 | } 144 | 145 | //Get get conn in pool 146 | func (c *channelPool) Get() (interface{}, error) { 147 | if c.conns == nil { 148 | return nil, ErrClosed 149 | } 150 | 151 | for { 152 | select { 153 | case wrapConn := <-c.conns: 154 | if wrapConn == nil { 155 | return nil, ErrClosed 156 | } 157 | if timeout := c.idleTimeout; timeout > 0 { 158 | if wrapConn.t.Add(timeout).Before(time.Now()) { 159 | c.Close(wrapConn.conn) 160 | c.decrCurCount() 161 | continue 162 | } 163 | } 164 | 165 | if c.ping != nil { 166 | if err := c.Ping(wrapConn.conn); err != nil { 167 | continue 168 | } 169 | } 170 | return wrapConn.conn, nil 171 | 172 | default: 173 | c.incrCurCount() 174 | if c.curConnCount > c.maxActive { 175 | c.decrCurCount() 176 | 177 | time.Sleep(20 * time.Millisecond) 178 | continue 179 | } 180 | conn, err := c.factory() 181 | if err != nil { 182 | c.decrCurCount() 183 | return nil, err 184 | } 185 | 186 | return conn, nil 187 | } 188 | } 189 | } 190 | 191 | //Put put the connect to pool 192 | func (c *channelPool) Put(conn interface{}) error { 193 | if conn == nil { 194 | return errors.New("connection is nil. rejecting") 195 | } 196 | 197 | c.mu.Lock() 198 | defer c.mu.Unlock() 199 | 200 | if c.conns == nil { 201 | c.curConnCount-- 202 | return c.Close(conn) 203 | } 204 | 205 | select { 206 | case c.conns <- &idleConn{conn: conn, t: time.Now()}: 207 | return nil 208 | default: 209 | // connect pool is full, close the conn 210 | c.curConnCount-- 211 | return c.Close(conn) 212 | } 213 | } 214 | 215 | //Close close a connect 216 | func (c *channelPool) Close(conn interface{}) error { 217 | if conn == nil { 218 | return errors.New("connection is nil. rejecting") 219 | } 220 | return c.close(conn) 221 | } 222 | 223 | //Ping try check connect 224 | func (c *channelPool) Ping(conn interface{}) error { 225 | if conn == nil { 226 | return errors.New("connection is nil. rejecting") 227 | } 228 | return c.ping(conn) 229 | } 230 | 231 | //Release release all conn in pool 232 | func (c *channelPool) Release() { 233 | c.mu.Lock() 234 | conns := c.conns 235 | c.conns = nil 236 | c.factory = nil 237 | closeFun := c.close 238 | c.close = nil 239 | c.mu.Unlock() 240 | 241 | if conns == nil { 242 | return 243 | } 244 | 245 | close(conns) 246 | for wrapConn := range conns { 247 | closeFun(wrapConn.conn) 248 | } 249 | } 250 | 251 | //Len conns's count in pool 252 | func (c *channelPool) Len() int { 253 | return len(c.getConns()) 254 | } 255 | 256 | //GetCurCount all conns count, contains not return pool 257 | func (c *channelPool) GetCurCount() int { 258 | return c.curConnCount 259 | } 260 | --------------------------------------------------------------------------------