温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

golang中怎么利用channel 实现一个连接池

发布时间:2021-07-06 15:13:30 来源:亿速云 阅读:218 作者:Leah 栏目:大数据

这篇文章给大家介绍golang中怎么利用channel 实现一个连接池,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。

何为通用?

连接池的实现不依赖具体的实例,而依赖某个接口,本文的连接池选用的是io.Closer接口,只要是实现了该接口的对象都可以被池管理。
当然,你可以实现基于interface{}的连接池,这样任何对象都可以被管理。

实现原理

将连接句柄存入channel中,由于缓存channel的特性,获取连接时如果池中有连接,将直接返回,如果池中没有连接,将阻塞或者新建连接(没超过最大限制的情况下)。
由于面向接口编程,所有创建连接的逻辑是不清楚的,这里需要传入一个函数,该函数返回一个io.Closer对象。

实现

由于并发问题,在需要操作池中互斥数据的时候需要加锁。

package pool

import (
   "errors"
   "io"
   "sync"
   "time"
)

var (
   ErrInvalidConfig = errors.New("invalid pool config")
   ErrPoolClosed    = errors.New("pool closed")
)

type factory func() (io.Closer, error)

type Pool interface {
   Acquire() (io.Closer, error) // 获取资源
   Release(io.Closer) error     // 释放资源
   Close(io.Closer) error       // 关闭资源
   Shutdown() error             // 关闭池
}

type GenericPool struct {
   sync.Mutex
   pool        chan io.Closer
   maxOpen     int  // 池中最大资源数
   numOpen     int  // 当前池中资源数
   minOpen     int  // 池中最少资源数
   closed      bool // 池是否已关闭
   maxLifetime time.Duration
   factory     factory // 创建连接的方法
}

func NewGenericPool(minOpen, maxOpen int, maxLifetime time.Duration, factory factory) (*GenericPool, error) {
   if maxOpen <= 0 || minOpen > maxOpen {
       return nil, ErrInvalidConfig
   }
   p := &GenericPool{
       maxOpen:     maxOpen,
       minOpen:     minOpen,
       maxLifetime: maxLifetime,
       factory:     factory,
       pool:        make(chan io.Closer, maxOpen),
   }

   for i := 0; i < minOpen; i++ {
       closer, err := factory()
       if err != nil {
           continue
       }
       p.numOpen++
       p.pool <- closer
   }
   return p, nil
}

func (p *GenericPool) Acquire() (io.Closer, error) {
   if p.closed {
       return nil, ErrPoolClosed
   }
   for {
       closer, err := p.getOrCreate()
       if err != nil {
           return nil, err
       }
       // todo maxLifttime处理
       return closer, nil
   }
}

func (p *GenericPool) getOrCreate() (io.Closer, error) {
   select {
   case closer := <-p.pool:
       return closer, nil
   default:
   }
   p.Lock()
   if p.numOpen >= p.maxOpen {
       closer := <-p.pool
       p.Unlock()
       return closer, nil
   }
   // 新建连接
   closer, err := p.factory()
   if err != nil {
       p.Unlock()
       return nil, err
   }
   p.numOpen++
   p.Unlock()
   return closer, nil
}

// 释放单个资源到连接池
func (p *GenericPool) Release(closer io.Closer) error {
   if p.closed {
       return ErrPoolClosed
   }
   p.Lock()
   p.pool <- closer
   p.Unlock()
   return nil
}

// 关闭单个资源
func (p *GenericPool) Close(closer io.Closer) error {
   p.Lock()
   closer.Close()
   p.numOpen--
   p.Unlock()
   return nil
}

// 关闭连接池,释放所有资源
func (p *GenericPool) Shutdown() error {
   if p.closed {
       return ErrPoolClosed
   }
   p.Lock()
   close(p.pool)
   for closer := range p.pool {
       closer.Close()
       p.numOpen--
   }
   p.closed = true
   p.Unlock()
   return nil
}

关于golang中怎么利用channel 实现一个连接池就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI