什么是资源池技术?它有什么用?

在开发应用程序过程中,涉及到对系统资源进行有效管理时往往会用到池化操作。资源池模式的应用场景很多,可以管理那些想要通过重用来分摊昂贵初始化代价的对象,而管理数据库连接就是很好的一种应用场景。数据库连接池作为一种典型的池化技术手段,能够显著提升数据库访问效率,在Mybatis等主流ORM框架中都有对应的实现方案。本文将对资源池技术进行全面介绍。

在日常开发过程中,相信你对线程池、数据库连接池等技术并不陌生。这里池(Pool)是一种对资源的抽象方法,代表一组可以随时使用的资源,但这些资源的创建和释放过程则基于一定的管理策略。

资源池的应用非常广泛,存在线程池、数据库连接池等多种具体的池化组件。这些技术组件虽然表现形式多样,但基本原理都是一致的。在介绍具体的池化技术之前,让我们先来分析资源池的基本原理,并尝试自己动手实现一个资源池。

资源池的基本原理与实现

一个典型的资源池的结构如下图所示。


可以看到,客户端可以向池请求资源, 用它来完成一些任务并当任务完成时归还该资源,而被归还的资源可以继续用来满足请求,从而达到资源复用的效果。资源池的特点主要在于节省了创建资源实例的开销和时间,但存储空间会随着对象的增多而增大。

基于资源池的基本结构,我们可以进一步分析它的应用场景。作为一种通用的技术组件,资源池的应用场景很多:

  1. 管理那些想要通过重用来分摊昂贵初始化代价的对象;
  2. 或者面向请求资源的频率很高且使用资源总数较低的业务处理过程;
  3. 当系统面临性能问题时,也可以通过资源池模式进行时间延迟方面的处理。

资源池的概念本身比较简单,我们对它的基本操作进行抽象,可以自己实现一个资源池,如下所示。

public abstract class ResourcePool<T> {

private HashSet<T> available = new HashSet<>();

private HashSet<T> inUse = new HashSet<>();

protected abstract T create();

public synchronized T out() {

if (available.size() <= 0) {

available.add(create());

}

T instance = available.iterator().next();

available.remove(instance);

inUse.add(instance);

return instance;

}

public synchronized void in(T instance) {

inUse.remove(instance);

available.add(instance);

}

@Override

public String toString() {

return String.format("池中可用资源=%d 在用资源=%d", available.size(), inUse.size());

}

}

可以看到,在ResourcePool类中,使用HashSet保持了池中的可用资源以及在用资源列表。然后,我们提供了out方法和in方法分别用于从资源池中获取资源以及将资源返回给资源池。我们在这两个方法上也通过使用synchronized关键词确保线程安全。

注意到,ResourcePool是一个抽象类,提供了create()抽象方法供具体的资源类使用。例如,我们可以构建如下所示的UserPool来实现基于User对象的资源池。

public class UserPool extends ResourcePool<User> {

@Override

protected User create() { 

return new User();

}

}

接下来,为了模拟昂贵的对象初始化过程,我们可以构建如下所示的User类,注意到在构造函数中,我们通过Thread.sleep(1000)来模拟这种高成本的创建过程。

