TCP Keepalive
tcp连贯被形象为一个socket,socket上增加了SO_KEEPALIVE后,该socket便能够启用keepalive。
keepalive的连贯为长连贯,这样client向server交互时不必每次都新建连贯,用长连贯进行继续的数据读取和写入即可。
keepalive的连贯须要定期进行探测,当client不再沉闷时,server端及时的开释该连贯。
tcp keepalive的参数:
tcp_keepalive_time: 单位s,默认7200
- client与server多久没有数据交互,就认为connection idle,而后开始发动探测。
tcp_keepalive_intvl: 单位s,默认75
- 一次探测结束后,期待多久进行下一次探测。
tcp_keepalive_probes:单位次数,默认9
- 最大探测次数,某个连贯通过N次探测后依然不沉闷将被开释。
默认状况下:
- 2个小时(7200s)(tcp_keepalive_time)没有数据交互,就认为connection idle;
- 而后发动keep-alive音讯,探测client是否存活;
- 每隔tcp_keepalive_intvl(75s)发动一次探测,探测tcp_keepalive_probes(9)次后,将彻底kill连贯;
总结来说,1个tcp连贯,要等:7200+75*9=2hour11min后,才被kill掉;
个别生产环境都会配置下面的3个参数,目录/proc/sys/net/ipv4/下:
//tcp_keepalive_time 参数/proc/sys/net/ipv4 # cat tcp_keepalive_time90//tcp_keeplive_intv 参数/proc/sys/net/ipv4# cat tcp_keepalive_intvl15//tcp_keepalive_probes参数/proc/sys/net/ipv4# cat tcp_keepalive_probes2
当程序中socket未配置keep-alive参数时,就应用零碎上配置的参数。
Keepalive: TCP VS HTTP
Http的keepalive用于连贯复用,在同一个连贯上request-response。
Tcp的keepalive用于保活、心跳。
go-rpc的TCP Keepalive
go-rpc是golang自带的rpc框架,server端的代码:
func StartRpc() { tcpAddr, err := net.ResolveTCPAddr("tcp", addr) if err != nil { log.Fatalln("addr err:", err) } listener, err := net.ListenTCP("tcp", tcpAddr) if err != nil { log.Fatalln("listen err:", err) } server := rpc.NewServer() server.Register(new(Transfer)) for { conn, err := listener.AcceptTCP() if err != nil { log.Println("accept err:", err) continue } log.Println("accept tcp from:", conn.RemoteAddr()) go server.ServeCodec(jsonrpc.NewServerCodec(conn)) }}
服务端接管1个connection,而后启动1个goroutine解决该连贯上的request:
conn, err := listener.AcceptTCP()go server.ServeCodec(jsonrpc.NewServerCodec(conn))
接管TCP连贯时,先配置TCP为keepalive长连贯,而后再配置keepalive参数:
func (ln *TCPListener) accept() (*TCPConn, error) { fd, err := ln.fd.accept() if err != nil { return nil, err } tc := newTCPConn(fd) if ln.lc.KeepAlive >= 0 { setKeepAlive(fd, true) //启用tcp keepalive ka := ln.lc.KeepAlive if ln.lc.KeepAlive == 0 { ka = defaultTCPKeepAlive //默认keepalive工夫=15s } setKeepAlivePeriod(fd, ka) } return tc, nil}
启用TCP keepalive:
//配置Keepalive标记func setKeepAlive(fd *netFD, keepalive bool) error { err := fd.pfd.SetsockoptInt(syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, boolint(keepalive)) runtime.KeepAlive(fd) return wrapSyscallError("setsockopt", err)}
配置TCP keepalive的工夫参数:
- syscall.TCP_KEEPIDLE: tcp_keepalive_time参数,配置为15s;
- syscall.TCP_KEEPINTVL: tcp_keepalive_intvl参数,配置为15s;
- tcp_keepalive_probes应用系统配置:2;
总结下来,server在连贯15s没有数据后,发动探测,距离15s发动一次探测,探测2次后不再沉闷就kill连贯,故一个闲暇连贯要等:15+15*2=45s后被kill。
func setKeepAlivePeriod(fd *netFD, d time.Duration) error { // The kernel expects seconds so round to next highest second. secs := int(roundDurationUp(d, time.Second)) if err := fd.pfd.SetsockoptInt(syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, secs); err != nil { return wrapSyscallError("setsockopt", err) } err := fd.pfd.SetsockoptInt(syscall.IPPROTO_TCP, syscall.TCP_KEEPIDLE, secs) runtime.KeepAlive(fd) return wrapSyscallError("setsockopt", err)}
syscall中的调用参数:
//其中:TCP_KEEPIDLE --> /proc/sys/net/ipv4/tcp_keepalive_timeTCP_KEEPINTVL --> /proc/sys/net/ipv4/tcp_keepalive_intvlTCP_KEEPCNT --> /proc/sys/net/ipv4/tcp_keepalive_probes
参考
1.https://zhuanlan.zhihu.com/p/...
2.https://tldp.org/HOWTO/TCP-Ke...