04 | 连接池:别让连接池帮了倒忙

连接池一般对外提供获得连接、归还连接的接口给客户端使用,并暴露最小空闲连接数、最大连接数等可配置参数,在内部则实现连接建立、连接心跳保持、连接管理、空闲连接回收、连接可用性检测等功能。

注意鉴别客户端 SDK 是否基于连接池

我们首先要确定客户端 SDK 是否是基于连接池技术实现的。我们知道,TCP 是面向连接的基于字节流的协议:

  1. 面向连接,意味着连接需要先创建再使用,创建连接的三次握手有一定开销;
  2. 基于字节流,意味着字节是发送数据的最小单元,TCP 协议本身无法区分哪几个字节是完整的消息体,也无法感知是否有多个客户端在使用同一个 TCP 连接,TCP 只是一个读写数据的管道。

如果客户端 SDK 没有使用连接池,而直接是 TCP 连接,那么就需要考虑每次建立 TCP 连接的开销,并且因为 TCP 基于字节流,在多线程的情况下对同一连接进行复用,可能会产生线程安全问题
TCP 连接的客户端 SDK,对外提供 API 的三种方式:

  1. 连接池和连接分离的 API
  2. 内部带有连接池的 API
  3. 非连接池的 API

在使用三方SDK 时,一定要先查看官方文档了解其最佳实践,或是在类似 Stackoverflow 的网站搜索
XXX threadsafe/singleton 字样看看大家的回复,也可以一层一层往下看源码,直到定位到原始 Socket 来判断 Socket 和客户端 API 的对应关系。

  1. 如果是分离方式,那么连接池本身一般是线程安全的,可以复用。每次使用需要从连接池获取连接,使用后归还,归还的工作由使用者负责。
  2. 如果是内置连接池,SDK 会负责连接的获取和归还,使用的时候直接复用客户端。
  3. 如果 SDK 没有实现连接池(大多数中间件、数据库的客户端 SDK 都会支持连接池),那通常不是线程安全的,而且短连接的方式性能不会很高,使用的时候需要考虑是否自己封装一个连接池。

案例:操作jedis库
启动两个线程,共享操作同一个 Jedis 实例,每一个线程循环 1000 次,分别读取Key 为 a 和 b 的 Value,判断是否分别为 1 和 2:

Jedis jedis = new Jedis("127.0.0.1", 6379);
new Thread(() -> {for (int i = 0; i < 1000; i++) {String result = jedis.get("a");if (!result.equals("1")) {log.warn("Expect a to be 1 but found {}", result);return;}}
}).start();
new Thread(() -> {for (int i = 0; i < 1000; i++) {String result = jedis.get("b");if (!result.equals("2")) {log.warn("Expect b to be 2 but found {}", result);return;}}
}).start();
TimeUnit.SECONDS.sleep(5);

执行程序多次,可以看到日志中出现了各种奇怪的异常信息,有的是读取 Key 为 b 的Value 读取到了 1,有的是流非正常结束,还有的是连接关闭异常:
分析源码:
Jedis 继承了 BinaryJedis,BinaryJedis 中保存了单个 Client 的实例,Client最终继承了 Connection,Connection 中保存了单个 Socket 的实例,和 Socket 对应的两个读写流。因此,一个 Jedis 对应一个 Socket 连接

private static JedisPool jedisPool = new JedisPool("127.0.0.1", 6379);
new Thread(() -> {try (Jedis jedis = jedisPool.getResource()) {for (int i = 0; i < 1000; i++) {String result = jedis.get("a");if (!result.equals("1")) {log.warn("Expect a to be 1 but found {}", result);return;}}}
}).start();

最好通过 shutdownhook,在程序退出之前关闭 JedisPool:

@PostConstruct
public void init() {Runtime.getRuntime().addShutdownHook(new Thread(() -> {jedisPool.close();}));
}

JedisPool 的 getResource 方法在拿到 Jedis 对象后,将自己设置为了连接池。连接池JedisPool,继承了 JedisPoolAbstract,而后者继承了抽象类 Pool,Pool 内部维护了Apache Common 的通用池 GenericObjectPool。JedisPool 的连接池就是基于GenericObjectPool 的。
Jedis 的 API 实现是连接池和连接分离的 API,JedisPool 是线程安全的连接池,Jedis 是非线程安全的单一连接

使用连接池务必确保复用

池一定是用来复用的,否则其使用代价会比每次创建单一对象更大。对连接池来说更是如此,原因如下:

  1. 创建连接池的时候很可能一次性创建了多个连接,大多数连接池考虑到性能,会在初始化的时候维护一定数量的最小连接
  2. 大多数的连接池都有闲置超时的概念。连接池会检测连接的闲置时间,定期回收闲置的连接,把活跃连接数降到最低(闲置)连接的配置值,减轻服务端的压力.

连接池的配置不是一成不变的

最大连接数不是设置得越大越好,太大的话,需要使用过多的资源来维护,会给服务端带来更大的压力。连接池最大连接数设置得太小,很可能会因为获取连接的等待时间太长,导致吞吐量低下,甚至超时无法获取连接。
这里要强调的是,修改配置参数务必验证是否生效,并且在监控系统中确认参数是否生效、是否合理。之所以要“强调”,是因为这里有坑。
应用准备针对大促活动进行扩容,把数据库配置文件中Druid 连接池最大连接数 maxActive 从 50 提高到了 100,修改后并没有通过监控验证,结果大促当天应用因为连接池连接数不够爆了。
经排查发现,当时修改的连接数并没有生效。原因是,应用虽然一开始使用的是 Druid 连接池,但后来框架升级了,把连接池替换为了 Hikari 实现,原来的那些配置其实都是无效的,修改后的参数配置当然也不会生效

