您当前的位置: 首页 > 

恐龙弟旺仔

暂无认证

  • 1浏览

    0关注

    282博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

Netty4实现心跳检测及IdleStateHandler源码分析

恐龙弟旺仔 发布时间:2018-11-19 09:15:08 ,浏览量:1

1.什么是心跳检测?

    判断对方是否正常运行,一般采用定时发送简单的通讯包,如果在指定时间内未接收到对方响应,则判定对方已经宕掉。用于检测TCP的异常断开。

    心跳包一般就是客户端发送给服务端的简单消息,如果服务端几分钟内没有收到客户端消息,则视为客户端已经断开,这个时候就主动关闭客户端的通道。

 

2.使用Netty实现服务端心跳检测

    下面我们编写服务端代码,服务端实现以下功能:如果在N长时间内没有接受到客户端连接,则发送一段信息给客户端,并关闭其通道

 

    * 我们创建服务端心跳检测的Handler,命名为HeartBeatHandler,并重写userEventTriggered方法

/**
 * 服务端心跳检测
 */
public class HeartBeatHandler extends ChannelInboundHandlerAdapter {

	@Override
	public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {

		if(evt instanceof IdleStateEvent){
			IdleStateEvent ise = (IdleStateEvent)evt;
			
			// 服务端读空闲
			if(ise.state().equals(IdleState.READER_IDLE)){
				ctx.writeAndFlush("client reader idle, channel will close");
				ctx.channel().close();
				
			// 服务端写空闲
			}else if (ise.state().equals(IdleState.WRITER_IDLE)){
				ctx.write("pong message");
				
			// 服务端读写空闲
			}else if (ise.state().equals(IdleState.ALL_IDLE)){
				ctx.channel().close();
			}else{
				// DO NOTHING
			}
		}
	}
}

    * 服务端代码,如下

public class Server {

	public static void main(String[] args) {
		//服务类
		ServerBootstrap bootstrap = new ServerBootstrap();
		
		//boss和worker
		EventLoopGroup boss = new NioEventLoopGroup();
		EventLoopGroup worker = new NioEventLoopGroup();
		
		try {
			//设置线程池
			bootstrap.group(boss, worker);
			
			//设置socket工厂
			bootstrap.channel(NioServerSocketChannel.class);
			
			//设置管道工厂
			bootstrap.childHandler(new ChannelInitializer() {

               
				@Override
				protected void initChannel(Channel ch) throws Exception {
					ch.pipeline().addLast(new StringEncoder());
                     // 主要在这里,先创建一个IdleStateHandler,10秒内没有读写则判定为空闲
					ch.pipeline().addLast(new IdleStateHandler(10, 10, 10, TimeUnit.SECONDS));
                    // 然后执行心跳检测
					ch.pipeline().addLast(new HeartBeatHandler());
				}
			});
			
			//设置参数,TCP参数
			bootstrap.option(ChannelOption.SO_BACKLOG, 2048);//serverSocketchannel的设置,链接缓冲池的大小
			bootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);//socketchannel的设置,维持链接的活跃,清除死链接
			bootstrap.childOption(ChannelOption.TCP_NODELAY, true);//socketchannel的设置,关闭延迟发送
			
			//绑定端口
			ChannelFuture future = bootstrap.bind(8088).sync();
			
			System.out.println("server start...");
			//等待服务端关闭
			future.channel().closeFuture().sync();
			
		} catch (Exception e) {
			e.printStackTrace();
		} finally{
			//释放资源
			boss.shutdownGracefully();
			worker.shutdownGracefully();
		}
	}
}

    * 启动服务端并测试

    启动服务端之后,我们来开启一个客户端,还是使用telnet的方式

    连接到服务端之后,如果我们十秒钟不发送数据的话,则会被强制关闭连接,如下图所示

 

3.关于IdleStateHandler源码分析

 

    1)IdleStateHandler结构分析

public class IdleStateHandler extends ChannelDuplexHandler {
    
    // 对象初始化的时候初始化好这些数据
    private final long readerIdleTimeNanos;
    private final long writerIdleTimeNanos;
    private final long allIdleTimeNanos;

    // 通过构造函数可知
    public IdleStateHandler(
            long readerIdleTime, long writerIdleTime, long allIdleTime,
            TimeUnit unit) {
        if (unit == null) {
            throw new NullPointerException("unit");
        }

        if (readerIdleTime  0) {
        allIdleTimeout = loop.schedule(
            new AllIdleTimeoutTask(ctx),
            allIdleTimeNanos, TimeUnit.NANOSECONDS);
    }
}

    总结:由上可知,实现心跳检测的关键就在这个schedule上,根据用户设置的空闲时间来确定定时任务的频率。

    那么,定时任务是如何做到检测的呢?我们继续来看

 

    3)ReaderIdleTimeoutTask

    源码如下:

private final class ReaderIdleTimeoutTask implements Runnable {

    private final ChannelHandlerContext ctx;

    ReaderIdleTimeoutTask(ChannelHandlerContext ctx) {
        this.ctx = ctx;
    }

    @Override
    public void run() {
        if (!ctx.channel().isOpen()) {
            return;
        }

        // 1.获取当前时间和最后一次读时间
        long currentTime = System.nanoTime();
        long lastReadTime = IdleStateHandler.this.lastReadTime;
        
        // nextDelay即为比较用户设置的读空闲时间和 当前时间-最后一次时间
        long nextDelay = readerIdleTimeNanos - (currentTime - lastReadTime);
        
        // 2.如果nextDelay            
关注
打赏
1655041699
查看更多评论
0.0398s