Java 业务开发常见错误 100 例
朱晔
贝壳金服资深架构师
52944 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 48 讲
代码篇 (23讲)
Java 业务开发常见错误 100 例
15
15
1.0x
00:00/00:00
登录|注册

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

HTTP连接池
Redis连接池
数据库连接池
内部实现连接建立、连接心跳保持、连接管理、空闲连接回收、连接可用性检测等功能
暴露最小空闲连接数、最大连接数等可配置参数
对外提供获得连接、归还连接的接口
针对数据库连接池、HTTP连接池、Redis连接池等建立完善的监控和报警机制
监控验证参数是否生效
MongoDB Java驱动中的MongoClient类
设置连接超时时间
参数调整
监控和报警机制
最大连接数
显式关闭连接池释放资源
确保连接池是复用的
非连接池的API
内部带有连接池的API
池和连接分离的API
常见连接池
结构
坑与经验分享
思考与讨论
连接池参数配置
使用姿势
客户端SDK连接池实现方式
连接池
用对连接池,别让它帮了倒忙

该思维导图由 AI 生成,仅供参考

你好,我是朱晔。今天,我们来聊聊使用连接池需要注意的问题。
在上一讲,我们学习了使用线程池需要注意的问题。今天,我再与你说说另一种很重要的池化技术,即连接池。
我先和你说说连接池的结构。连接池一般对外提供获得连接、归还连接的接口给客户端使用,并暴露最小空闲连接数、最大连接数等可配置参数,在内部则实现连接建立、连接心跳保持、连接管理、空闲连接回收、连接可用性检测等功能。连接池的结构示意图,如下所示:
业务项目中经常会用到的连接池,主要是数据库连接池、Redis 连接池和 HTTP 连接池。所以,今天我就以这三种连接池为例,和你聊聊使用和配置连接池容易出错的地方。

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

在使用三方客户端进行网络通信时,我们首先要确定客户端 SDK 是否是基于连接池技术实现的。我们知道,TCP 是面向连接的基于字节流的协议:
面向连接,意味着连接需要先创建再使用,创建连接的三次握手有一定开销;
基于字节流,意味着字节是发送数据的最小单元,TCP 协议本身无法区分哪几个字节是完整的消息体,也无法感知是否有多个客户端在使用同一个 TCP 连接,TCP 只是一个读写数据的管道。
如果客户端 SDK 没有使用连接池,而直接是 TCP 连接,那么就需要考虑每次建立 TCP 连接的开销,并且因为 TCP 基于字节流,在多线程的情况下对同一连接进行复用,可能会产生线程安全问题
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

连接池在软件开发中扮演着重要角色,但其配置和使用中可能存在一些问题。本文深入探讨了数据库连接池、Redis连接池和HTTP连接池的使用和配置容易出错的地方,并提出了解决多线程环境下连接复用可能带来的问题的解决方案。通过对Jedis的源码分析,作者指出了在多线程环境下复用Jedis对象可能导致的线程安全问题,并提出了使用JedisPool来获得Jedis实例以修复这些问题的最佳实践。文章通过具体的案例和源码分析,帮助读者更好地理解了连接池的使用方式和注意事项,对于开发人员具有一定的参考价值。文章还通过具体案例和源码分析,帮助读者更好地理解了连接池的使用方式和注意事项,对于开发人员具有一定的参考价值。文章还强调了连接池参数配置的重要性,特别是最大连接数的设置,以及建立完善的监控和报警机制的必要性。文章内容技术性较强,适合对连接池有一定了解的读者阅读。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《Java 业务开发常见错误 100 例》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(53)

  • 最新
  • 精选
  • Darren
    置顶
    实操性比较强,收获满满!!! 自从spring boot 2.x版本后,有较大的改动: 默认的redis的链接池从JedisPool变成了LettucePool,Lettuce主要利用netty实现与redis的同步和异步通信。所以更安全和性能更好; 默认的数据库连接池也变更为HikariCP,HiKariCP 号称是业界跑得最快的数据库连接池,HiKariCP 官方网站解释了其性能之所以如此之高的秘密。微观上 HiKariCP 程序编译出的字节码执行效率更高,站在字节码的角度去优化 Java 代码,HiKariCP 的作者对性能的执着可见一斑,不过遗憾的是他并没有详细解释都做了哪些优化。而宏观上主要是和两个数据结构有关,一个是 FastList,另一个是 ConcurrentBag。 FastList是对Java List的增强,HiKariCP作者认为Java SDK的List在其使用场景下比较慢,因此在SDK提供的List(ArrayList的remove)的基础上做了增强; ConcurrentBag是对Java并发集合的增强, 通过 ThreadLocal 做一次预分配,避免直接竞争共享资源,非常适合池化资源的分配。 试着回答下课后的问题: 第一个问题: JedisPool的设置: 获取链接超时:maxWait TCP超时:JedisPool中有一个soTimeout的属性,在链接的时候,使用socket.setSoTimeout(soTimeout)控制的。 HikariCP的设置: 获取链接超时:connectionTimeout。 This property controls the maximum number of milliseconds that a client (that's you) will wait for a connection from the pool. If this time is exceeded without a connection becoming available, a SQLException will be thrown. Lowest acceptable connection timeout is 250ms. Default: 30000 (30 seconds) TCP超时:数据的库的wait_timeout属性吧 Apache HttpClient设置: 获取链接超时:connectionRequestTimeout 建立链接超时:connectionTimeout 等待响应超时:socketTimeout 第二个问题就不回答了,因为到目前为止,还没有在生产使用过Mongo,😂😂😂😂 上面有些参数感觉说的不对,请老师指点

    作者回复: 假设我们希望设置连接超时5s,获取连接超时10s: hikari两个参数设置方式: spring.datasource.hikari.connection-timeout=10000 spring.datasource.url=jdbc:mysql://localhost:6657/common_mistakes?connectTimeout=5000&characterEncoding=UTF-8&useSSL=false&rewriteBatchedStatements=true jedis两个参数设置: JedisPoolConfig config = new JedisPoolConfig(); config.setMaxWaitMillis(10000); try (JedisPool jedisPool = new JedisPool(config, "127.0.0.1", 6379, 5000); Jedis jedis = jedisPool.getResource()) { return jedis.set("test", "test"); } httpclient两个参数设置: RequestConfig requestConfig = RequestConfig.custom() .setConnectTimeout(5000) .setConnectionRequestTimeout(10000) .build(); HttpGet httpGet = new HttpGet("http://127.0.0.1:45678/twotimeoutconfig/test"); httpGet.setConfig(requestConfig); try (CloseableHttpResponse response = httpClient.execute(httpGet)) {...

    2020-03-16
    5
    67
  • 👽
    置顶
    个人总结: 1. 池化技术的核心在于,在鱼塘养好一群鱼,需要的时候就从里面拿一条,用完再放回去。而不是自己生产一条鱼,然后用完就销毁。从而减少了开销。 2. 大多已经实现的连接池,都是有线程安全处理的。通常比个人创建管理连接更加安全。 3. 使用了连接池技术,就要保证连接池能够被有效复用。频繁创建连接池比频繁创建链接更加耗费资源。 4. 连接池的参数配置要根据实际情况,并不存在多多益善 5. 连接池的主要好处:(1)减少资源消耗,(2)利用现有的线城安全实现,(3)提升并发量

    作者回复: 总结的不错

    2020-03-16
    2
    13
  • Wiggle Wiggle
    置顶
    请问对于连接池的监控,是把监控系统直连JMX,监控、修改操作都走连接池已经实现好的JMX比较好?还是自己做一层封装,对外暴露接口,以编程方式获取、设置参数比较好?

    作者回复: 自己封装必要不大,然后如果需要走http的话用jolokia,或者使用prometheus的jmx_exporter以agent方式暴露mbeans,https://github.com/prometheus/jmx_exporter

    2020-03-14
    38
  • 蚂蚁内推+v
    退出程序前为什么要关闭连接池啊,程序都结束了连接不就释放了么

    作者回复: 优雅关闭总是更好的

    2020-03-14
    3
    11
  • 每天晒白牙
    干货满满,还需要慢慢消化一下

    作者回复: 如有收货,欢迎转发

    2020-03-14
    8
  • 👽
    课后题2: 受限于本人英文水平,无奈与使用谷歌翻译阅读文档。从文档中得知,MongoClient 对象的正确使用姿势应该是:使用 MongoClients.create()(或者其他有参) 方法创建,并再整个应用程序中使用它。文档内容如下: MongoClient (从3.7版本开始) 一个MongoClient实例表示到数据库连接池; MongoClient即使有多个线程,您也只需要一个类的实例。 重要 通常,您只MongoClient为给定的MongoDB部署创建一个实例(例如独立实例,副本集或分片群集),并在整个应用程序中使用它。但是,如果您确实创建了多个实例: 所有资源使用限制(例如,最大连接数等)适用于每个MongoClient实例。 要处置实例,请致电MongoClient.close()以清理资源。

    作者回复: 是的

    2020-03-16
    3
    6
  • 👽
    hikari具体配置项为application.yml 中 spring.datasource.hikari.connection-timeout 点进去可以发现是 HikariDataSource 类,继承了HikariConfig。 点进HikariConfig可看出 connectionTimeout不允许小于250毫秒,小于250ms会被强制重置为30秒。 参数connectionTimeout定义是并未赋初始值的原始类型long,初始值应该是0L; 所以,个人判断,默认的connectionTimeout数值就是30秒。 如有纰漏,欢迎指正

    作者回复: 回答一下Hikari的配置,其ConnectionTimeout是从连接池获取数据库连接的超时,不是和MySQL建立连接的超时,后者需要设置JDBC连接字符串中的connectTimeout属性。对于Hikari的JavaConfig配置这2个参数的方式是: @Bean public DataSource dataSource(){ HikariConfig hikariConfig = new HikariConfig(); hikariConfig.setConnectionTimeout(2400); hikariConfig.setJdbcUrl("jdbc:mysql://localhost:6658/common_mistakes"); hikariConfig.addDataSourceProperty("connectTimeout", "1200"); HikariDataSource dataSource = new HikariDataSource(hikariConfig); return dataSource; }

    2020-03-16
    3
    5
  • Husiun
    每次更新都是第一时间打开,每一课都干货满满,必须给老师赞一个,http那个平时研究不多还需要好好消化一下。

    作者回复: 有收货就好

    2020-03-14
    5
  • justin
    老师你好,看了这篇文章感觉收获满满,然后关于上面的CloseableHttpClient有个几个疑问: 1、复用同一个tcp连接的时候比每次都创建一个新的tcp连接的QPS高很多,当有大量http请求服务端时,每个http连接都共用同一个tcp连接时,这种情况下不会造成其中一些http请求的响应速度变慢吗。 2、 httpClient = HttpClients.custom().setMaxConnPerRoute(1).setMaxConnTotal(1).evictIdleConnections(60, TimeUnit.SECONDS).build(); 当我尝试去扩大setMaxConnTotal这个最大连接数时,qps反而降低了。如果线上有上千qps的话,设置连接数为1就可以了吗。

    作者回复: 1、其实本文说的点不是复用连接,而是复用连接池,也就是CloseableHttpClient,一个TCP连接同时自然无法实现多个HTTP请求的复用 2、文中设置为1只是举例,实际应用的时候显然应该设置一个合理的最大值,不能是1。扩大MaxConnPerRoute和MaxConnTotal之后qps降低,这个有没有实际的性能数据?你是怎么测试的呢?

    2020-03-26
    2
    4
  • pedro
    干货很多,收获很大。问老师一个问题,使用hook来关闭连接池的时候,都会创建一个线程,那如果有多个连接池,每个连接池都有一个线程来调用hook,这样做是否有点奢侈,有没有更优的办法?

    作者回复: 可以都放到一个Thread,放到多个的话会并行执行,只要不是太多问题不大。或者用Spring的话还可以使用@PreDestroy来实现资源释放。

    2020-03-14
    2
    4
收起评论
显示
设置
留言
53
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部