04 | 连接池:别让连接池帮了倒忙相关推荐

  1. 连接池:别让连接池帮了倒忙

    今天,我再与你说说另一种很重要的池化技术,即连接池. 我先和你说说连接池的结构.连接池一般对外提供获得连接.归还连接的接口给客户端使用,并暴露最小空闲连接数.最大连接数等可配置参数,在内部则实现连接建 ...

  2. c3p0 mysql 连接池配置文件_数据库连接池c3p0的使用

    原标题:数据库连接池c3p0的使用 来源:java联盟 https://mp.weixin.qq.com/s/5Tbkf8dVFfH8AvtqWl-7Xg 程序员共读整理发布,转载请联系作者获得授权 ...

  3. oracle 尚未从池中获取连接,解决一个问题的思路 之“解决已经写满con.close() 仍然出现但是尚未从池中获取连接的连接池耗尽问题”...

    这篇文章主要写解决一个网上答案不能解决自己问题的时候,解决问题的整个流程, 如果大家觉得这个没价值,还请管理员帮忙撤销发首页 先谢谢各位了. 一年前帮学校做了一个就业信息管理系统, 当时图热闹,觉得虽 ...

  4. mysql odbc连接池_Java Mysql连接池配置和案例分析--超时异常和处理

    前言: 最近在开发服务的时候, 发现服务只要一段时间不用, 下次首次访问总是失败. 该问题影响虽不大, 但终究影响用户体验. 观察日志后发现, mysql连接因长时间空闲而被关闭, 使用时没有死链检测 ...

  5. Druid线程池中的连接什么时候会关闭?

    Druid线程池帮我们实现了应用程序和数据之间的长连接管理,一个线上变更引起了我的疑问,如果我们数据库切换到备用集群,怎么变更? 数据库连接,一般都是域名连接,现在将域名和IP的绑定关系变了,更新ng ...

  6. 连接池和协程池为何能提升并发能力?

    你有没有发现,"内存池"和"进程池"都带有"池"字?其实,这两种技术都属于"池化技术".它通常是由系统预先分配一批资源并 ...

  7. mybatis连接mysql数据库连接池_对于数据库连接池的一些思考和MyBatis的集成与使用...

    Java应用要连接数据库需要先通过jdbc与数据库之间产生connection,然后通过sql语句产生statment,再执行这个statment查询的到ResultSet返回给应用,应用解析Resu ...

  8. java redis释放连接池_Java 使用连接池操作redis

    构建连接池对象JedisPool JedisPool jedisPool = new JedisPool(jedisPoolConfig, "127.0.0.1", 6379); ...

  9. c3p0和jdbctemplate配置oracle集群rac,C3P0连接池、DRUID连接池和JdbcTemplate

    目录 一.C3P0连接池 1.C3P0连接池简介 2.常用的配置参数 3.C3P0连接池基本使用 (1)C3P0配置文件 (2)API介绍 4.使用步骤 二.DRUID连接池 1. DRUID简介 2 ...

最新文章

  1. SpringBoot+MDC实现全链路调用日志跟踪,这才叫优雅!
  2. 转帖-MySQL Innodb日志机制深入分析
  3. 微服务架构中熔断器_基于 Golang 语言的微服务熔断器
  4. struts 普通的action
  5. 企业在管理系统方面要有主动权
  6. Bag of Tricks for Image Classification
  7. Unity C# Job System介绍(一) Job System总览和多线程
  8. python--List extend()方法
  9. 一点桌面计算机为什么打开方式,电脑默认软件打开方式 电脑上默认打开方式在哪设置...
  10. 2019最新升级【超能版】 vbox硬件级虚拟机系统 去虚拟化去vm标识 支持批量启动 批量克隆 CPA网赚挂机电商 virtualbox
  11. 有趣的姓名小知识:你身边有叫沐宸和若汐的宝宝吗?
  12. Windows 10新版可以更新了!这些新功能值得升级
  13. 智能停车场车牌识别计费系统
  14. Java国密加密SM2代码
  15. android换肤动画,Android换肤(二) — 插件式换肤
  16. 三点确定一个圆(输出圆心、弧长、圆心角、方向)
  17. mock 数据常用的工具网站
  18. linux中mut目录,Linux 下常见文件目录及作用
  19. 微信不会把关注取消事件推送给服务器,微信公众平台开发关注及取消关注事件的方法...
  20. 苹果 MAC 电脑 boot camp 助手装 Windows10 双系统出现的各种问题和解决方法

热门文章

  1. CoreData基础
  2. 17岁少年捅死想要性侵女友的歹徒:我坐牢,也不能让她受伤
  3. 深圳市文化创意产业百强(2011-2012)公示
  4. 【老生谈算法】matlab实现轮盘赌算法-Roulette——轮盘赌算法
  5. JSP九大内置对象:
  6. AOP(面向切面)原理及使用
  7. 网络通信学习笔记之 ———Socket网络通信
  8. Vue3 项目遇到的问题
  9. USB与串口的区别简要总结
  10. MySQL中的聚簇索引、非聚簇索引、联合索引和唯一索引