public class User {

private static int counter = 1;

private final int id;

public User() {

id = counter++;

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

public int getId() {

return id;

}

@Override

public String toString() {

return String.format("User id=%d", id);

}

}

我们运行一下如下所示的代码来模拟对ResourcePool的使用过程。

public static void main(String[] args) {

UserPool pool = new UserPool();

System.out.println(pool);

User user1 = pool.out();

System.out.println("使用 " + user1);

System.out.println(pool);

User user2 = pool.out();

System.out.println("使用 " + user2);

User user3 = pool.out();

System.out.println("使用 " + user3);

System.out.println(pool);

System.out.println("归还 " + user1);

pool.in(user1);

System.out.println("归还" + user2);

pool.in(user2);

System.out.println(pool);

User user4 = pool.out();

System.out.println("使用 " + user4);

System.out.println(pool);

}

运行结果如下所示,体现了系统的运行时快照。

池中可用资源=0 在用资源=0

使用 User id=1

池中可用资源=0 在用资源=1

使用 User id=2

使用 User id=3

池中可用资源=0 在用资源=3

归还 User id=1

归还User id=2

池中可用资源=2 在用资源=1

使用 User id=2

池中可用资源=1 在用资源=2

作为示例,这个ResourcePool的对象体系是比较简单的,但已经完整阐述了基本的资源池模式。现实中资源池中存放的资源一般不大会是类似User类这样的业务对象,而更多关注于诸如数据库连接和套接字连接等需要网络通信的远程资源,以及线程和内存等系统资源。在接下来的内容中,我们将基于Mybatis中的数据库连接池的实现过程来进一步加深对资源池的理解。

资源池的应用场景:数据库连接池

在介绍Mybatis中数据库连接池之前,我们有必要对连接池的实现机制有个总体的把握。

数据库连接池工作流程

在连接池中,对连接的管理策略是重点,也在很大程度上决定了不同连接池之间的实现方式。常见的实现策略是:当客户请求连接时,首先查看连接池中是否有空闲连接,如果存在空闲连接,则将连接分配给客户使用;如果没有空闲连接,则查看当前所开的连接数是否已经达到最大连接数,如果没达到就重新创建一个连接给请求的客户;如果达到就按设定的最大等待时间进行等待,如果超出最大等待时间,则抛出异常给客户。整个流程如下图所示。


围绕前面介绍的连接池的管理方式,我们可以抽象出一些控制维度和参数,常见的参数包括最小空闲连接数(MinIdle)、最大空闲连接数(MaxIdle)、连接池最大活跃连接数(MaxActive)、最大超时时间(MaxTimeout)等。

对于连接池而言,性能是我们选择不同实现工具的首要考虑因素。基于已知内容,我们可以进一步分析连接池内连接的分配和释放对系统的性能的影响:

  1. 如果将总连接数的上限设置得过大,可能因连接数过多而导致数据库僵死,系统整体性能下降;
  2. 如果总连接数上限过小,则无法完全发挥数据库的性能,浪费数据库资源。

另一方面:

