数据库连接池长时间不用,乍一用还用不了,结果是防火墙的锅

数据库,连接池,长时间,不用,还用,不了,结果,防火墙 · 浏览次数 : 12

小编点评

## 生成内容时需要带简单的排版 以下内容生成时需要带简单的排版,例如: * 使用tab键将内容按顺序排列 * 使用空格将内容按照顺序排列 * 使用换行将内容按顺序排列 以下内容为例: * 使用tab键将内容按顺序排列 ``` client.c,就是连接并发送请求。 服务端发起监听,客户端发起连接,完成三次握手:[root@node1 ~]# ./server [root@node2 ~]# ./client please input: ``` * 使用空格将内容按照顺序排列 ``` client.c,就是连接并发送请求。 服务端发起监听,客户端发起连接,完成三次握手:[root@node1 ~]# ./server [root@node2 ~]# ./client please input: ``` * 使用换行将内容按顺序排列 ``` client.c,就是连接并发送请求。 服务端发起监听,客户端发起连接,完成三次握手:[root@node1 ~]# ./server [root@node2 ~]# ./client please input: ``` **最终生成的内容如下:** client.c,就是连接并发送请求。 服务端发起监听,客户端发起连接,完成三次握手。 **注意:** * 以上只是一个例子,根据具体需求可以进行调整 * 使用不同的排版方法可以提升内容的清晰度

正文

前言

我们的程序,在实际的网络部署时,一般比较复杂,会经过很多的网络设备,防火墙就是其中的一种。做开发的同事,一般对这块了解不多,也很可能被防火墙坑到。比如,应用一般需要访问数据库,为了避免频繁建立连接,一般是会提前建立一个连接池,每次来一个请求,就从连接池取一个连接来用,用完再归还到池子里。

连接池中的连接是啥呢,其实就是和数据库之间的完成了三次握手后的socket,这个socket在白天时,一般经常有数据传输,而到了凌晨这种,可能就很少数据传输,等到了第二天,某个请求来了,从池子里取了某个socket,就直接发送数据,此时,很可能就会出现一个read timeout的情况。

为啥呢,是数据库不返回数据吗?不一定,如果应用服务器和db服务器之间,经过了防火墙的话,很可能,你这个socket发出去的包,直接就防火墙给丢弃了,根本没有到达数据库。如果想搜索这块相关的案例,可以搜索“防火墙 长连接”关键字。

状态防火墙(stateful firewall)

现在的防火墙,基本都是有状态的,什么叫做有状态呢?以tcp为例,当服务端收到第一次握手(syn)时,此时,会认为这是要新建立连接,当三次握手完成后,再收到客户端在该socket上发来的请求报文时,此时,就知道这个报文不再是要新建立连接了,socket的状态此时也是established。说白了,握手时候的报文和握手完成后的报文,是不一样的,我们是可以区别对待的,这就是通俗意义上的“有状态”。

而在此之前,都是无状态防火墙(stateless firewall),不管是什么报文,都是一视同仁,没法根据状态来区分处理。

接下来,我们可以以iptables为例,理解下状态防火墙(iptables就是典型的状态防火墙)。

iptables中的状态

该部分参考官网:https://www.frozentux.net/iptables-tutorial/iptables-tutorial.html#STATEMACHINE

iptables是用户态中的命令,在内核中由netfilter实现,netfilter中实现连接的状态跟踪的机制,叫做conntrack(connection track)。在这个机制中,一共定义了4种状态:NEW, ESTABLISHED, RELATED,INVALID,UNTRACKED。

注意这里的conntrack,中文就是连接追踪,既然是连接,那么,我们怎么区分一个连接呢,比如,在tcp中,就是依靠四元组,这个四元组就可以唯一标识一个连接。

我们可以定义一个hash表,在收到一个tcp报文时,检查下这个四元组在hash表中是否存在(key就是四元组),如果不存在,我们就可以认为这个报文此时是新来的,也就是状态是new;当我们收到服务端返回的报文时,我们又可以检测下四元组在本地hash表是否存在,发现已经存在了,此时就可以认为状态是established。

这里大家如果要看这个状态的详细解释,可以查看官网文档;另外,我发现一篇文章也不错:

https://mp.weixin.qq.com/s/kv32lyWak4dCaPjMI4_6jw

NEW:
新建连接请求的数据包,且该数据包没有和任何已有连接相关联。 判断的依据是conntrack当前“只看到一个方向数据包(UNREPLIED)”,没有回包。
ESTABLISHED:
该连接是某NEW状态连接的回包,也就是完成了连接的双向关联。

