百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程网 > 正文

什么是连接池?如何实现一个Java连接池?

yuyutoo 2024-12-27 17:38 4 浏览 0 评论

什么是连接池?

结构

连接池对外提供接口:

  • 获得连接
  • 归还连接

并暴露客户端可配置的参数:

  • 最小空闲连接数
  • 最大连接数

在内部则实现功能:

  • 连接建立
  • 连接心跳保持
  • 连接管理
  • 空闲连接回收
  • 连接可用性检测
  • 连接池结构示意图

业务中经常也会用到各种连接池:

  • 数据库连接池
  • Redis连接池
  • HTTP连接池

客户端SDK是否基于连接池

在使用三方客户端进行网络通信时,首要确定客户端SDK是否是基于连接池技术实现的。

若客户端SDK没有使用连接池,而直接是TCP连接,就需要考虑每次建立TCP连接的开销,因为TCP基于字节流,若在多线程下对同一连接操作,就有线程安全隐患。

TCP连接的客户端SDK,对外提供API的方式

连接池和连接分离的API

有一个XXXPool类负责连接池实现:

  • 先从其获得连接XXXConnection
  • 再用所获连接请求服务端
  • 完成后归还连接

XXXPool必须是线程安全的,可并发获取和归还连接,而XXXConnection是非线程安全的。 对应到连接池结构示意图,XXXPool就是右边连接池那个框,左边客户端是我们自己的代码。

内部带有连接池的API

对外提供一个XXXClient类,通过该类可直接请求服务端。该类内部维护了连接池,SDK使用者无需考虑连接的获取和归还问题。 XXXClient是线程安全的。对应到连接池结构示意图中,整个API就是蓝框。

非连接池的API

一般命名为XXXConnection,以区分其是基于连接池or单连接,而不建议命名为XXXClient。直接连接方式的API基于单一连接,每次使用都需要创建和断开连接,性能一般,且通常不是线程安全的。对应到连接池的结构示意图中,这种形式相当于没有右边连接池那个框,客户端直接连接服务端创建连接。

不排除有一些客户端特立独行,因此在使用三方SDK时,一定要

  • 查看官方文档了解其最佳实践
  • Stackoverflow搜索XXX threadsafe/singleton
  • 看源码,直到定位到原始Socket来判断Socket和客户端API的对应关系

使用SDK的最佳实践

分离方式

连接池本身一般是线程安全的,可复用。每次使用需要从连接池获取连接,使用后归还,归还的工作由使用者负责。

内置连接池

大多数中间件、数据库的客户端SDK都会支持连接池,SDK负责连接的获取和归还,使用的时候直接复用客户端。

SDK没有实现连接池

那通常不是线程安全的,而且短连接的方式性能不会很高,使用的时候需要考虑是否自己封装一个连接池。

下面看Jedis类到底属于哪种类型的API,直接在多线程环境下复用一个连接会产生什么问题,以及如何用最佳实践来修复这个问题。

首先,向Redis初始化2组数据,Key=a、Value=1,Key=b、Value=2:

@PostConstruct
public void init() {
    try (Jedis jedis = new Jedis("127.0.0.1", 6379)) {
        Assert.isTrue("OK".equals(jedis.set("a", "1")), "set a = 1 return OK");
        Assert.isTrue("OK".equals(jedis.set("b", "2")), "set b = 2 return OK");
    }
}
复制代码

然后,启动两个线程,共享操作同一个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,有的是流非正常结束,还有的是连接关闭异常:

//错误1
[14:56:19.069] [Thread-28] [WARN ] [.t.c.c.redis.JedisMisreuseController:45  ] - Expect b to be 2 but found 1
//错误2
redis.clients.jedis.exceptions.JedisConnectionException: Unexpected end of stream.
	at redis.clients.jedis.util.RedisInputStream.ensureFill(RedisInputStream.java:202)
	at redis.clients.jedis.util.RedisInputStream.readLine(RedisInputStream.java:50)
	at redis.clients.jedis.Protocol.processError(Protocol.java:114)
	at redis.clients.jedis.Protocol.process(Protocol.java:166)
	at redis.clients.jedis.Protocol.read(Protocol.java:220)
	at redis.clients.jedis.Connection.readProtocolWithCheckingBroken(Connection.java:318)
	at redis.clients.jedis.Connection.getBinaryBulkReply(Connection.java:255)
	at redis.clients.jedis.Connection.getBulkReply(Connection.java:245)
	at redis.clients.jedis.Jedis.get(Jedis.java:181)
	at org.geekbang.time.commonmistakes.connectionpool.redis.JedisMisreuseController.lambda$wrong$1(JedisMisreuseController.java:43)
	at java.lang.Thread.run(Thread.java:748)
//错误3
java.io.IOException: Socket Closed
	at java.net.AbstractPlainSocketImpl.getOutputStream(AbstractPlainSocketImpl.java:440)
	at java.net.Socket$3.run(Socket.java:954)
	at java.net.Socket$3.run(Socket.java:952)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.net.Socket.getOutputStream(Socket.java:951)
	at redis.clients.jedis.Connection.connect(Connection.java:200)
	... 7 more

让我们分析一下Jedis类的源码,搞清楚其中缘由吧。

BinaryClient封装了各种Redis命令

都是调用其父类Connection方法,使用Protocol类发送命令

Protocol类的sendCommand方法

发送命令时是直接操作RedisOutputStream写字节。

