golang 通用连接池 高性能连接各种tcp连接,实例rabbitmq,mysql ,redis....

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()  {

}

 

最后测试一下连接池能不能正常使用

 

 

完美。。。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值