其他几种状态,RELATEDINVALID我感觉一般比较少用,至于UNTRACKED ,可以等后续再慢慢领悟。

实际感受状态变化

客户端首次握手(syn)

可以通过cat /proc/net/nf_conntrack查看连接状态。

如收到首次握手请求后,会看到如下内容:

tcp      6 117 SYN_SENT src=192.168.1.5 dst=192.168.1.35 sport=1031 \
     dport=23 [UNREPLIED] src=192.168.1.35 dst=192.168.1.5 sport=23 \
     dport=1031 use=1

各个字段的意思:

  • tcp,协议名

  • 6,传输层协议代号,其中tcp是6,udp是17,

  • 117,ttl,表示这个conntrack的过期时间,类似于redis里的key的ttl

  • SYN_SENT,socket此时的状态。官方:The value SYN_SENT tells us that we are looking at a connection that has only seen a TCP SYN packet in one direction

  • src=192.168.1.5 dst=192.168.1.35 sport=1031 dport=23部分:

    the source IP address, destination IP address, source port and destination port

  • UNREPLIED(未回复)

    we have seen no return traffic for this connection,表示我们还没看到回包,现在只有单方向的包

  • src=192.168.1.35 dst=192.168.1.5 sport=23 dport=1031

    这是我们期待的回包的样子

服务端返回syn+ack

tcp      6 57 SYN_RECV src=192.168.1.5 dst=192.168.1.35 sport=1031 \
     dport=23 src=192.168.1.35 dst=192.168.1.5 sport=23 dport=1031 \
     use=1

收到syn+ack后,此时,我们的状态由SYN_SENT变成SYN_RECV,此时,也移除了之前的UNREPLIED关键字

客户端第三次握手 ack

tcp      6 431999 ESTABLISHED src=192.168.1.5 dst=192.168.1.35 \
     sport=1031 dport=23 src=192.168.1.35 dst=192.168.1.5 \
     sport=23 dport=1031 [ASSURED] use=1

状态变成ESTABLISHED,增加了关键字:ASSURED

其他

涉及到socket关闭的部分,导致的状态变化请参考官方文档:https://www.frozentux.net/iptables-tutorial/iptables-tutorial.html#STATEMACHINE

image-20230926220825759

简单来说,就是两边都完成关闭,状态才变成closed。

状态标记发生的阶段

我们上面可以看到在一个connection上状态的变化,说白了,这部分就是给一个报文打上标记,关于状态的标记,后续我们才能基于这个状态label进行匹配。

所以,我们对于这个标记发生的时间,需要特别注意,可以看下,下图的左上角的绿色方块(connection tracking)就是状态标记发生的位置,基本上是最前面了(最上面是网卡,再下来是raw,再下来就是connection tracking了)。

在这个绿色方块执行完成后,状态就已经标记好了,我们就可以根据状态来匹配这些报文来进行accept或者drop了,具体看下文。

4515962123a9190c28252a1d8dbedc4

iptables模拟长连接超时后继续使用该连接的场景

部署图

我们的两台机器如下:

image-20230926221456416

服务器:10.0.2.15:2222,监听2222端口

客户端:10.0.2.4

iptables配置

放行第一次握手请求

三次握手的报文都需要放行,第一次握手请求的特征是,此时,根据前面我们讲的状态,第一次握手到来时,本地是找不到该四元组的,所以state是new,再加上目的端口是2222,因此,我们的匹配条件是:

--dport 2222 -m state --state NEW,因此,命令如下:

iptables -I INPUT 4 -p tcp --dport 2222 -m state --state NEW -j ACCEPT

放行第二次、第三次握手请求

此时,马上会收到2222端口返回的syn + ack,此时,状态就会变成established。

这种报文的特征是,state为established,这种怎么放心呢,这种其实默认就放心了,不需要我们再干啥。

image-20230926222413959

第三次请求,也是同理,状态会被标记为established。

等待conntrack记录过期或手动删除、清空

我们这边就需要模拟连接建立后,等conntrack记录过期后,客户端再使用这个socket会发生什么。

那么,我们怎么来查看这些conntrack呢,需要安装工具:yum install conntrack-tools

查看hash表
[root@node1 ~]# conntrack -L
tcp      6 431997 ESTABLISHED src=10.0.2.2 dst=10.0.2.15 sport=9526 dport=22 src=10.0.2.15 dst=10.0.2.2 sport=22 dport=9526 [ASSURED] mark=0 secctx=system_u:object_r:unlabeled_t:s0 use=1

我们可以看到过期时间为431997,单位是秒,差不多是5天。这个初始值是432000,来自于内核参数:net.netfilter.nf_conntrack_tcp_timeout_established :