在多线程环境下复用Jedis对象,其实就是在复用RedisOutputStream。如果多个线程在执行操作,那么既无法确保整条命令以一个原子操作写入Socket,也无法确保写入后、读取前没有其他数据写到远端。 这就能解释了为何多线程下使用Jedis对象操作Redis会出现各种问题:

  • 写操作互相干扰,多条命令交织,必然是非法的Redis命令,则Redis会关闭客户端连接,导致连接断开
  • 线程1和2先后写入get a和get b请求,Redis也返回了值1和2,但是线程2先读取了数据1就会出现数据错乱的问题。

那么如何修复呢? 使用Jedis提供的线程安全的类JedisPool来获得Jedis的实例。JedisPool作为连接池,可以声明为static 被多线程共享。注意使用try-with-resources模式。 这样使用后代码不再有线程安全问题。最好再通过shutdownhook,在程序退出之前关闭JedisPool:

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

Jedis#close

若Jedis是从连接池获取的话,则close方法会调用连接池的return方法归还连接:

如果不是,则直接关闭连接,其最终调用Connection类的disconnect方法来关闭TCP连接:

可见Jedis可独立使用,也可配合连接池(JedisPool)

JedisPool

  • JedisPool继承JedisPoolAbstract又继承抽象类Pool,Pool内部持有Apache Common的GenericObjectPool。

所以JedisPool的连接池其实就是直接复用的GenericObjectPool,并没有自己实现一套池子。

综上,Jedis API属于连接池和连接分离的API,JedisPool是线程安全的连接池,Jedis是非线程安全的单一连接。

作者:JavaEdge
原文链接:https://juejin.cn/post/6941292700456976398

相关推荐

TCP协议原理,有这一篇就够了

先亮出这篇文章的思维导图:TCP作为传输层的协议,是一个软件工程师素养的体现,也是面试中经常被问到的知识点。在此,我将TCP核心的一些问题梳理了一下,希望能帮到各位。001.能不能说一说TC...

Win10专业版无线网络老是掉线的问题

有一位电脑基地的用户,使用...

学习计算机网络需要掌握以下几方面基础知识

计算机基础知识操作系统:了解常见操作系统(如Windows、Linux)的基本操作和网络配置,例如如何设置IP地址、子网掩码、网关和DNS服务器等,以及如何通过命令行工具(如ping、tr...

网络工程师的圣经!世界级网工手绘268张图让TCP/IP直接通俗易懂

要把知识通俗地讲明白,真的不容易。——读者说TCP/IP从字面意义上讲,有人可能会认为TCP/IP是指TCP和IP两种协议。实际生活当中有时候也确实就是这两种协议。然而在很多情况下,它只是...

三分钟了解通信知识TCP与IP协议(含“通信技术”资料分享)

TCP/IPTCP/IP分层模型①应用层...

网闸与防火墙:网络安全设备的差异与应用

在网络安全领域,网闸(安全隔离网闸,GAP)和防火墙(Firewall)是两类重要的防护设备。尽管它们都服务于网络安全防护,但在设计理念、技术原理、安全效能及适用场景等方面存在显著差异,以下从五个维度...

S7-300的TCP/IP通信

一、首先在项目中创建2个S7-300的站点;二、硬件组态中,设置合适的TCP/IP地址,在同一网段内;...

西门子S7-1500 PLC的 MODBUS TCP通信

MODBUSTCP使MODBUS_RTU协议运行于以太网,MODBUSTCP使用TCP/IP和以太网在站点间传送MODBUS报文,MODBUSTCP结合了以太网物理网络和网络标准TC...

系统规划与管理师新版备考必备:第7章考点思维导图解析

备考系统规划与管理师的小伙伴们,福利又来啦!今天为大家带来《系统规划与管理师(第2版)》第7章考点的思维导图,助你高效梳理重点,让备考更有方向!...

TCP/IP、Http、Socket 有何区别与联系?

HTTP协议对应于应用层,Socket则是对TCP/IP协议的封装和应用(程序员层面上)。HTTP是应用层协议,主要解决如何包装数据。而我们平时说的最多的Socket是什么呢?实际上...

西门子PLC串口协议与以太网通信协议对比

西门子plc品牌众多,通信协议的类型就更多了,具体可分为串口协议和以太网通信协议两大类。...

网络编程懒人入门(十三):一泡尿的时间,快速搞懂TCP和UDP的区别

本文引用了作者Fundebug的“一文搞懂TCP与UDP的区别”一文的内容,感谢无私分享。1、引言...

程序员必备的学习笔记《TCP/IP详解(一)》

为什么会有TCP/IP协议在世界上各地,各种各样的电脑运行着各自不同的操作系统为大家服务,这些电脑在表达同一种信息的时候所使用的方法是千差万别。就好像圣经中上帝打乱了各地人的口音,让他们无法合作一样...

一文读懂TCP/IP协议工作原理和工作流程

简述本文主要介绍TCP/IP协议工作原理和工作流程。含义TCP/IP协议,英文全称TransmissionControlProtocol/InternetProtocol,包含了一系列构成互联网...

如何在 Windows 10 和 Windows 11 上重置 TCP/IP 堆栈

传输控制协议/Internet协议,通常称为TCP/IP,是您的WindowsPC如何与Internet上的其他设备进行通信的关键部分。但是当事情出错时会发生什么?你如何解决它?幸运的...

取消回复欢迎 发表评论: