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

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

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

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

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

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

    作者回复: 总结的不错

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

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

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

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

    3
    10
  • 每天晒白牙
    干货满满,还需要慢慢消化一下

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

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

    作者回复: 是的

    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; }

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

    作者回复: 有收货就好

    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降低,这个有没有实际的性能数据?你是怎么测试的呢?

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

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

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