背景
其实是两年之前处理的一个现场问题了,因为最近学习极客时间关于TCP
优化的内容,又把这事想了起来,权且记录下。
现场问题
某个 Java 应用报数据库连接超时
。
原因很好定位,应用和数据库在不同的网络,应用的数据库连接请求被防火墙
拦了。
昨天晚上还是好好的,过了一夜就不行了,防火墙规则也没变过...
一开始没有思路,后来在DBA
提醒下,查了几个参数
[root@localhost ~]# cat /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_established
7200
这个参数是防火墙的连接超时时间
,说实话有点奇怪,配的也太短了(2 小时),不会有问题么?
[root@localhost ~]# sysctl net.ipv4.tcp_keepalive_time
net.ipv4.tcp_keepalive_time = 7200
[root@ocalhost ~]# sysctl net.ipv4.tcp_keepalive_intvl
net.ipv4.tcp_keepalive_intvl = 75
[root@ocalhost ~]# sysctl net.ipv4.tcp_keepalive_probes
net.ipv4.tcp_keepalive_probes = 9
这几个参数,决定了TCP 连接
的工作机制:
- 在 7200 秒(2 小时)空闲时间后,TCP 开始发送第一个 Keepalive 探测包。
- 如果没有收到响应,将每隔 75 秒发送一个新的探测包。
- 总共发送 9 个探测包。如果仍然没有响应,TCP 将认为连接已失效并
关闭连接
两边都配置的是 2 小时,正好我们的应用长时间没有活动,于是 TCP 连接被拆除了。
这么看的话,应该是一类典型场景:
应用与数据库在不同的网络中,连接需要经过防火墙,怎么能够使应用和数据库之间即使比较空闲也能够保持一定数量的长连接
三种解决方案
方案一:修改连接池参数配置
Druid
数据库连接池提供一些参数,可以每隔一段时间执行固定SQL
,用以保持连接可用
testWhileIdle
:设置为 truetimeBetweenEvictionRunsMillis
:间隔时间,注意要小于连接超时时间validationQuery
:要执行的sql
但这种方式只适用于当个应用,数据库日志会记录大量无效 sql,不太推荐。
方案二:修改操作系统的 keepalive 参数
在 CentOS 系统中,TCP Keepalive 功能默认是启用的。
所以我们只需要把tcp探测包发送的时间设置成小于防火墙的连接超时时间,防火墙就会检查到连接中仍然有数据传输,就不会断开这个连接
# 临时修改
sysctl -w net.ipv4.tcp_keepalive_time=600
# 永久修改,编辑/etc/sysctl.conf
net.ipv4.tcp_keepalive_time = 600
方案三:数据库参数
MySQL、PostgreSQL、Oracle 等主流数据库提供了类似操作系统的 tcpKeepAlive 参数。
需要在jdbc
上配置连接参数
tcpKeepAlive=true
数据库也都提供了参数来配置超时时长,MySQL 是 net_read_timeout,PostgreSQL 是 tcp_keepalives_idle,其它的就不赘述了。
不过这种方式比较麻烦,需要修改每个应用的jdbc
连接参数。
结尾
欢迎纠错,欢迎补充,欢迎评论。
以上就是全部内容,感谢阅读。
本文由mdnice多平台发布
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。