Netty中的心跳檢測(cè)機(jī)制詳解
Netty心跳檢測(cè)機(jī)制
1 心跳檢測(cè)使用場(chǎng)景
長(zhǎng)連接的應(yīng)用場(chǎng)景非常的廣泛,比如監(jiān)控系統(tǒng),IM系統(tǒng),即時(shí)報(bào)價(jià)系統(tǒng),推送服務(wù)等等。像這些場(chǎng)景都是比較注重實(shí)時(shí)性,如果每次發(fā)送數(shù)據(jù)都要進(jìn)行一次DNS解析,建立連接的過(guò)程肯定是極其影響體驗(yàn)。
而長(zhǎng)連接的維護(hù)必然需要一套機(jī)制來(lái)控制。比如 HTTP/1.0 通過(guò)在 header 頭中添加 Connection:Keep-Alive參數(shù),如果當(dāng)前請(qǐng)求需要?;顒t添加該參數(shù)作為標(biāo)識(shí),否則服務(wù)端就不會(huì)保持該連接的狀態(tài),發(fā)送完數(shù)據(jù)之后就關(guān)閉連接。HTTP/1.1以后 Keep-Alive 是默認(rèn)打開的。
Netty 是 基于 TCP 協(xié)議開發(fā)的,在四層協(xié)議 TCP 協(xié)議的實(shí)現(xiàn)中也提供了 keepalive 報(bào)文用來(lái)探測(cè)對(duì)端是否可用。TCP 層將在定時(shí)時(shí)間到后發(fā)送相應(yīng)的 KeepAlive 探針以確定連接可用性。
Netty 中提供了 tcp-keepalive 的設(shè)置:

.childOption(ChannelOption.SO_KEEPALIVE,true) 表示打開 TCP 的 keepAlive 設(shè)置。
2 Netty心跳檢測(cè)機(jī)制
Netty 中提供了 IdleStateHandler 類專門用于處理心跳。構(gòu)造函數(shù)如下:
public IdleStateHandler(long readerIdleTime, long writerIdleTime, long allIdleTime,TimeUnit unit) {
this(false, readerIdleTime, writerIdleTime, allIdleTime, unit);
}參數(shù)說(shuō)明:
- readerIdleTime 隔多久檢查一下讀事件是否發(fā)生,如果 channelRead() 方法超過(guò) readerIdleTime 時(shí)間未被調(diào)用則會(huì)觸發(fā)超時(shí)事件調(diào)用 userEventTrigger() 方法
- writerIdleTime 隔多久檢查一下寫事件是否發(fā)生,如果 write() 方法超過(guò) writerIdleTime 時(shí)間未被調(diào)用則會(huì)觸發(fā)超時(shí)事件調(diào)用 userEventTrigger() 方法;
- allIdleTime 隔多久檢查讀寫事件是否發(fā)生
- unit 時(shí)間單位
可以分別控制讀,寫,讀寫超時(shí)的時(shí)間,如果設(shè)置為0表示不檢測(cè),所以如果全是0,則相當(dāng)于沒(méi)添加這個(gè) IdleStateHandler,連接是個(gè)普通的短連接。
2.1 代碼演示
服務(wù)端
public class TestHeartServer {
public static void main(String[] args) throws InterruptedException {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap bootstrap=new ServerBootstrap();
bootstrap.group(bossGroup,workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG,128)
.childOption(ChannelOption.SO_KEEPALIVE,true)
.handler(new LoggingHandler(LogLevel.INFO))//bossGroup處理handler
.childHandler(new ChannelInitializer<SocketChannel>() {//workergroup處理handler
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
//每隔5s檢查一下是否有讀事件發(fā)生
pipeline.addLast(new IdleStateHandler(5,0,0, TimeUnit.SECONDS));
pipeline.addLast(new TestHeartServerHandler());
}
});
ChannelFuture channelFuture = bootstrap.bind(9999).sync();
channelFuture.channel().closeFuture().sync();
}finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}服務(wù)端handler
public class TestHeartServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("channelActive");
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buf=(ByteBuf) msg;
System.out.println("客戶端消息:"+buf.toString(StandardCharsets.UTF_8));
//向客戶端發(fā)送消息
//ctx.writeAndFlush(Unpooled.copiedBuffer("heart",StandardCharsets.UTF_8));
}
/**
*如果5s沒(méi)有讀請(qǐng)求,則向客戶端發(fā)送心跳
* @param ctx
* @param evt
* @throws Exception
*/
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
IdleStateEvent event = (IdleStateEvent) evt;
switch (event.state()) {
case READER_IDLE: //讀空閑
//如果5s沒(méi)有讀請(qǐng)求,則向客戶端發(fā)送心跳
ctx.writeAndFlush("server send Heartbeat").addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
break;
case WRITER_IDLE://寫空閑
break;
case ALL_IDLE://讀寫空閑
break;
}
}
}客戶端
public class TestHeartClient {
public static void main(String[] args) {
EventLoopGroup eventExecutors=new NioEventLoopGroup();
try {
Bootstrap bootstrap=new Bootstrap();
bootstrap.group(eventExecutors)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
//每隔4s檢查一下是否有寫事件
pipeline.addLast(new IdleStateHandler(0,4,0, TimeUnit.SECONDS));
pipeline.addLast(new TestHeartClientHandler());
}
});
ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 9999).sync();
//向服務(wù)端發(fā)送消息
channelFuture.channel().writeAndFlush(Unpooled.copiedBuffer("Hello server, i'm online", StandardCharsets.UTF_8));
channelFuture.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
eventExecutors.shutdownGracefully();
}
}
}客戶端Handler
public class TestHeartClientHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buf=(ByteBuf) msg;
System.out.println("服務(wù)端發(fā)送的消息:"+buf.toString(StandardCharsets.UTF_8));
}
/**
*
* @param ctx
* @param evt
* @throws Exception
*/
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
IdleStateEvent event = (IdleStateEvent) evt;
String eventType = null;
switch (event.state()) {
//讀空閑
case READER_IDLE:
break;
case WRITER_IDLE://寫空閑
//如果4s沒(méi)有收到寫請(qǐng)求,則向服務(wù)端發(fā)送心跳請(qǐng)求
ctx.writeAndFlush(Unpooled.copiedBuffer("client send Heartbeat",StandardCharsets.UTF_8)).addListener(ChannelFutureListener.CLOSE_ON_FAILURE) ;
break;
case ALL_IDLE://讀寫空閑
break;
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}解釋一下代碼的邏輯:
服務(wù)端添加了:
Copypipeline.addLast(new IdleStateHandler(5, 0, 0, TimeUnit.SECONDS));
每隔5s檢查一下是否有讀事件發(fā)生,如果沒(méi)有就觸發(fā) handler 中的 userEventTriggered(ChannelHandlerContext ctx, Object evt)邏輯。
客戶端添加了:
Copynew IdleStateHandler(0, 4, 0, TimeUnit.SECONDS)
每隔4s檢查一下是否有寫事件,如果沒(méi)有就觸發(fā) handler 中的 userEventTriggered(ChannelHandlerContext ctx, Object evt)邏輯。
到此這篇關(guān)于Netty中的心跳檢測(cè)機(jī)制詳解的文章就介紹到這了,更多相關(guān)Netty心跳檢測(cè)機(jī)制內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
springboot2.x 接入阿里云市場(chǎng)短信發(fā)送的實(shí)現(xiàn)
本文主要介紹了springboot2.x 接入阿里云市場(chǎng)短信發(fā)送的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-11-11
Java實(shí)現(xiàn)實(shí)時(shí)監(jiān)控目錄下文件變化的方法
今天小編就為大家分享一篇關(guān)于Java實(shí)現(xiàn)實(shí)時(shí)監(jiān)控目錄下文件變化的方法,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-03-03
詳解Java List的擴(kuò)容機(jī)制原理及應(yīng)用
在Java中,List是一種非常常用的數(shù)據(jù)結(jié)構(gòu),用于存儲(chǔ)有序的元素集合,本文將分析Java List的擴(kuò)容機(jī)制原理,并通過(guò)示例代碼和測(cè)試代碼來(lái)加強(qiáng)闡述內(nèi)容,具有一定的參考價(jià)值,感興趣的可以了解一下2023-08-08
springmvc后臺(tái)基于@ModelAttribute獲取表單提交的數(shù)據(jù)
這篇文章主要介紹了springmvc后臺(tái)基于@ModelAttribute獲取表單提交的數(shù)據(jù),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-10-10
mybatis使用foreach查詢不出結(jié)果也不報(bào)錯(cuò)的問(wèn)題
這篇文章主要介紹了mybatis使用foreach查詢不出結(jié)果也不報(bào)錯(cuò)的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03
Spring Boot集成Java DSL的實(shí)現(xiàn)代碼
這篇文章主要介紹了Spring Boot集成Java DSL的實(shí)現(xiàn)代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-01-01
mybatis-plus之自動(dòng)映射字段(typeHandler)的注意點(diǎn)及說(shuō)明
這篇文章主要介紹了mybatis-plus之自動(dòng)映射字段(typeHandler)的注意點(diǎn)及說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04