  1. 如果将空闲连接的上限设置得过大,则会浪费系统资源来维护这些空闲连接;
  2. 如果空闲连接上限过小,当出现瞬间的峰值请求时,系统的快速响应能力就比较弱。

所以在设置数据库连接池的这些值时,需要进行测试和权衡,不同的实现方案会有不同的考虑。

Mybatis中的数据库连接池实现过程

我们知道DataSource是JDBC中定义的接口,该接口只包含两个重载的getConnection()方法。Mybatis实现了该接口,并提供了三种实现方案,其中最主要的就是池化类PooledDataSource。我们直接来看PooledDataSource的getConnection()方法,发现它进一步调用了如下所示的popConnection()方法。这个方法非常长,为了更好的把握代码结构,我们对该方法的代码进行裁剪,只关注于主要的分支和流程。

private PooledConnection popConnection(String username, String password) throws SQLException {

    

    while (conn == null) {

      synchronized (state) {

        if (!state.idleConnections.isEmpty()) {

          // 如果idle列表不为空,表示有可用连接,直接选取第一个元素

          conn = state.idleConnections.remove(0);          

        } else {

          // 连接池没有可用连接的场景

          if (state.activeConnections.size() < poolMaximumActiveConnections) {

            // 如果active列表没有满,直接创建新连接

            conn = new PooledConnection(dataSource.getConnection(), this);            

          } else {// active已经满了

            // 获得试用最久的连接,判断是否已经超时

            PooledConnection oldestActiveConnection = state.activeConnections.get(0);

            long longestCheckoutTime = oldestActiveConnection.getCheckoutTime();

            if (longestCheckoutTime > poolMaximumCheckoutTime) {

              // 已经超时,将原连接废弃并建立新连接               

              conn = new PooledConnection(oldestActiveConnection.getRealConnection(), this);               

            } else {

              // 如果没有超时,则进行等待,并计算时间累加

              state.wait(poolTimeToWait);

            }

          }

        }

        if (conn != null) {

          // 如果获取到了连接,则验证该连接是否有效

          if (conn.isValid()) {

         // 如果连接有效,更新该链接相关参数和状态

          } else {

         // 如果连接无效,且无效连接数量超过上限则抛异常             

          }

        }

      }

    }

    if (conn == null) {

     // 如果最终无法获取有效连接,则同样抛异常

    }

    return conn;

}

在上述代码中,我们添加了很多注释来解释用于获取Connection连接对象的整体流程。在整体流程上,当Mybatis执行查询时会首先从idleConnections列表中申请一个空闲的连接,只有当idleConnections列表为空时才会常见新连接。当然PooledDataSource并不允许无限建立新连接,当连接池中连接数目达到一定数量时,即使idleConnections列表为空,也不会建立新连接。而是从activeConnections列表中找出使用最久的一个连接,判断其是否超时。如果超时,则将该连接废弃并建立新连接,否则线程等待直到有连接池中有新的可用连接。

可以看到popConnection()方法结构非常清晰。如果我们对前面介绍都的连接池管理方法有一定了解的话,理解这段代码难度并不大。

全文小结

本文系统分析了在日常开发过程中非常常用的一种技术组件,即资源池。我们通过分析资源池的结构了解了它的基本实现原理,并尝试自己动手实现一个简单的资源池。更为重要的,我们详细分析了资源池的应用场景,并基于Mybatis这一主流ORM框架,给出了数据库连接池这一具体资源池的工作流程和实现过程。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/625040.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

云端的艺术革命:云渲染如何重塑动画与视觉特效产业

在 2019 年&#xff0c;乔恩费儒&#xff08;Jon Favreau&#xff09;决定重拍迪士尼的经典电影《狮子王》。他的创新构想是以真实动物为模型&#xff0c;在非洲草原上拍摄&#xff0c;由真实动物“出演”的辛巴和其他角色&#xff0c;随后通过配音赋予它们生命。 为了实现这一…

vue前端时间段选择控件

实现效果: 可选具体的某天的某时某分某秒 vue前端代码: <el-form-item label"日期"><el-date-pickerv-model"daterangerq"style"width: 240px"value-format"yyyy-MM-dd HH:mm:ss"type"datetimerange"range-separat…

[笔记] srlua库编译

文章目录 前言一、环境二、编译过程2.1 gcc安装2.2 编译lua2.3 编译srlua库 三、测试srlua库参考总结 前言 一、环境 centos7.9 gcc version 4.8.5 20150623 (Red Hat 4.8.5-44) (GCC) lua5.1源码 srlua 源码 二、编译过程 2.1 gcc安装 yum install gcc这里gcc安装过程和环…

基础学习-Git(分布式版本控制系统)

学习视频推荐 http://【黑马程序员Git全套教程&#xff0c;完整的git项目管理工具教程&#xff0c;一套精通git】 https://www.bilibili.com/video/BV1MU4y1Y7h5/?p5&share_sourcecopy_web&vd_source2b85bd9be9213709642d908906c3d863 1、Git环境配置 安装Git Git下…

CAXA 3D实体设计2024:塑造未来的创新引擎

在数字化时代的浪潮中&#xff0c;3D CAD实体建模设计正成为推动工业创新的核心动力。CAXA 3D实体设计2024&#xff0c;以其卓越的性能和丰富的功能&#xff0c;为企业和个人用户带来了前所未有的设计体验。 CAXA 3D实体设计2024不仅拥有直观易用的界面&#xff0c;还配备了强…

【JS】call和 apply函数的详解

JavaScript 中 call() 和 apply() 函数的详解 在JavaScript中&#xff0c;call()和apply()都是非常重要的方法&#xff0c;用于调用函数时指定函数体内的this的值&#xff0c;从而实现不同对象之间的方法共享。尽管它们的功能非常相似&#xff0c;但在实际使用中各有其优势和特…

数据结构选择题(期末)

1.给定NN的二维数组A&#xff0c;则在不改变数组的前提下&#xff0c;查找最大元素的时间复杂度是&#xff08;A&#xff09;&#xff1a; A.O(N2) B.O(NlogN) C.O(N) D.O(N2logN) 两重循环即O(N2)的时间复杂度 2.与数据元素本身的形式、内容、相对位置、个数无关的是数据的…

OpenAI 发布新款大型语言模型 GPT-4o,带大家了解最新ChatGPT动态。

OpenAI 发布新款大型语言模型 GPT-4o 昨日OpenAI 举办了一场线上活动&#xff0c;正式发布了其最新研发的 AI 模型 GPT-4o&#xff0c;并详细介绍了该模型的强大功能和未来发展规划。此次发布标志着 AI 技术的重大突破&#xff0c;为用户提供了更加便捷、高效的 AI 工具&#…

荆州科技局副局长乔梁莅临湖北点赋网络科技公司参观调研

近日&#xff0c;荆州科技局副局长乔梁&#xff0c;莅临湖北点赋网络科技公司进行参观调研。点赋科技总经理崔梦娇亲自陪同&#xff0c;向副局长介绍了公司的D咖智能饮品机器人经营状况和研发进展情况。 在参观过程中&#xff0c;副局长乔梁对点赋科技的创新能力和技术成果给予…

【计算机毕业设计】基于SSM++jsp的高校专业信息管理系统【源码+lw+部署文档+讲解】

目录 第1章 绪论 1.1 课题背景 1.2 课题意义 1.3 研究内容 第2章 开发环境与技术 2.1 MYSQL数据库 2.2 JSP技术 2.3 SSM框架 第3章 系统分析 3.1 可行性分析 3.1.1 技术可行性 3.1.2 经济可行性 3.1.3 操作可行性 3.2 系统流程 3.2.1 操作流程 3.2.2 登录流程 3.2.3 删除信息流…

FedDML:Federated Mutual Learning

这篇把DML运用到FL上 论文地址:arvix code: 作者git 贡献 我们针对三种异质性(DOM)提出了一种新颖的联邦学习范式,称为联邦相互学习(FML)。 首先,FML 处理数据和目标通过使每个客户能够训练个性化模型来实现异质性。 从OH的意义上来说,DH对服务器有害,但对客户端有…

全面了解 Swagger 导出功能的使用方式

Swagger 是一个强大的平台&#xff0c;专门用于开发、构建和记录 RESTful Web 接口。通过其提供的交互式用户界面&#xff0c;开发人员能够轻松且迅速地创建和测试 API。Swagger 还允许用户以多种格式&#xff0c;包括 JSON 和 Markdown&#xff0c;导出 API 文档。选择 JSON 格…

Go微服务: Prometheus性能监控与Grafana平台的搭建

Prometheus 概述 promethues 是一套开源的监控&报警&时间序列数据库的组合基本原理是通过http协议周期性抓取被监控组件的状态适合Docker、Kubernetes环境的监控系统 Promethues 整体架构 一、抓取数据的两种方式 1 &#xff09;Short-lived jobs 短暂的任务 不会提…

The Quantcast File System——论文泛读

VLDB 2013 Paper 分布式元数据论文阅读笔记整理 问题 在2013年之前&#xff0c;由于网络链路带宽有限&#xff0c;数据在集群中移动速度慢&#xff0c;因此Hadoop尽量将数据留在原来的位置&#xff0c;并将处理代码发送给它。随着网络链路的发展&#xff0c;可以之前更高的数…

2024年为什么很多电商商家,都想涌入视频号,究竟是什么原因?

大家好&#xff0c;我是电商糖果 对电商有了解的朋友&#xff0c;在今年肯定发现一个现象&#xff0c;那就是很多商家对视频号比较青睐。 视频号究竟有何魔力&#xff0c;让越来越多的商家都想要入驻。 其实很简单&#xff0c;它让商家看到了市场。 视频号背后是谁&#xf…

SpringBoot集成Curator实现Zookeeper基本操作

系列文章目录 文章目录 系列文章目录前言 前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站&#xff0c;这篇文章男女通用&#xff0c;看懂了就去分享给你的码吧。 Zookeeper是一个Ap…

未来想从事营销策划类的工作,需要怎么学习?

从事营销策划类的工作&#xff0c;提升和学习主要从以下三个方面&#xff1a; 一、营销底层逻辑的搭建 二、营销系统知识的构建 三、大量营销案例的积累 营销入门&#xff0c;其实大多数人一直都在入门的道路上&#xff0c;每个人都是终身学习者。虽然从事营销工作十年多了…

PMP考前冲刺攻略,考试前必看

调整心态 考场就像战场一样&#xff0c;不仅仅是实力的较量&#xff0c;更是心理素质的较量。如果感到过于焦虑&#xff0c;可以通过运动等方式来缓解&#xff0c;也可以多与家人、朋友和老师沟通。只有稳定心态才能发挥出最大的实力&#xff01; 高效学习 课本是基础&#…

C#学习笔记12:Winform网页操作-CefSharp内嵌浏览器

今日学习使用Winform操作网页&#xff0c;先从从窗体内嵌一个浏览器开始吧&#xff1a; 文章提供测试代码讲解、测试效果图、整体测试工程下载 目录 CefSharp介绍与安装&#xff1a; 创建解决方案安装CefSharp&#xff1a; 控件放置&#xff1a; 整体代码贴出&#xff1a; 更改…

上位机图像处理和嵌入式模块部署(树莓派4b的提高版)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 目前人工智能非常火&#xff0c;但是人工智能需要极高的算力和海量的数据&#xff0c;因此相关的关联公司非常吃香&#xff0c;nvidia就是提供算力…