golang tcp的keepalive

您所在的位置:网站首页 keepalive_probes golang tcp的keepalive

golang tcp的keepalive

2023-10-04 14:43| 来源: 网络整理| 查看: 265

socket tcp 如何维护长连接

操作系统实现:

tcp的keepalive机制开启后,在一定时间内,一般为7200s(参数tcp_keepalive_time)在链路上没有数据传送的情况下,tcp层将发送相应的keepalive探针以确定连接的可用性,探测失败后重试9次(tcp_keepalive_probes),每次间隔时间75s(tcp_keepalive_intvl),所有探测失败后,才认为当前连接已经不可用。

这些参数是机器级别的,可以调整。

keepalive保活机制只在链路空闲的情况下才会起作用。

image.png

应用层实现:

如果客户端已经消失而连接未断开,则会使得服务器上保留一个半开放的连接。保活功能就是试图在服务器端检测到这种半开放的连接。

1)客户端依然正常运行且可达。此时客户端正常响应,服务器将保活定时器复位。

2)客户端主机已经崩溃,并且已经重新启动。服务端会收到一个对其保活探测的响应,这个响应是一个复位(RST),使得服务器终止这个连接。

3)客户端主机已经崩溃,并且关闭或者正在重启。客户端无法响应,服务端将无法收到客户端的探测响应,服务端连续发送多个探测,每个间隔指定时间,若服务器没有收到响应,就认为客户端已经关闭并终止连接。

4)客户端正常,服务端不可达,同第3点

net包的Dial源码

func (d *Dialer) Dial(network, address string) (Conn, error) { return d.DialContext(context.Background(), network, address) } func (d *Dialer) DialContext(ctx context.Context, network, address string) (Conn, error) { ... var c Conn if len(fallbacks) > 0 { c, err = sd.dialParallel(ctx, primaries, fallbacks) } else { c, err = sd.dialSerial(ctx, primaries) } if err != nil { return nil, err } if tc, ok := c.(*TCPConn); ok && d.KeepAlive >= 0 { setKeepAlive(tc.fd, true) // ka := d.KeepAlive if d.KeepAlive == 0 { ka = 15 * time.Second } setKeepAlivePeriod(tc.fd, ka) // testHookSetKeepAlive(ka) } return c, nil func (d *Dialer) DialContext(ctx context.Context, network, address string) (Conn, error) { c, err = sd.dialSerial(ctx, primaries) } func (sd *sysDialer) dialSerial(ctx context.Context, ras addrList) (Conn, error) { c, err := sd.dialSingle(dialCtx, ra) } func (sd *sysDialer) dialSingle(ctx context.Context, ra Addr) (c Conn, err error) { c, err = sd.dialTCP(ctx, la, ra) } func (sd *sysDialer) dialTCP(ctx context.Context, laddr, raddr *TCPAddr) (*TCPConn, error) { return sd.doDialTCP(ctx, laddr, raddr) } func (sd *sysDialer) doDialTCP(ctx context.Context, laddr, raddr *TCPAddr) (*TCPConn, error) { fd, err := internetSocket(ctx, sd.network, laddr, raddr, syscall.SOCK_STREAM, 0, "dial", sd.Dialer.Control) // for i := 0; i < 2 && (laddr == nil || laddr.Port == 0) && (selfConnect(fd, err) || spuriousENOTAVAIL(err)); i++ { if err == nil { fd.Close() } fd, err = internetSocket(ctx, sd.network, laddr, raddr, syscall.SOCK_STREAM, 0, "dial", sd.Dialer.Control) } if err != nil { return nil, err } return newTCPConn(fd), nil }

net包的listen

image.png

由图可以看出,http的监听是对net.Listen()的封装,而net.Listen()是对Tcp和Unix的封装。而http的监听传入的都是tcp。所有http的监听最终都是实现的net.ListenTcp()。

其中net.ListenTcp,net.ListenUdp(),net.ListenIP()最终走的都是internetSocket()。

func Listen(network, address string) (Listener, error) { var lc ListenConfig return lc.Listen(context.Background(), network, address) } func (lc *ListenConfig) Listen(ctx context.Context, network, address string) (Listener, error) { ... switch la := la.(type) { case *TCPAddr: l, err = sl.listenTCP(ctx, la) case *UnixAddr: l, err = sl.listenUnix(ctx, la) } ... } func (sl *sysListener) listenTCP(ctx context.Context, laddr *TCPAddr) (*TCPListener, error) { fd, err := internetSocket(ctx, sl.network, laddr, nil, syscall.SOCK_STREAM, 0, "listen", sl.ListenConfig.Control) // if err != nil { return nil, err } return &TCPListener{fd}, nil }

net/http/server.go

func (srv *Server) ListenAndServe() error { if srv.shuttingDown() { return ErrServerClosed } addr := srv.Addr if addr == "" { addr = ":http" } ln, err := net.Listen("tcp", addr) if err != nil { return err } return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)}) } type tcpKeepAliveListener struct { *net.TCPListener } func (ln tcpKeepAliveListener) Accept() (net.Conn, error) { tc, err := ln.AcceptTCP() if err != nil { return nil, err } tc.SetKeepAlive(true) // tc.SetKeepAlivePeriod(3 * time.Minute) return tc, nil }

net.TCPConn 的 SetKeepAlive 用来启用TCP keepalive, SetKeepAlivePeriod

参考:

net.Dail 源码分析 blog.csdn.net/yyj18085644…

net.Listen 源码 blog.csdn.net/aixinaxc/ar…



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3