当前位置: 首页 > news >正文

go语言 Pool实现资源池管理数据库连接资源或其他常用需要共享的资源

go Pool

Pool用于展示如何使用有缓冲的通道实现资源池,来管理可以在任意数量的goroutine之间共享及独立使用的资源。这种模式在需要共享一组静态资源的情况(如共享数据库连接或者内存缓冲区)下非 常有用。如果goroutine需要从池里得到这些资源中的一个,它可以从池里申请,使用完后归还到资源池里。

按照上面的要求,整个资源池的要求和框架图梳理如下:
在这里插入图片描述

  1. 资源池能够根据工厂函数创建多个,也就是资源池要是个通用的不能只针对一种资源的资源池
  2. 资源池要有上限
  3. 资源池需要保证多线程安全
  4. 资源池在回收资源时,如果资源已经超过资源池上限不能放到资源池里面,需要直接释放Close掉

提到池子,channel天然就是一个池子,而且channel是多线程安全的,所以可以直接使用管道来安全的共享资源。文章按照如下四个方面对Pool资源池的设计过程以及使用进行剖析。
在这里插入图片描述

数据结构

既然确定了使用管道进行资源的共享,那么Pool里面就不能少了管道。

resources chan io.Closer

这里我们定义一个类型为io.Closer的chan,因为管道中的资源都需要再不使用时清理资源,因此这里所有传入到管道的资源都需要实现io.Closer接口。

向管道中放入资源和从管道中取出资源是多线程安全的,但是打开管道和关闭管道本身又是线程不安全的,因此这里需要对管道的创建和关闭进行保护, 这里采用sync.Mutex互斥锁进行保护。

mutex sync.Mutex

除了管道,Pool还需要一个资源创建函数,用于在资源池里创建资源。

factory func() (io.Closer, error)

factory用于创建资源,返回一个实现了io.Closer的对象,表示创建的资源。

最后,还需要一个原子变量用来表示Pool的状态。

closed bool

atomic包提供了一些原子操作,包括对bool变量的设置。

错误处理

当向一个已经关闭的Pool请求资源时,需要返回一个错误提醒用户该Pool已经关闭。

var ErrPoolClosed = errors.New("Pool has been closed.")

方法实现

创建Pool

调用New方法创建Pool,需要提供一个资源创建函数,以及资源池中初始化的资源数量。

// New 创建一个新的池子,这个池子会调用factory创建资源,并可设置资源上限
func New(fn func() (io.Closer, error), size uint) (*Pool, error) {if size <= 0 {return nil, errors.New("size value too small")}return &Pool{factory:   fn,resources: make(chan io.Closer, size),}, nil
}

获取资源

既然是资源池,那么就要提供获取资源的方法,这里采用Acquire作为获取资源的方法。资源获取时有两种情况:

  1. 如果有空闲资源,就直接返回该资源。
  2. 如果没有空闲资源,就调用factory创建一个新资源。

// Acquire 从池子中获取一个资源
func (p *Pool) Acquire() (io.Closer, error) {select {// 检查是否有空闲资源case r, ok := <-p.resources:if !ok {return nil, ErrPoolClosed}return r, nil//	因为没有空闲资源,所以创建一个新资源default:if p.factory == nil {return nil, ErrPoolClosed}return p.factory()}
}

这里用来select的一个技巧将阻塞的资源变成非阻塞的分支。当向一个空管道中获取资源时,会阻塞住,但是通过select可以在通道阻塞的情况下执行default分支,从而将阻塞的操作转换为非阻塞。

释放资源

当释放资源时也存在两种情况:

  1. 如果资源池已经关闭,就直接关闭该资源。
  2. 如果资源池没有满,就直接将资源放回资源池。
// Release 将一个使用后的资源放回池子里面
func (p *Pool) Release(r io.Closer) {// Secure this operation with the Close operation.p.m.Lock()defer p.m.Unlock()// If the pool is closed, discard the resource.if p.closed {err := r.Close()if err != nil {return}return}// select能将所有阻塞的资源通过默认分支变成非阻塞的分支select {// 试图将资源放回资源池case p.resources <- r:log.Println("Release:", "In Queue")// 如果资源池已经满了,就关闭这个资源default:log.Println("Release:", "Closing")err := r.Close()if err != nil {return}}
}

关闭资源池Pool

当资源池使用完毕时需要及时关闭资源池,并释放掉所有的资源。

// Close 关闭资源池
func (p *Pool) Close() {// 保证多线程安全p.m.Lock()defer p.m.Unlock()// 如果资源池已经关闭,就不用再关闭了if p.closed {return}// 关闭资源池并释放所有资源close(p.resources)p.closed = true// 通道即使关闭了,也能通过range将通道中的资源全部接收一遍for r := range p.resources {err := r.Close()if err != nil {return}}
}

按照上述步骤将资源池的视线示例汇总如下

// Package pool 管理用户资源的池子
package poolimport ("errors""io""log""sync"
)// 说明go 1.6之后,自带sync.Pool用来管理资源// Pool 管理用户资源的池子,可以在多个goroutine之间共享资源
type Pool struct {m         sync.Mutexresources chan io.Closerfactory   func() (io.Closer, error)closed    bool
}// ErrPoolClosed 表示(Acquire)了一个已经关闭的池子
var ErrPoolClosed = errors.New("pool has been closed")// New 创建一个新的池子,这个池子会调用factory创建资源,并可设置资源上限
func New(fn func() (io.Closer, error), size uint) (*Pool, error) {if size <= 0 {return nil, errors.New("size value too small")}return &Pool{factory:   fn,resources: make(chan io.Closer, size),}, nil
}// Acquire 从池子中获取一个资源
func (p *Pool) Acquire() (io.Closer, error) {select {// 检查是否有空闲资源case r, ok := <-p.resources:if !ok {return nil, ErrPoolClosed}return r, nil//	因为没有空闲资源,所以创建一个新资源default:if p.factory == nil {return nil, ErrPoolClosed}return p.factory()}
}// Release 将一个使用后的资源放回池子里面
func (p *Pool) Release(r io.Closer) {// Secure this operation with the Close operation.p.m.Lock()defer p.m.Unlock()// If the pool is closed, discard the resource.if p.closed {err := r.Close()if err != nil {return}return}// select能将所有阻塞的资源通过默认分支变成非阻塞的分支select {// 试图将资源放回资源池case p.resources <- r:log.Println("Release:", "In Queue")// 如果资源池已经满了,就关闭这个资源default:log.Println("Release:", "Closing")err := r.Close()if err != nil {return}}
}// Close 关闭资源池
func (p *Pool) Close() {// 保证多线程安全p.m.Lock()defer p.m.Unlock()// 如果资源池已经关闭,就不用再关闭了if p.closed {return}// 关闭资源池并释放所有资源close(p.resources)p.closed = true// 通道即使关闭了,也能通过range将通道中的资源全部接收一遍for r := range p.resources {err := r.Close()if err != nil {return}}
}

调用示例

// 这个示例展示了Pool的使用,其实新版本的go里面已经内置了资源池,这里只是为了演示如何使用自己实现的Pool
package mainimport ("io""log""math/rand""sync""sync/atomic""time""patterns/pool"
)const (maxGoroutines   = 25 // 协程数量.pooledResources = 2  // 资源池的大小
)// 数据库连接池资源信息.
type dbConnection struct {ID int32
}// Close 实现 io.Closer 接口 
func (dbConn *dbConnection) Close() error {log.Println("Close: Connection", dbConn.ID)return nil
}// idCounter 为每个连接生成一个唯一的ID
var idCounter int32// createConnection 连接创建工厂函数
func createConnection() (io.Closer, error) {id := atomic.AddInt32(&idCounter, 1)log.Println("Create: New Connection", id)return &dbConnection{id}, nil
}func main() {// 协程同步var wg sync.WaitGroupwg.Add(maxGoroutines)// 创建资源池.p, err := pool.New(createConnection, pooledResources)if err != nil {log.Println(err)}// 资源池性能模拟测试.for query := 0; query < maxGoroutines; query++ {// 这里将query作为参数传入,是为了避免闭包时引用导致所有协程都拿到的是同一个query的值go func(q int) {performQueries(q, p)wg.Done()}(query)}// 等待所有协程结束wg.Wait()// 关闭资源池log.Println("Shutdown Program.")p.Close()
}// performQueries 测试资源池的查询表现
func performQueries(query int, p *pool.Pool) {// 获取一个连接池连接conn, err := p.Acquire()if err != nil {log.Println(err)return}// 将连接信息放回连接池defer p.Release(conn)// 使用延迟模拟查询事件time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond)log.Printf("Query: QID[%d] CID[%d]\n", query, conn.(*dbConnection).ID)
}

附录

  1. 数据来源-《go语言实战》
  2. 代码仓库:gitee pool

在这里插入图片描述

如果感觉文章对你有用欢迎点赞,评论和关注,谢谢!

相关文章:

go语言 Pool实现资源池管理数据库连接资源或其他常用需要共享的资源

go Pool Pool用于展示如何使用有缓冲的通道实现资源池&#xff0c;来管理可以在任意数量的goroutine之间共享及独立使用的资源。这种模式在需要共享一组静态资源的情况&#xff08;如共享数据库连接或者内存缓冲区&#xff09;下非 常有用。如果goroutine需要从池里得到这些资…...

mysql一个事务最少几次IO操作

事务的IO操作过程 开始事务&#xff1a;用户发起一个事务&#xff0c;例如执行START TRANSACTION;&#xff0c;此时事务开始。读取和修改数据&#xff1a;用户读取和修改数据时&#xff0c;InnoDB首先从Buffer Pool查找所需的数据页。如果数据页不在Buffer Pool中&#xff0c;…...

运输层总结

运输层协议&#xff1a;端到端协议 面向连接的传输控制协议 TCP无连接的用户数据报协议 UDP - 主要任务&#xff1a;为相 互通信的应用进程 提供 逻辑通信服务 - 屏蔽&#xff1a;运输层向高层用户 屏蔽 了下面网络核心的细节&#xff08;如网络拓扑、所采用 的路由选择协议等…...

【嵌套查询】.NET开源 ORM 框架 SqlSugar 系列

.NET开源 ORM 框架 SqlSugar 系列 【开篇】.NET开源 ORM 框架 SqlSugar 系列【入门必看】.NET开源 ORM 框架 SqlSugar 系列【实体配置】.NET开源 ORM 框架 SqlSugar 系列【Db First】.NET开源 ORM 框架 SqlSugar 系列【Code First】.NET开源 ORM 框架 SqlSugar 系列【数据事务…...

React 前端框架1

一、React 简介 &#xff08;一&#xff09;什么是 React React 是一个用于构建用户界面的 JavaScript 库&#xff0c;由 Facebook 开源并维护。它采用了组件化的开发思想&#xff0c;允许开发者将复杂的 UI 拆分成一个个独立、可复用的小组件&#xff0c;就如同搭积木一般&am…...

【真正离线安装】Adobe Flash Player 32.0.0.156 插件离线安装包下载(无需联网安装)

网上很多人声称并提供的flash离线安装包是需要联网才能安装成功的&#xff0c;其实就是在线安装包&#xff0c;而这里提供的是真正的离线安装包&#xff0c;无需联网即可安装成功。 点击下面地址下载离线安装包&#xff1a; Adobe Flash Player 32.0.0.156 for IE Adobe Fla…...

数据采集时,不同地区的动态IP数据质量有什么差异?

在数据采集的广阔世界中&#xff0c;动态IP扮演着至关重要的角色。它们不仅帮助我们突破地域限制&#xff0c;还能够提供多样化的数据来源。但是&#xff0c;不同地区的动态IP在数据质量上是否存在差异呢&#xff1f;本文将探讨这一问题&#xff0c;并为您提供实用的见解。 动…...

【Python爬虫五十个小案例】爬取猫眼电影Top100

博客主页&#xff1a;小馒头学python 本文专栏: Python爬虫五十个小案例 专栏简介&#xff1a;分享五十个Python爬虫小案例 &#x1f40d;引言 猫眼电影是国内知名的电影票务与资讯平台&#xff0c;其中Top100榜单是影迷和电影产业观察者关注的重点。通过爬取猫眼电影Top10…...

等保测评和 ISO27001 都是信息保护,区别是什么?

ISO27001 和等级保护&#xff08;等保&#xff09;都是信息安全领域重要的标准和制度&#xff0c;但它们在多个方面存在区别&#xff1a; 定义和性质 ISO27001 它是国际标准化组织&#xff08;ISO&#xff09;发布的信息安全管理体系标准&#xff0c;其目的是帮助组织建立、实…...

Linux系统编程之进程创建

概述 在Linux系统中&#xff0c;通过创建新的进程&#xff0c;我们可以实现多任务处理、并发执行和资源隔离等功能。创建进程的主要方法为&#xff1a;fork、vfork、clone。下面&#xff0c;我们将分别进行介绍。 fork fork是最常用的创建新进程的方法。当一个进程调用fork时&a…...

JAVA-IO

目录 IO流 一 字节流 1 FileOutStream 1 书写&#xff1a; 2 换行书写与续写&#xff1a; 2 FileInputStream 1 读取数据 2 循环读取&#xff1a; 二 字符流 1 FileReader 1 空参的read()方法读取数据&#xff1a; 2 有参的read()方法读取数据&#xff1a; 3 指定字…...

动态系统特征分析:特征向量、特征值、频率与阻尼比、参与因子计算方法

特征值和特征向量在动态系统分析中是核心工具&#xff0c;广泛用于电力系统小信号稳定性、机械系统模态分析等领域。以下详细介绍计算方法及应用。 1. 求解特征值与特征向量 对于一个 n n n\times n nn的系统矩阵 A A A&#xff1a; 右特征向量与特征值 特征值( λ \lambd…...

乐鑫发布 esp-iot-solution v2.0 版本

今天&#xff0c;乐鑫很高兴地宣布&#xff0c;esp-iot-solution v2.0 版本已经发布&#xff0c;release/v2.0 分支下的正式版本组件将为用户提供为期两年的 Bugfix 维护&#xff08;直到 2027.01.25 ESP-IDF v5.3 EOL&#xff09;。该版本将物联网开发中常用的功能进行了分类整…...

动态代理如何加强安全性

在当今这个信息爆炸、网络无孔不入的时代&#xff0c;我们的每一次点击、每一次浏览都可能留下痕迹&#xff0c;成为潜在的安全隐患。如何在享受网络便利的同时&#xff0c;有效保护自己的隐私和信息安全&#xff0c;成为了每位网络使用者必须面对的重要课题。动态代理服务器&a…...

Flutter 之 InheritedWidget

InheritedWidget 是 Flutter 框架中的一个重要类&#xff0c;用于在 Widget 树中共享数据。它是 Flutter 中数据传递和状态管理的基础之一。通过 InheritedWidget&#xff0c;你可以让子 Widget 在不需要显式传递数据的情况下&#xff0c;访问祖先 Widget 中的数据。这种机制对…...

AI 助力开发新篇章:云开发 Copilot 深度体验与技术解析

本文 一、引言&#xff1a;技术浪潮中的个人视角1.1 AI 和低代码的崛起1.2 为什么选择云开发 Copilot&#xff1f; 二、云开发 Copilot 的核心功能解析2.1 自然语言驱动的低代码开发2.1.1 自然语言输入示例2.1.2 代码生成的模块化支持 2.2 实时预览与调整2.2.1 实时预览窗口功能…...

MyBatis-Plus介绍及基本使用

文章目录 概述介绍MyBatis-Plus 常用配置分页插件配置类注解配置 快速入门maven 依赖编写配置文件编写启动类编写 MybatisPlus 配置类 代码生成器&#xff1a;MybatisPlusGeneratormaven依赖代码生成器核心类 概述 介绍 MyBatis-Plus&#xff08;简称 MP&#xff09;是一个 M…...

SpringBoot 整合 Avro 与 Kafka

优质博文&#xff1a;IT-BLOG-CN 【需求】&#xff1a;生产者发送数据至 kafka 序列化使用 Avro&#xff0c;消费者通过 Avro 进行反序列化&#xff0c;并将数据通过 MyBatisPlus 存入数据库。 一、环境介绍 【1】Apache Avro 1.8&#xff1b;【2】Spring Kafka 1.2&#xf…...

支持JT1078和GB28181的流媒体服务器-LKM启动配置文件参数说明

流媒体服务器地址&#xff1a;https://github.com/lkmio/lkm GB28181信令&#xff0c;模拟多个国标设备工具&#xff1a;https://github.com/lkmio/gb-cms 文章目录 gop_cachegop_buffer_sizeprobe_timeoutwrite_timeoutmw_latencylisten_ippublic_ipidle_timeoutreceive_timeo…...

什么是隐式类型转换?隐式类型转换可能带来哪些问题? 显式类型转换(如强制类型转换)有哪些风险?

C 中的隐式类型转换 定义&#xff1a;在 C 中&#xff0c;隐式类型转换是指由编译器自动执行的类型转换&#xff0c;不需要程序员显式地进行操作。这种转换在很多情况下会自动发生&#xff0c;比如在表达式求值、函数调用传参等过程中。常见场景 算术运算中的转换&#xff1a;…...

conda相比python好处

Conda 作为 Python 的环境和包管理工具&#xff0c;相比原生 Python 生态&#xff08;如 pip 虚拟环境&#xff09;有许多独特优势&#xff0c;尤其在多项目管理、依赖处理和跨平台兼容性等方面表现更优。以下是 Conda 的核心好处&#xff1a; 一、一站式环境管理&#xff1a…...

TDengine 快速体验(Docker 镜像方式)

简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能&#xff0c;本节首先介绍如何通过 Docker 快速体验 TDengine&#xff0c;然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker&#xff0c;请使用 安装包的方式快…...

Python:操作 Excel 折叠

💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例

使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件&#xff0c;常用于在两个集合之间进行数据转移&#xff0c;如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model&#xff1a;绑定右侧列表的值&…...

前端导出带有合并单元格的列表

// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...

华为OD机试-食堂供餐-二分法

import java.util.Arrays; import java.util.Scanner;public class DemoTest3 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextIn…...

【单片机期末】单片机系统设计

主要内容&#xff1a;系统状态机&#xff0c;系统时基&#xff0c;系统需求分析&#xff0c;系统构建&#xff0c;系统状态流图 一、题目要求 二、绘制系统状态流图 题目&#xff1a;根据上述描述绘制系统状态流图&#xff0c;注明状态转移条件及方向。 三、利用定时器产生时…...

项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)

Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败&#xff0c;具体原因是客户端发送了密码认证请求&#xff0c;但Redis服务器未设置密码 1.为Redis设置密码&#xff08;匹配客户端配置&#xff09; 步骤&#xff1a; 1&#xff09;.修…...

智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制

在数字化浪潮席卷全球的今天&#xff0c;数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具&#xff0c;在大规模数据获取中发挥着关键作用。然而&#xff0c;传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时&#xff0c;常出现数据质…...

C# 求圆面积的程序(Program to find area of a circle)

给定半径r&#xff0c;求圆的面积。圆的面积应精确到小数点后5位。 例子&#xff1a; 输入&#xff1a;r 5 输出&#xff1a;78.53982 解释&#xff1a;由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982&#xff0c;因为我们只保留小数点后 5 位数字。 输…...