linux上TCP connection timeout问题解决办法

 linux上TCP connection timeout问题解决办法

最近在产线上经常出现connection timeout的问题,先看看Java 中关于connection timeout 的异常如何产生

JAVA中的timeout

java.net.SocketTimeoutException: connect timed out
客户端异常:connect timed out
  at java.net.PlainSocketImpl.socketConnect(Native Method)
  at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:345)
  at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
  at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
  at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
  at java.net.Socket.connect(Socket.java:589)

我们能经常看到的connect timed out异常产生,看一下java 是如何生成这个异常

plainsocketimpl.c 中

while (1) {
        jlong newTime;
#ifndef USE_SELECT
        {
          struct pollfd pfd;
          pfd.fd = fd;
          pfd.events = POLLOUT; 

          errno = 0;
          connect_rv = NET_Poll(&pfd, 1, timeout);
        }
#else
        {
          fd_set wr, ex;
          struct timeval t; 

          t.tv_sec = timeout / 1000;
          t.tv_usec = (timeout % 1000) * 1000; 

          FD_ZERO(&wr);
          FD_SET(fd, &wr);
          FD_ZERO(&ex);
          FD_SET(fd, &ex); 

          errno = 0;
          connect_rv = NET_Select(fd+1, 0, &wr, &ex, &t);
        }
#endif 

        if (connect_rv >= 0) {
          break;
        }
        if (errno != EINTR) {
          break;
        } 

        /*
         * The poll was interrupted so adjust timeout and
         * restart
         */
        newTime = JVM_CurrentTimeMillis(env, 0);
        timeout -= (newTime - prevTime);
        if (timeout <= 0) {
          connect_rv = 0;
          break;
        }
        prevTime = newTime; 

      } /* while */ 

      if (connect_rv == 0) {
        JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException",
              "connect timed out"); 

        /*
         * Timeout out but connection may still be established.
         * At the high level it should be closed immediately but
         * just in case we make the socket blocking again and
         * shutdown input & output.
         */
        SET_BLOCKING(fd);
        JVM_SocketShutdown(fd, 2);
        return;
      }

这里可以看到在做connect的时候,是调用 NET_Poll 或者 NET_Select, 在linux 上就是使用 poll/select

当发生timeout的时候connect_rv=0  ,这里有个注意点虽然在poll/select 是传入timeout的时间,但是这是会被打断的,connect_rv返回的值为-1 ,所以jvm里面重新计算了timeout , 确保timeout 的时间片已经运行完了,才推出循环。

newTime = JVM_CurrentTimeMillis(env, 0);
        timeout -= (newTime - prevTime);
        if (timeout <= 0) {
          connect_rv = 0;
          break;
        }

同时设置connect_rv 为0, 也是下面只有当connect_rv为0的时候才抛出connect timeout

什么是connect timeout ?

也就是client 发出 syn 包,server端在你指定的时间内没有回复ack,poll/select 返回0

server 端为什么没有回复ack, 因为syn包的回复是内核层的,要么网络层丢包,要么就是内核层back_log的queue满了,关于backlog在本片中就不详细描述了。

当时查看产线上的连接最高能到1000多,同时查看了backlog 的queue的大小

cat /proc/sys/net/ipv4/tcp_max_syn_backlog 

有8192 在产线上没有这么多的客户端的连接,不可能backlog queue会满,虽然syn_backlog 的设置是8192 但并不代表服务器启动的时候设置成了8192,所以必须查这个端口所设置的backlog大小

ss -lt 

看到Send-Q在8080端口是128 ,原来在服务器端启动listen 的时候设置了128的backlog

查看tomcat 的配置,默认bio的设置

<Connector executor="tomcatThreadPool"
      port="8080"
        protocol="HTTP/1.1"
          acceptCount="5000"
          connectionTimeout="25000"
          maxHttpHeaderSize="8192"
          useBodyEncodingForURI="true"
          enableLookups="false"
          redirectPort="8443"
          URIEncoding="UTF-8"
          maxThreads="500"
          maxKeepAliveRequests="1000"
          keepAliveTimeout="30000"
        />

产线上已经设置了acceptCount, 默认是100 但是这里设置了是5000 ,这与通过ss看到的send-q的结果严重不符合
通过内核代码分析,发现原来内核参数不仅仅是通过tcp_max_syn_backlog控制,同时也受somaxconn控制
查看

