使用Go语言实现一个简单的无界资源池

 更新时间:2024年05月06日 10:02:07   作者:shark_chili  
本文我们希望通过go语言实现一个简单的资源池,而这个资源池的资源包括但不限于数据库连接池,线程池,协程池,网络连接池,只要这些资源实现我们指定的关闭方法,则都可以通过我们封装的资源池进行统一管理,文中通过代码示例给大家介绍的非常详细,需要的朋友可以参考下

写在文章开头

我们希望通过go语言实现一个简单的资源池,而这个资源池的资源包括但不限于:

  • 数据库连接池
  • 线程池
  • 协程池
  • 网络连接池

只要这些资源实现我们指定的关闭方法,则都可以通过我们封装的资源池进行统一管理,需要简单说明一下这个资源池的要求:

  • 需要用户指定资源以及资源的创建方法。
  • 当协程通过Acquire方法获取资源时,若发现当前池中有资源可以分配则直接返回,若没有足够的资源则基于传入的创建方法创建一个全新的资源分配。
  • 支持资源释放和资源池关闭。

听起来很像是Java的无界线程池,接下来我们就基于这个需求实现一个版本。

需求落地

给出资源池结构

我们首先需要给出资源池的结构,很明显作为一个资源池它需要有一个管理资源池的channel,为了保证多协程竞争资源的协程安全,我们还需要通过一把Mutex完成操作互斥,同时给出创建资源的工厂方法要求这个工厂方法创建的资源具备资源关闭能力:

// Pool 定义一个结构体 包含重量级锁 有缓冲区Chanel 工厂方法 连接池关闭状态
type Pool struct {
 m        sync.Mutex
 resource chan io.Closer
 factory  func() (io.Closer, error)
 closed   bool
}

创建资源池

有个上述的定义之后,我们的创建方法就很容易实现了,只需基于外部的size和工厂方法完成Pool成员变量初始化即可:

var ErrPoolClosed = errors.New("连接池已关闭")


func New(fn func() (io.Closer, error), size uint) (*Pool, error) {
 //判断size大小是否合法
 if size <= 0 {
  return nil, errors.New("size不合法")
 }
 //基于工厂方法和size创建资源池
 return &Pool{
  resource: make(chan io.Closer, size),
  factory:  fn,
 }, nil

}

获取资源

当协程需要获取资源时,会查看当前缓冲通道是否有足够的资源,如果有则在正确运行的情况下返回出去,反之基于我们上文传入的工厂方法完成资源创建并返回:

func (p *Pool) Acquire() (io.Closer, error) {
 select {
 //如果channel有足够的资源分配则直接返回
 case r, ok := <-p.resource:
  if !ok {
   log.Println("连接池已关闭")
   return nil, ErrPoolClosed
  }
  log.Println("拿到连接池共享资源")
  return r, nil
  //基于工厂方法创建全新的资源返回出去
 default:
  log.Println("资源不足,创建新的连接资源")
  return p.factory()
 }

}

释放与关闭

这里我们将资源的释放和关闭放在一起说明,在进行资源释放和关闭时我们需要考虑3个问题即:

  • 已关闭的资源池无需归还资源。
  • 正在关闭资源池时不可归还资源。
  • 正在归还资源时不可关闭资源池。

所以进行这两个操作时,我们需要通过互斥锁确保两个操作互斥:

// Release 上锁 设置方法退出后解锁 查看当前连接池是否已关闭,若关闭则直接将资源关闭 ,反之select查看能否将其存入缓冲区,若可以输出入队成功,反之输出队列已满
func (p *Pool) Release(r io.Closer) {
 //上锁确保关闭和归还资源操作互斥
 p.m.Lock()
 //函数退出时解锁
 defer p.m.Unlock()
 //如果资源池关闭则直接将当前资源关闭销毁
 if p.closed {
  log.Println("连接池已关闭,直接销毁当前资源")
  r.Close()
 }
 //将连接归还,如果满了则直接关闭销毁
 select {
 case p.resource <- r:
  log.Println("连接归还成功")
 default:
  log.Println("连接池已满,资源直接销毁")
  r.Close()
 }

}

// Close 方法 上锁 设置方法退出后解锁 遍历所有资源将其关闭 然后再关闭连接池
func (p *Pool) Close() {
 p.m.Lock()

 defer p.m.Unlock()

 if p.closed {
  log.Println("连接池已关闭,直接销毁当前资源")
  return
 }
 //设置为关闭
 p.closed = true
 //关闭资源
 close(p.resource)
 //遍历资源池资源
 for r := range p.resource {
  r.Close()
 }

}

测试代码与输出

最后我们给出测试代码,可以看到我们基于资源池工具类模拟数据库连接池的管理:

//设置最大协程数与资源池数为24
const maxGoroutines = 24
const poolResources = 24

//创建可关闭的数据库连接
type dbConnection struct {
 ID int32
}
//对应的关闭方法
func (d *dbConnection) Close() error {
 log.Println("当前数据库连接", d.ID, "已关闭")
 return nil
}

var idCounter int32

