当前位置 : 主页 > 编程语言 > c语言 >

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

来源:互联网 收集:自由互联 发布时间:2023-09-03
前言 根据以往的项目构造,业务层数据库基本使用长连接形式进行批量操作。大部分周期有执行的链接基本正常。再长期的内测中也没有发生CLOSE_WAIT的现象。上线后采用的数据库使用

前言

根据以往的项目构造,业务层数据库基本使用长连接形式进行批量操作。大部分周期有执行的链接基本正常。再长期的内测中也没有发生CLOSE_WAIT的现象。 上线后采用的数据库使用了新的版本,发现产生CLOSE_WAIY。根据开发经验和网上搜索,发现网上也有相关的开发人员询问ORACLE。但是没有直观答案,全是检查网络的。 也让网络侧的进行了相关查阅配置。根据协议开发经验,我断定是会话层引入了链路保活机制,工作年限十年以内,没有和最初的项目建设人员共事过。不知道以往有没有这个现象。

验证环境

  1. linux服务器
  2. oracle 数据-该位DBA设定,我查阅了一边sql.ora 没有什么特殊的配置,应当是默认值
  3. PRO*C程序
  4. tcpdump 抓包软件
  5. wireshark分析

程序构造

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

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时间断定程序线程的执行时间
-- 先查到程序清单
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包结构。 TNSNoRsp 2023-05-14 130123.png 通过sql端口过滤出来的,猜测以下为服务端的链路探测包 TNSRESPONSE-2023-05-14-130021.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

上一篇:单调队列算法模板及应用
下一篇:没有了
网友评论