cat /proc/sys/net/core/somaxconn

发现值是128, OK 原因找到了,修改/etc/sysctl.conf 添加

net.core.somaxconn = 8192 

sysctl -f /etc/sysctl.conf 重新加载一下,这样就能改变全局了

问题:是1000多个连接,500个工作线程,因为backlog的大小是受socket.accept控制的,我们通常境况下会单独起一个线程去serversocket.accept(),而当前server的load并不高,不因该会出现back_log queue出现满的情况,更何况只有1000多个连接,代码就是真相,查看tomcat的源码。

原来accptor 线程在accept 之前,会去countUpOrWaitConnection 发现接受到的的socket数目大于设置的work线程数目的时候,会停止accept.

<strong>countUpOrAwaitConnection</strong>(); 

         Socket socket = null;
         try {
           // Accept the next incoming connection from the server
           // socket
           socket = serverSocketFactory.acceptSocket(serverSocket);
         } catch (IOException ioe) {
           countDownConnection();
           // Introduce delay if necessary
           errorDelay = handleExceptionWithDelay(errorDelay);
           // re-throw
           throw ioe;
         }

也就是说当并发超过628个连接以上,就有可能出现backlog queue满的情况,而出现connect timeout的情况,一切皆清楚了。

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

(0)

相关推荐

  • Linux 下的五种 IO 模型详细介绍

    概念说明 用户空间与内核空间 现在操作系统都是采用虚拟存储器,那么对32位操作系统而言,它的寻址空间(虚拟存储空间)为4G(2的32次方).操作系统的核心是内核,独立于普通的应用程序,可以访问受保护的内存空间,也有访问底层硬件设备的所有权限.为了保证用户进程不能直接操作内核(kernel),保证内核的安全,操作系统将虚拟空间划分为两部分,一部分为内核空间,一部分为用户空间.针对linux操作系统而言,将最高的1G字节(从虚拟地址0xC0000000到0xFFFFFFFF),供内核使用,称为内核空

  • Linux的Socket IO模型趣解

    前言 之前有看到用很幽默的方式讲解Windows的socket IO模型,借用这个故事,讲解下linux的socket IO模型: 老陈有一个在外地工作的女儿,不能经常回来,老陈和她通过信件联系. 他们的信会被邮递员投递到他们小区门口的收发室里.这和Socket模型非常类似. 下面就以老陈接收信件为例讲解linux的 Socket I/O模型. 一.同步阻塞模型 老陈的女儿第一次去外地工作,送走她之后,老陈非常的挂心她安全到达没有: 于是老陈什么也不干,一直在小区门口收发室里等着她女儿的报平安的

  • Linux IO的水平触发和边缘触发的区别

    Linux IO的水平触发和边缘触发的区别 在linux的IO多路复用中有水平触发,边缘触发两种模式,这两种模式的区别如下: 水平触发:如果文件描述符已经就绪可以非阻塞的执行IO操作了,此时会触发通知.允许在任意时刻重复检测IO的状态.select,poll就属于水平触发. 边缘触发:如果文件描述符自上次状态改变后有新的IO活动到来,此时会触发通知.在收到一个IO事件通知后要尽可能多的执行IO操作,因为如果在一次通知中没有执行完IO那么就需要等到下一次新的IO活动到来才能获取到就绪的描述符.信号

  • linux上TCP connection timeout问题解决办法

     linux上TCP connection timeout问题解决办法 最近在产线上经常出现connection timeout的问题,先看看Java 中关于connection timeout 的异常如何产生 JAVA中的timeout java.net.SocketTimeoutException: connect timed out 客户端异常:connect timed out at java.net.PlainSocketImpl.socketConnect(Native Method

  • Jedis出现connection timeout问题解决方法(JedisPool连接池使用实例)

    今天发现Jedis 默认的连接方式 jedis=new Jedis("localhost",6379),老是发生connection timeout. 后来发现jedis类包还有一种可以设置最大连接时间的方法. 1->获取Jedis实例需要从JedisPool中获取:2->用完Jedis实例需要还给JedisPool:3->如果Jedis在使用过程中出错,则也需要还给JedisPool:代码如下 复制代码 代码如下: JedisPoolConfig config =

  • java.net.ConnectException: Connection refused问题解决办法

    Socket异常 客户端异常 java.net.ConnectException: Connection refused: connect. 该异常发生在客户端进行new Socket(ip, port)操作时,该异常发生的原因是或者具有ip地址的机器不能找到(也就是说从当前机器不存在到指定ip路由),或者是该ip存在,但找不到指定的端口进行监听.出现该问题,首先检查客户端的ip和port是否写错了,如果正确则从客户端ping一下服务器看是否能ping通,如果能ping通(服务服务器端把ping

  • Linux下IP设置脚本的实例及遇到问题解决办法

    Linux下IP设置脚本的实例及遇到问题解决办法 背景 一个Java web项目有一个功能是IP修改,Linux的IP修改脚本如下: #!/bin/bash #useing parameter ip:netmask:gateway:dns1 #system version:centos6,7/redhat6,7 #read parameter ipaddr=`echo $1|cut -d ":" -f 1` netmask=`echo $1|cut -d ":" -

  • Linux系统安装NoSQL(MongoDB和Redis)步骤及问题解决办法(总结篇)

    如下是我工作中的记录,介绍的是linux系统下NoSQL:MongoDB和Redis的安装过程和遇到的问题以及解决办法: 需要的朋友可以按照如下步骤进行安装,可以快速安装MongoDB和Redis,希望可以帮助大家:)! 一.MongoDB 1.MongoDB安装 (1)将安装包mongodb-linux-i686-3.0.2.tgz拷贝到要安装的服务器中 这里我用的rz命令,如果不支持需要安装yum -y install lrzsz (2)解压安装程序 tar xzvf mongodb-lin

  • Jenkins+tomcat自动发布的热部署/重启及遇到的问题解决办法(推荐)

    一.背景 公司的项目一直手动maven打包.上传服务器.关闭/开启tomcat,整个流程下来耗时耗力,虽然可以将所有流程通过shell脚本一次性解决,但如果可以通过idea的Jenkins插件一键自动部署,那更省时省力. 下面是一个简单的发布tomcat的shell脚本,执行下面脚本的前提是要在服务器中安装了git.maven # 先关闭tomcat进程 kill -9 `ps aux|grep tomcat|grep -v 'grep'| awk 'NR==1{print $2}'` # 切换

  • Python pip安装lxml出错的问题解决办法

    Python  pip安装lxml出错的问题解决办法 1.  在使用pip安装lxml过程中出现了一下错误: >>> pip install lxml C:\Users\Chen>pip install lxml Collecting lxml Using cached lxml-3.5.0.tar.gz Installing collected packages: lxml Running setup.py install for lxml ... error Complete

  • Kotlin基本类型自动装箱出现问题解决办法

    Kotlin基本类型自动装箱出现问题解决办法 问题 在Kotlin官方文档介绍基本类型时,给我们说明了在有些情况下会对基本类型自动进行装箱操作. 但是具体是如何进行装箱,以及何时进行装箱缺没有提供详细介绍.只是提供了一个例子,如下: val a: Int = 10000 print(a === a) // Prints 'true' val boxedA: Int? = a val anotherBoxedA: Int? = a print(boxedA === anotherBoxedA) /

  • 在Linux上安装Python的Flask框架和创建第一个app实例的教程

    无论你在linux上娱乐还是工作,这对你而言都是一个使用python来编程的很好的机会.回到大学我希望他们教我的是Python而不是Java,这学起来很有趣且在实际的应用如yum包管理器中很有用. 本篇教程中我会带你使用python和一个称为flask的微型框架来构建一个简单的应用,来显示诸如每个进程的内存使用,CPU百分比之类有用的信息. 前置需求 Python基础.列表.类.函数.模块.HTML/CSS (基础). 学习这篇教程你不必是一个python高级开发者,但是首先我建议你阅读http

  • 微信小程序 wx.login解密出现乱码的问题解决办法

    微信小程序 wx.login解密出现乱码的问题解决办法 最近在给公司开发微信小程序,需要用到微信登录,根据文档要求需要把获取的用户信息按照AES进行解密. 我使用的是官方提供的PHP demo,拷贝到程序中,测试发现,解密之后的数据前面有一串乱码. 类似于这样子的,前面一段是乱码. 经过仔细的检查,发现官方的提供的demo中的帐号和机密之后的信息是可以解密的,这就说明解密代码是没有问题的. 后来查询微信开发者社区,找到好多解密失败.其中一个回答说是因为多次调用wx.login之后导致的问题. 终

随机推荐