长连接KeepAlive

文章详细解释了HTTP1.1中的长连接与TCPKeepAlive机制,包括其目的和工作原理,并通过举例展示了在BIO、NIO和Netty中如何设置这些选项。同时,讨论了Zookeeper和Dubbo的心跳机制,以及它们如何处理连接保持和异常恢复。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

根本上来说:

  • Http 1.1 默认 Connection:keep-alive , 意图在于短时间内连接复用,希望可以短时间内在同一个连接上进行多次请求/响应。需要服务端和客户端都同意才能使用长连接通信!Connection; close 标识需要关闭连接。
  • TCP的KeepAlive机制意图在于协议层面心跳保活,检测连接错误,太长时间没通讯发送1个无意义的字节探活报文,连接失效下需要尽快端口连接释放资源 ( 持续空闲时长默认7200s(2h),最多发送9次探测报文,每个报文的间隔为75秒钟; 即在9次探测都没有接收到对端的回复之后,认为连接已经断开 ,关闭socket回收资源 ) 。 但不能检测应用程序是否可用,所以应用层要自己实现心跳 。
[root@main app]# sysctl -a | grep keepalive
sysctl: reading key "net.ipv6.conf.all.stable_secret"
net.ipv4.tcp_keepalive_intvl = 75
net.ipv4.tcp_keepalive_probes = 9
net.ipv4.tcp_keepalive_time = 7200
sysctl: reading key "net.ipv6.conf.default.stable_secret"
sysctl: reading key "net.ipv6.conf.eth0.stable_secret"
sysctl: reading key "net.ipv6.conf.lo.stable_secret"

而通俗来说:

 客服端在与服务端通讯过程中,能够复用同一个Socket,即可称之为长连接,是否设置了KeepAlive属性无关。

应用程序设置 TcpNoDelay + SoTimeout, 通过独立线程间隔循环ping自己实现心跳保活。

设置方式

BIO

ServerSocket serverSocket = new ServerSocket(8080);
Socket clientSocket = serverSocket.accept();
clientSocket.setKeepAlive(true);
clientSocket.setTcpNoDelay(true);
clientSocket.setSoTimeout(2000); // 与此 Socket 关联的 SocketInputStream#read() 将阻塞至该事件,  如果数据包传输超过超时值,该Socket将引发 java.net.SocketTimeoutException

NIO

serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress(8080)).configureBlocking(false).register(selector, SelectionKey.OP_ACCEPT);
serverSocketChannel.setOption(StandardSocketOptions.SO_KEEPALIVE, true);
serverSocketChannel.setOption(StandardSocketOptions.TCP_NODELAY, true);
serverSocketChannel.socket().setSoTimeout(6000);

Netty

ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
	.option(ChannelOption.TCP_NODELAY, true)
    .option(ChannelOption.SO_KEEPALIVE, true)
	.option(ChannelOption.SO_TIMEOUT, 60000)

 zookeeper的心跳机制

  1. 它对外服务使用的是NettyServerCnxn - ClientCnxnSocketNIO NIO非阻塞通讯 ,原生的实现里没有心跳保活(参见:服务端==>NettyServerCnxnFactory构造函数、客户端==>ClientCnxnSocketNIO#connect ->#createSock()) , 所以在zkClient的扩展里会实现心跳重连。(参见dubbo的ZookeeperRegistry: 给ZkclientZookeeperClient增加连接状态监听,当因异常发起重连成功后#recover将变动的url重新Subscribe、Register)。
  2. 而集群内部则使用BIO的Socket,由Leader#lead() 间隔 [tickTime/2] 循环主动发出ping (ping包扔到发送队列queuedPackets里),follower收到后ack (Follower#processPacket -> #ping);  在双方的socket上都设置了setSoTimeout(tickTime * initLimit) , 在与此 Socket 关联的 InputStream 上调用 #read() 时若无数据将阻塞至此时间, 如果超过该配置值将引发 java.net.SocketTimeoutException。

Leader#lead()

dubbo的心跳机制

在客户端:DubboProtocol#refer -> #getClients 创建与服务端的通信客户端HeaderExchangeClient

 P.S:   url上可以指定client的配置参数: eg. 选择哪种客户端、心跳频率、延时connect、版本、序列化方式...

真实服务地址url上的配置项:connections

  1. 未配置默认0:则#getSharedClient(url):相同的服务端Provider( host+port )下同此配置的所有服务接口都共用一个客户端连接。 默认底层是NettyClient
  2. 非0 :则#initClient(url) 新创建N个client ;该Provider提供的服务接口(service interface)单独有N个连接客户端。当dubbo消费者发送请求路由到该Provider时DubboInvoker#doInvoke将轮询使用。

 HeaderExchangeClient 调度执行HeartbeatTask: 发送心跳后如果超时响应了,将重连服务提供者。 DEFAULT_HEARTBEAT = 60 * 1000 

HeaderExchangeClient
HeartBeatTask#run()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值