TCP Keepalive与go-rpc的tcp连接
source link: https://segmentfault.com/a/1190000040885321
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
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_time 90 //tcp_keeplive_intv 参数 /proc/sys/net/ipv4# cat tcp_keepalive_intvl 15 //tcp_keepalive_probes参数 /proc/sys/net/ipv4# cat tcp_keepalive_probes 2
当程序中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_time TCP_KEEPINTVL --> /proc/sys/net/ipv4/tcp_keepalive_intvl TCP_KEEPCNT --> /proc/sys/net/ipv4/tcp_keepalive_probes
1.https://zhuanlan.zhihu.com/p/...
2.https://tldp.org/HOWTO/TCP-Ke...
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK