• tcp协议在定位中的应用(2)


      上一篇文章说到还是产生 accpet open too many files的错误。

      这个一般是通过修改ulimit就可以了,但是修改这个参数有一个误区,就是生效的时机。虽然你登录终端看ulimit -a看到open files连接数是够大了,但是对于app运行的环境并不一定是这个数目。具体可以通过cat /proc/{app自己的pid}/limits来确认真实的limits。

      对于当时情况是:ulimit 修改后没有对用户进程产生作用。经过不停排除发现进程是由supervisor来启动,先找到supervisor,修改superv里面的fd值,发现还是不能生效。发现supervisord又是由 systemctrl启动,在systemtrl的配置文件里面修改了最大文件数,最后才生效。

      通过修改好limit open fils这一项,后续再也没有出现open too many files的错误了。

           当时在看出现open too many files的时候,发现netstat -apnt 查看,发现有很多close_wait。这个是如何产生的?

           首先我们理解accept: open too many files中的open too many files是句柄数目的限制。那么accept是什么?

           tcp连接中,分为两种连接,半连接和全连接。全连接的也就是 ESTABLISHED 建立的连接,也就是accept操作建立的连接。accpet队列就是全连接队列。这个队列的大小由两个参数的最小值决定,一个是系统级别,另一个是独立应用级别的。

    • 全连接队列的大小:min(backlog, /proc/sys/net/core/somaxconn),意思是取backlog 与 somaxconn 两值的最小值,net.core.somaxconn 定义了系统级别的全连接队列最大长度,而 backlog 只是应用层传入的参数,所以 backlog 值尽量小于net.core.somaxconn;

    • net.core.somaxconn(内核态参数,系统中每一个端口最大的监听队列的长度);

    • net.core.netdev_max_backlog(每个网络接口接收数据包的速率比内核处理这些包的速率快时,允许送到队列的数据包的最大数目);

    • ServerSocket(int port, int backlog) 代码中的backlog参数;

    • 文件句柄;

    • net.ipv4.tcp_abort_on_overflow = 0,此值为 0 表示握手到第三步时全连接队列满时则扔掉 client 发过来的 ACK,此值为 1 则说明握手到第三步时全连接队列满时则返回 reset 给客户端。

      连接的数量有一个地方需要澄清,客户端进入了连接状态,但是服务端不一定进入了连接状态,根据状态转移图:有一个步骤的时间差,就是收到客户端的ack,后服务端才认为自己正式建立了连接。所以连接数,在客户端与服务端之间有一个时间差。

     

      实际中,发现服务端建立连接,但是由于服务端各种原因,自己可以让服务睡眠时间久点。accept客户端的连接请求慢点,导致最终accept队列满了,一般阈值是(128+1)个连接。然后客户端超时,请求关闭还没有进入到全连接队列的连接,SYN_RECV 状态持续一段时间后会消失。服务端对于客户端的关闭请求回复了ack,进入了close_wait, 但是服务端的连接还在被accept阻塞,所以会一直处于close_wait的状态中不释放。
      最终要把close_wait释放的手段,一种程序内部调用close关闭连接(需要改代码),一种是被kill掉。通过tcpdump抓包,发现这种是服务端主动发起RST报文,然后关闭了连接。
           所以线上出现了close_wait问题,那么很有可能就是程序被阻塞,或者是某种情况没有close掉连接
     
    注意,该代码没有给连接主动close掉。
    package main
    
    import (
            "log"
            "net"
            "time"
    )
    
    func main() {
            l, err := net.Listen("tcp", ":8889")
            if err != nil {
                    log.Println("error listen:", err)
                    return
            }
            defer l.Close()
            log.Println("listen ok")
    
            var i int
            for {
                    time.Sleep(time.Second * 100000)
                    log.Printf("%d: accept a new connection
    ", i)
                    if _, err := l.Accept(); err != nil {
                            log.Println("accept error:", err)
                            break
                    }
                    i++
                    log.Printf("%d: accept a new connection
    ", i)
            }
    }

    netstat -antp 查看

    cp        0      0 192.168.0.105:8889      192.168.0.100:40088     SYN_RECV    -                   
    tcp        0      0 192.168.0.105:8889      192.168.0.100:40100     SYN_RECV    -                   
    tcp        0      0 192.168.0.105:8889      192.168.0.100:40102     SYN_RECV    -                   
    tcp        0      0 192.168.0.105:8889      192.168.0.100:40090     SYN_RECV    -                   
    tcp        0      0 192.168.0.105:8889      192.168.0.100:40098     SYN_RECV    -                   
    tcp        0      0 192.168.0.105:8889      192.168.0.100:40092     SYN_RECV    -                   
    tcp        0      0 192.168.0.105:8889      192.168.0.100:40096     SYN_RECV    -                   
    tcp        0      0 192.168.0.105:8889      192.168.0.100:40094     SYN_RECV    -                   
    tcp        0      0 192.168.0.105:8889      192.168.0.100:40086     SYN_RECV    -                   
    tcp6     129      0 :::8889                 :::*                    LISTEN      37482/server        
    tcp6       0      0 192.168.0.105:8889      192.168.0.100:40034     ESTABLISHED -                   
    tcp6       0      0 192.168.0.105:8889      192.168.0.100:40014     ESTABLISHED -                   
    tcp6       0      0 192.168.0.105:8889      192.168.0.100:39844     ESTABLISHED -                   
    tcp6       0      0 192.168.0.105:8889      192.168.0.100:40002     ESTABLISHED -                   
    tcp6       0      0 192.168.0.105:8889      192.168.0.100:40040     ESTABLISHED -                   
    tcp6       0      0 192.168.0.105:8889      192.168.0.100:39942     ESTABLISHED -                   
    tcp6       0      0 192.168.0.105:8889      192.168.0.100:40022     ESTABLISHED -                   
    tcp6       0      0 192.168.0.105:8889      192.168.0.100:39992     ESTABLISHED -         
    ......
     
    [jet@192 ~]$ sudo netstat -lpnt  | grep 8889
    tcp6     129      0 :::8889                 :::*                    LISTEN      37482/server    
     
    客户端:
    [jet@192 ~]$ sudo netstat -antp | grep client | wc -l
    129
     
    服务端:
    jet@192 ~]$ sudo sysctl -a | grep conn
    net.core.somaxconn = 128
     
     
    kill 掉客户端请求后,就发现服务端的状态进入了close_wait,因为kill掉客户端后,客户端会发出关闭连接请求, SYN_RECV    状态请求在半连接队列里面,后面会消失:
    [jet@192 ~]$ sudo netstat -antp | grep client 
    [jet@192 ~]$ 
    服务端
    [jet@192 ~]$ sudo netstat -anpt  | grep 8889 | grep CLOSE_WAIT | wc -l
    129
  • 相关阅读:
    基本STRUTS标签-学习笔记-Logic标签
    Static的使用
    模板template
    iostream与iostream.h的区别
    数据库连接池
    canvas基础
    javascript面试题集
    ES6新特性学习
    原型和原型链
    css为tbody或者li奇数偶数行样式
  • 原文地址:https://www.cnblogs.com/studyNT/p/13975600.html
Copyright © 2020-2023  润新知