func createConnection() (io.Closer, error) {
 id := atomic.AddInt32(&idCounter, 1)
 return &dbConnection{ID: id}, nil
}

func main() {
 //创建maxGoroutines个WaitGroup
 var wg sync.WaitGroup
 wg.Add(maxGoroutines)
 //传入createConnection方法和连接池大小poolResources创建数据库连接池
 p, err := pool.New(createConnection, poolResources)
 if err != nil {
  log.Println(err)
 }
 //创建24个协程获取资源
 for i := 0; i < maxGoroutines; i++ {
  go func(queryParam int) {
   queryData(queryParam, p)
   defer wg.Done()
  }(i)
 }
 //等待操作完成关闭连接池
 wg.Wait()
 log.Println("查询完成")
 p.Close()

}
//queryData 基于连接池Acquire获取资源,完成后通过Release归还资源
func queryData(queryParam int, p *pool.Pool) {

 r, e := p.Acquire()
 if e != nil {
  log.Println(e)
  return
 }

 defer p.Release(r)

 time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond)
 log.Println("查询", queryParam, "使用连接", r.(*dbConnection).ID)
}

同时我们给出输出结果:

2024/05/05 23:36:10 资源不足,创建新的连接资源
2024/05/05 23:36:10 资源不足,创建新的连接资源
2024/05/05 23:36:10 资源不足,创建新的连接资源
2024/05/05 23:36:10 资源不足,创建新的连接资源
2024/05/05 23:36:10 资源不足,创建新的连接资源
2024/05/05 23:36:10 资源不足,创建新的连接资源
2024/05/05 23:36:10 资源不足,创建新的连接资源
2024/05/05 23:36:10 资源不足,创建新的连接资源
2024/05/05 23:36:10 资源不足,创建新的连接资源
2024/05/05 23:36:10 资源不足,创建新的连接资源
2024/05/05 23:36:10 资源不足,创建新的连接资源
2024/05/05 23:36:10 查询 17 使用连接 14
2024/05/05 23:36:10 连接归还成功
2024/05/05 23:36:10 查询 5 使用连接 5
2024/05/05 23:36:10 连接归还成功
2024/05/05 23:36:10 查询 3 使用连接 2
2024/05/05 23:36:10 连接归还成功
2024/05/05 23:36:10 查询 19 使用连接 19
2024/05/05 23:36:10 连接归还成功
.......

小结

到此这篇关于使用Go实现一个简单的无界资源池的文章就介绍到这了,更多相关Go无界资源池内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • GoLang中的iface 和 eface 的区别解析

    GoLang中的iface 和 eface 的区别解析

    iface 和 eface 都是 Go 中描述接口的底层结构体,区别在于 iface 描述的接口包含方法,而 eface 则是不包含任何方法的空接口:interface{},这篇文章主要介绍了GoLang之iface 和 eface 的区别,需要的朋友可以参考下
    2022-09-09
  • Go操作mongodb数据库方法示例

    Go操作mongodb数据库方法示例

    这篇文章主要为大家介绍了Go操作mongodb数据库方法示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-09-09
  • Golang 拷贝Array或Slice的操作

    Golang 拷贝Array或Slice的操作

    这篇文章主要介绍了Golang 拷贝Array或Slice的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-04-04
  • golang中gin框架接入jwt使用token验证身份

    golang中gin框架接入jwt使用token验证身份

    本文主要介绍了golang中gin框架接入jwt使用token验证身份,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-12-12
  • goland安装1.7版本报错Unpacked SDK is corrupted解决

    goland安装1.7版本报错Unpacked SDK is corrupted解决

    这篇文章主要为大家介绍了goland安装1.7版本报错Unpacked SDK is corrupted解决,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-11-11
  • Go语言的type func()用法详解

    Go语言的type func()用法详解

    在Go语言中,函数的基本组成为:关键字func、函数名、参数列表、返回值、函数体和返回语句,这篇文章主要介绍了Go语言的type func()用法,需要的朋友可以参考下
    2022-03-03
  • GoLang RabbitMQ实现六种工作模式示例

    GoLang RabbitMQ实现六种工作模式示例

    这篇文章主要介绍了GoLang RabbitMQ实现六种工作模式,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-12-12
  • 基于Go语言实现猜谜游戏

    基于Go语言实现猜谜游戏

    这篇文章主要为大家详细介绍了如何基于Go语言实现猜谜游戏,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习
    2023-09-09
  • Go语言基础知识总结(语法、变量、数值类型、表达式、控制结构等)

    Go语言基础知识总结(语法、变量、数值类型、表达式、控制结构等)

    这篇文章主要介绍了Go语言基础知识总结(语法、变量、数值类型、表达式、控制结构等),本文汇总了Go语言的入门知识,需要的朋友可以参考下
    2014-10-10
  • Go中变量命名规则与实例

    Go中变量命名规则与实例

    命名规则涉及变量、常量、全局函数、结构、接口、方法等的命名,下面这篇文章主要给大家介绍了关于Go中变量命名的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-01-01

最新评论