[root@node1 ~]# sysctl -a |grep net.netfilter.nf_conntrack_tcp_timeout_established
net.netfilter.nf_conntrack_tcp_timeout_established = 432000

我们测试的话,这个时间就太长了,我们可以修改这个内核参数进行设置。我们可以改成60s过期:

[root@node1 ~]# vim /etc/sysctl.conf
net.netfilter.nf_conntrack_tcp_timeout_established = 60

执行sysctl -p

我们也可以选择手动删除、清空这些conntrack记录:

删除目标端口为2222的记录:
conntrack -D -p tcp --dport 2222

清空整个hash表
conntrack -F

实时监控hash表的变动(增删改)
conntrack -E

丢弃客户端在长时间空闲的长连接上发过来的包

由于我们上一步执行了conntrack记录删除,此时,客户端发的报文,在iptables看来,又是全新的四元组,也就是会把状态标记为new,但是,这种请求报文和握手报文明显不同,这种报文的tcp层一般会设置psh标记,握手请求里则会设置syn等,所以,我们按照这个进行区分。

匹配条件为:--dport 2222 -m state --state NEW -m tcp --tcp-flags PSH PSH,动作为丢弃:drop

iptables -I INPUT 4 -p tcp --dport 2222 -m state --state NEW -m tcp  --tcp-flags PSH PSH  -j DROP

ok,接下来开始测试。

最终的iptables效果

image-20230926225144983

实际测试

用到了该博客中的客户端、服务端程序:

https://blog.csdn.net/fengcai_ke/article/details/125717134

server.c,主要是监听2222端口;client.c,就是连接并发送请求。

服务端发起监听,客户端发起连接,完成三次握手:

[root@node1 ~]# ./server 

[root@node2 ~]# ./client 
please input:

此时,可以看到三次握手:

image-20230926224609816

服务端的conntrack -E,也可以看到hash表的变化:

[root@node1 ~]# conntrack -E
    [NEW] tcp      6 120 SYN_SENT src=10.0.2.4 dst=10.0.2.15 sport=46216 dport=2222 [UNREPLIED] src=10.0.2.15 dst=10.0.2.4 sport=2222 dport=46216
 [UPDATE] tcp      6 60 SYN_RECV src=10.0.2.4 dst=10.0.2.15 sport=46216 dport=2222 src=10.0.2.15 dst=10.0.2.4 sport=2222 dport=46216
 [UPDATE] tcp      6 432000 ESTABLISHED src=10.0.2.4 dst=10.0.2.15 sport=46216 dport=2222 src=10.0.2.15 dst=10.0.2.4 sport=2222 dport=46216 [ASSURED]

可以看到established的conntrack:

[root@node1 ~]# conntrack -L|grep 2222
conntrack v1.4.4 (conntrack-tools): 5 flow entries have been shown.
tcp      6 431857 ESTABLISHED src=10.0.2.4 dst=10.0.2.15 sport=46216 dport=2222 src=10.0.2.15 dst=10.0.2.4 sport=2222 dport=46216 [ASSURED] mark=0 secctx=system_u:object_r:unlabeled_t:s0 use=1

接下来,删除conntrack记录:

删除记录:
[root@node1 ~]# conntrack -D -p tcp --dport 2222
tcp      6 431800 ESTABLISHED src=10.0.2.4 dst=10.0.2.15 sport=46216 dport=2222 src=10.0.2.15 dst=10.0.2.4 sport=2222 dport=46216 [ASSURED] mark=0 secctx=system_u:object_r:unlabeled_t:s0 use=1
conntrack v1.4.4 (conntrack-tools): 1 flow entries have been deleted.
查询:
[root@node1 ~]# conntrack -L|grep 2222
conntrack v1.4.4 (conntrack-tools): 4 flow entries have been shown.

客户端使用该socket发起请求:

[root@node2 ~]# ./client 
please input:11
send result
: Success

可以看到,服务端2222,没回复,客户端一直重传:

image-20230926224959627

观察iptables,可以发现,确实匹配上了drop那条:

image-20230926225312796

参考

https://blog.csdn.net/fengcai_ke/article/details/125717134

https://blog.csdn.net/weixin_46768610/article/details/109354433

https://www.cnblogs.com/saolv/p/13096965.html

https://mp.weixin.qq.com/s/kv32lyWak4dCaPjMI4_6jw

https://mp.weixin.qq.com/s/bXWAd62R8-g0PyInB9MFIQ

https://mp.weixin.qq.com/s/vTys4GJH_tH5jGrDmR2etw

与数据库连接池长时间不用,乍一用还用不了,结果是防火墙的锅相似的内容:

