ORACLE数据库长连接客户端持久的CLOSE_WAIT

 

前言

根据以往的项目构造,业务层数据库基本使用长连接形式进行批量操作。大部分周期有执行的链接基本正常。再长期的内测中也没有发生CLOSE_WAIT的现象。

上线后采用的数据库使用了新的版本,发现产生CLOSE_WAIT。根据开发经验和网上搜索,发现网上也有相关的开发人员询问ORACLE。但是没有直观答案,全是检查网络的。

也让网络侧的进行了相关查阅配置。根据协议开发经验,我断定是会话层引入了链路保活机制,工作年限十年以内,没有和最初的项目建设人员共事过。不知道以往有没有这个现象。

 验证环境

1. linux服务器

2. oracle 数据-该为DBA设定,我查阅了一边sqlnet.ora 没有什么特殊的配置,应当是默认值

3. PRO*C程序

程序构造

多线程,链路长连接。其中有一个专用线程只有产生数据才会入库。

伪代码

```

void* threadToDBoperator()

{

    int ret=0;

    while(1){

        if(hasdata)

        {

            ret=DataTooperator(DB);

            if(ret==ok)

            {

 

            }else

            {

                release(DB);

                connect(DB);

            }

        }else{sleep(1);}

    }

}

```

可以发现,一旦长期没有数据,那么程序是不会执行数据库操作,因此包括oracle自身的数据库基础链路保活协议都不会执行的。

 

虽然TCP层也有链路保活机制,但是TCP的机制在缓冲区满的情况是无法及时到达的,因此应用层的感知需要应用层有探测功能。

 

通过长期无数据的程序中,根据对各个线程增加线程周期执行时间定点日志,确定oralce的保活机制在确认长期无交互的情况回在服务器端主动掐断链路。因此客户端会出现CLOSE_WAIT。

 

定位方式

线程在执行数据库时进行时间日志打印,用该时间进行定位。我本来想用session的客户端端口定位的,但是找了半天,没有找到客户端提取客户端端口的办法,只能加调测信息,利用数据库的最高权限进行定位,并提取客户端端口

```sql

-- 根据最近执行接收的SQL时间断定程序线程的执行时间

-- 先查到程序清单

select TO_CHAR( PREV_EXEC_START,'yyyymmddhh24miss'),port,program from v$session;

-- 例如程序是wechat@hhapp

select TO_CHAR( PREV_EXEC_START,'yyyymmddhh24miss'),port,program from v$session where program like 'wechat@hhapp %' ;

 

date -d @1678188352  '+%Y-%m-%d %T %z'

```

已经通过客户端netstat 检测到接收缓冲区有数据残留在CLOSE_WAIT状态下。

也通过抓包看到TNS包结构。

41b2da7c1d594a3b9cdb89fa9d0c684e.png

 

通过sql端口过滤出来的,猜测以下为服务端的链路探测包

1ba6075c06014833adb7f64be2aa5148.png

 

程序方面如何应对解决

根据这个现象,目前我认为有两种解决方案,

1.直接长连接,变成短连接接。缺点:一旦需要频繁操作,性能耗在链路重连这边。

2.操作数据前进行保活探测,先执行一下无关的命令,命令失败再进行重连。

```

 SELECT TO_CHAR(SYSDATE,'yyyymmddhh24miss') into :CHECK_TIME  FROM DUAL;

 

```

 

附录


 

数据库的一些操作参数应当是和项目相关性极强的。程序的架构设计也是和项目特征相关性极强。

数据库TCP层阻塞操作。和数据库内部处理无关的。大部分数据库在tcp层设置的都是阻塞读写操作。

与之比较的mysql是在8.0后的版本后面才引入noblock操作。这种必须和项目的重要性严重捆绑。

与平时设计的流量太多,选择丢弃数据还是长期阻塞抉择是类似的。


 

```

https://docs.oracle.com/cd/E11882_01/network.112/e10835/sqlnet.htm#NETRF227

 

sqlnet.ora

 

设置连入数据库后必须在多长时间内完成认证(如:输入用户名/密码),超过此时间没有完成的话,数据库会断开此连接,并将客户端的IP地址和ORA-12170: TNS:Connect timeout occurred错误信息记录到sqlnet.log,而且客户端会收到ORA-12547: TNS:lost contact或ORA-12637: Packet receive failed错误信息。这个设置主要是为了防止denial-of-service攻击

在10.2.0.1.0版本中sqlnet.inbound_connect_timeout参数默认为60秒,即如果连接时间超过60秒则提示超时。

而在其他10G版本中这两个参数默认为0,即无限制。

SQLNET.INBOUND_CONNECT_TIMEOUT

CONNECT_TIME

Specify the total elapsed time limit for a session, expressed in minutes.

IDLE_TIME

Specify the permitted periods of continuous inactive time during a session, expressed in minutes. Long-running queries and other operations are not subject to this limit.

 

检测超时样例 分钟级别 提示服务端 探测终止连接或客户端终端异常需要断开

SQLNET.EXPIRE_TIME=10

SQLNET.RECV_TIMEOUT=3

SQLNET.SEND_TIMEOUT=3

 

```




 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值