golang 通用连接池 高性能连接各种tcp连接,实例rabbitmq,mysql ,redis....
完整代码在https://github.com/human2312/tcp-pool
package Pool
// @Time : 2020年3月13日12:27:33
// @Author : Lemyhello
// @Desc: 通用连接池
import (
"errors"
"fmt"
"sync"
"time"
)
var (
//ErrMaxActiveConnReached 连接池超限
ErrMaxActiveConnReached = errors.New("MaxActiveConnReached")
)
// Config 连接池相关配置
type Config struct {
//连接池中拥有的最小连接数
InitialCap int
//最大并发存活连接数
MaxCap int
//最大空闲连接
MaxIdle int
//生成连接的方法
Factory func() (interface{}, error)
//关闭连接的方法
Close func(interface{}) error
//检查连接是否有效的方法
Ping func(interface{}) error
//连接最大空闲时间,超过该事件则将失效
IdleTimeout time.Duration
}
// channelPool 存放连接信息
type channelPool struct {
mu sync.RWMutex
conns chan *idleConn
factory func() (interface{}, error)
close func(interface{}) error
ping func(interface{}) error
idleTimeout, waitTimeOut time.Duration
maxActive int
openingConns int
}
type idleConn struct {
conn interface{}
t time.Time
}
var (
//ErrClosed 连接池已经关闭Error
ErrClosed = errors.New("pool is closed")
)
// Pool 基本方法
type Pool interface {
Get() (interface{}, error)
Put(interface{}) error
Close(interface{}) error
Release()
Len() int
}
// NewChannelPool 初始化连接
func NewChannelPool(poolConfig *Config) (Pool, error) {
if ! (poolConfig.InitialCap <= poolConfig.MaxIdle && poolConfig.MaxCap >= poolConfig.MaxIdle && poolConfig.InitialCap >= 0 ){
return nil, errors.New("invalid capacity settings")
}
if poolConfig.Factory == nil {
return nil, errors.New("invalid factory func settings")
}
if poolConfig.Close == nil {
return nil, errors.New("invalid close func settings")
}
c := &channelPool{
conns: make(chan *idleConn, poolConfig.MaxIdle),
factory: poolConfig.Factory,
close: poolConfig.Close,
idleTimeout: poolConfig.IdleTimeout,
maxActive: poolConfig.MaxCap,
openingConns: poolConfig.InitialCap,
}
if poolConfig.Ping != nil {
c.ping = poolConfig.Ping
}
for i := 0; i < poolConfig.InitialCap; i++ {
conn, err := c.factory()
if err != nil {
c.Release()
return nil, fmt.Errorf("factory is not able to fill the pool: %s", err)
}
c.conns <- &idleConn{conn: conn, t: time.Now()}
}
return c, nil
}
// getConns 获取所有连接
func (c *channelPool) getConns() chan *idleConn {
c.mu.Lock()
conns := c.conns
c.mu.Unlock()
return conns
}
// Get 从pool中取一个连接
func (c *channelPool) Get() (interface{}, error) {
conns := c.getConns()
if conns == nil {
return nil, ErrClosed
}
for {
select {
case wrapConn := <-conns:
if wrapConn == nil {
return nil, ErrClosed
}
//判断是否超时,超时则丢弃
if timeout := c.idleTimeout; timeout > 0 {
if wrapConn.t.Add(timeout).Before(time.Now()) {
//丢弃并关闭该连接
c.Close(wrapConn.conn)
continue
}
}
//判断是否失效,失效则丢弃,如果用户没有设定 ping 方法,就不检查
if c.ping != nil {
if err := c.Ping(wrapConn.conn); err != nil {
c.Close(wrapConn.conn)
continue
}
}
return wrapConn.conn, nil
default:
c.mu.Lock()
defer c.mu.Unlock()
if c.openingConns >= c.maxActive {
return nil, ErrMaxActiveConnReached
}
if c.factory == nil {
return nil, ErrClosed
}
conn, err := c.factory()
if err != nil {
return nil, err
}
c.openingConns++
return conn, nil
}
}
}
// Put 将连接放回pool中
func (c *channelPool) Put(conn interface{}) error {
if conn == nil {
return errors.New("connection is nil. rejecting")
}
c.mu.Lock()
if c.conns == nil {
c.mu.Unlock()
return c.Close(conn)
}
select {
case c.conns <- &idleConn{conn: conn, t: time.Now()}:
c.mu.Unlock()
return nil
default:
c.mu.Unlock()
//连接池已满,直接关闭该连接
return c.Close(conn)
}
}
// Close 关闭单条连接
func (c *channelPool) Close(conn interface{}) error {
if conn == nil {
return errors.New("connection is nil. rejecting")
}
c.mu.Lock()
defer c.mu.Unlock()
if c.close == nil {
return nil
}
c.openingConns--
return c.close(conn)
}
// Ping 检查单条连接是否有效
func (c *channelPool) Ping(conn interface{}) error {
if conn == nil {
return errors.New("connection is nil. rejecting")
}
return c.ping(conn)
}
// Release 释放连接池中所有连接
func (c *channelPool) Release() {
c.mu.Lock()
conns := c.conns
c.conns = nil
c.factory = nil
c.ping = nil
closeFun := c.close
c.close = nil
c.mu.Unlock()
if conns == nil {
return
}
close(conns)
for wrapConn := range conns {
//log.Printf("Type %v\n",reflect.TypeOf(wrapConn.conn))
closeFun(wrapConn.conn)
}
}
// Len 连接池中已有的连接
func (c *channelPool) Len() int {
return len(c.getConns())
}
main函数实例调试
package main
// @Time : 2020年3月13日19:35:35
// @Author : Lemyhello
// @Desc: 展示如何使用Pool连接池拿到tcp各种应用实例
import (
"fmt"
"github.com/streadway/amqp"
"net"
"time"
"tcp-pool/Pool"
)
var (
mqurl = "amqp://root:root@127.0.0.1:5672/test" //根据实际情况填写mq配置连接
mqPool Pool.Pool
)
func main() {
rabbitmq()
mysql()
redis()
//拿到一个连接
mq,_ := mqPool.Get()
//实例化对象
mqconn :=mq.(*amqp.Connection)
//将连接放回连接池中
defer mqPool.Put(mq)
//开始操作rabbitmq...
mqconn.Channel()
//do something....
}
//rabbitmq rabbitmq连接池
func rabbitmq() {
//factory 创建连接的方法
factory := func() (interface{}, error) { return amqp.Dial(mqurl) }
//close 关闭连接的方法
close := func(v interface{}) error { return v.(net.Conn).Close() }
//创建一个连接池: 初始化2,最大连接5,空闲连接数是4
poolConfig := &Pool.Config{
InitialCap: 2,
MaxIdle: 5,
MaxCap: 4,
Factory: factory,
Close: close,
//连接最大空闲时间,超过该时间的连接 将会关闭,可避免空闲时连接EOF,自动失效的问题
IdleTimeout: 15 * time.Second,
}
mqPool, _ = Pool.NewChannelPool(poolConfig)
//从连接池中取得一个连接
//v, err := p.Get()
//do something
//conn :=v.(*amqp.Connection)
//将连接放回连接池中
//p.Put(v)
//释放连接池中的所有连接
//p.Release()
//查看当前连接中的数量
current := mqPool.Len()
fmt.Println("len=", current)
return
}
//mysql mysql连接池
func mysql() {
}
//redis redis连接池
func redis() {
}
最后测试一下连接池能不能正常使用
完美。。。