数据库连接池长时间不用,乍一用还用不了,结果是防火墙的锅

前言 我们的程序,在实际的网络部署时,一般比较复杂,会经过很多的网络设备,防火墙就是其中的一种。做开发的同事,一般对这块了解不多,也很可能被防火墙坑到。比如,应用一般需要访问数据库,为了避免频繁建立连接,一般是会提前建立一个连接池,每次来一个请求,就从连接池取一个连接来用,用完再归还到池子里。 连接

弹性数据库连接池探活策略调研(一)——HikariCP

# 调研背景: 数据库连接建立是比较昂贵的操作(至少对于 OLTP),不仅要建立 TCP 连接外还需要进行连接鉴权操作,所以客户端通常会把数据库连接保存到连接池中进行复用。连接池维护到弹性数据库(JED)的长连接,弹性数据库默认不会主动关闭客户端连接(除非报错),但一般客户端到弹性数据库之间还会有负

Mysql数据库部分管理命令极简学习总结

背景 今天遇到一个得很奇怪的问题. Mysql一个运行时间很长的select阻塞了对select里面左连接表做create index 操作的SQL 当时感觉不应该, 一直以为读锁不会与独占更新锁互斥. 经过与公司数据库大牛沟通, 得出结论如下: 在mysql做ddl语句的时候一定要特别小心 sel

你们的优雅停机真的优雅吗?

emm,又又遇到问题啦,现有业务系统应用上线存在窗口期,不能满足正常任务迭代上线。在非窗口期上线容易导致数据库、mq、jsf等线程中断,进而导致需要手动修单问题。故而通过添加优雅停机功能进行优化,令其在上线前选择优雅停机后,会优先断掉新流量的涌入,并预留一定时间处理现存连接,最后完全下线,可有效扩大上线预留窗口时间并降低上线期间线程中断,进而降低手动修单。可是什么是优雅停机呢?为什么现有的系统技术没有原生的优雅停机机制呢?通过调研整理文章如下。

[转帖]使用电信号传输TCP/IP:如何收发数据包(MTU,MSS,包的序号SYN,确认号ACK,动态调整等待ACK时长,滑动窗口)

https://www.jianshu.com/p/a819a777d33c 连接建立起来后,也就是TCP建链后也就进入数据传输阶段。数据收发操作是从应用程序调用write将要发送的数据交给协议栈开始的,协议栈收到数据后执行发送操作。 首先,协议栈并不关心应用程序传来的数据是什么内容。应用程序在调用

iptables防火墙调试,想打印个日志就这么难

# 背景 怎么会讲这个话题,这个说来真的长了。但是,长话短说,也是可以的。 我前面的文章提到,线上的服务用了c3p0数据库连接池,会偶发连接泄露问题,而分析到最后,又怀疑是db侧主动关闭连接,或者是服务所在机器和db之间有防火墙,防火墙主动关闭了连接。导致我们这边socket看着还健康,实际在对端已

[转帖]Linux内核网络协议知识补充

https://zhuanlan.zhihu.com/p/597798185 1. 什么是连接?什么情况下算建立了连接? 通过三次握手, 建立连接. 并且源和目的分配了相应资源(缓存等). 2. 什么是心跳? 建立了连接, 很长时间未发送数据包, 连接本身仍是有效的 但存在不可抗力(网线断了), 系

Apache IoTDB C# SDK 介绍

最近今天写了IoTDB的三篇相关文章,完成了安装部署和客户端连接:Windows Server上部署IoTDB 集群DBeaver 连接IoTDBDriver将IoTDB注册为Windows服务TsFile 是 IoTDB 的底层数据文件,一种专门为时间序列数据设计的列式文件格式。IoTDB TsF

长连接与短连接以及线程数相关的比较

长连接与短连接以及线程数相关的比较 摘要 对比了nginx与tomcat作为静态业务服务的性能之后 突然又想比较一下 长连接与否的性能情况. 很多时候恰当的配置还是非常重要的. 测试方法 分别使用长连接和短连接进行测试工作. 主要命令为: 短连接 ab -c 1000 -n 50000 -r htt

我在京东做研发丨【混合多云第一课】为何多云多活被称为“技术皇冠上的明珠”?

数据的爆炸性增长 对业务连续性带来了巨大的挑战 传统灾备方式资源利用率底、切换时间长、成本高 对此,基于云计算的多云多活技术正在逐步兴起 巨大的业务价值、超高的技术难度 让“多云多活”被称为“技术皇冠上的明珠” 本期,京东云资深混合多云多活专家将带来 京东内部秒级容灾切换实战分享 以及多行业跨云多活