Netty-心跳检测的作用和实现方法

    欢迎微信搜索并关注“小猴子的技术笔记”公众号 私信我 领取丰富的视频学习资料!Netty-心跳检测的作用和实现方法
    关于心跳我们在长链接的开发中一般都是会遇到的,因为是长链接所以需要定时发送心跳保持连接的活跃。当服务端检测不到客户端的心跳之后就会释放资源,这个操作是一个很重要的操作。

    如果你处理过原生socket的心跳检测机制,你会发现那是一个比较麻烦的处理。你需要起一个线程或者定时任务来不停的检测连接是否有心跳上送,如果没有心跳你就需要释放资源,关闭socket或者尝试重连机制。

    Netty为我们提供了一个“IdleStateHandler”闲置状态处理器。如果在一定的时间内没有读、写或者两者都没有的操作将会被触发这个处理器的事件。

    关于“IdleStateHandler”我们需要设置一些参数,一般的设置为:


public IdleStateHandler(
    long readerIdleTime, 
    long writerIdleTime,
    long allIdleTime,
    TimeUnit unit) {
    this(false, readerIdleTime, writerIdleTime, allIdleTime, unit);
}

    “readerIdleTime”:读取数据的超时时间,也就是说连接建立之后在这个readerIdleTime时间内没有读取到数据就会被触发。

    “writerIdleTime”:写超时事件,也就是说在连接建立之后在writerIdleTime时间内没有往外面写任何数据。

    “allIdleTime”:读写超时事件,也就是说在连接建立之后在allIdleTime时间内既没有发生读的事件,也没有发生写的事件。

    “unit”:前面参数的时间单位,是以分钟啊,还是秒啊,或者是小时为单位进行一次检测数据。

    如果参数设置成0的话,那么是“无效”的,也就是说netty会忽略到这个配置。接下来看看心跳检测的示例。

public class HeartBeatServer {
    public static void main(String[] args) {
        EventLoopGroup boss = new NioEventLoopGroup();
        EventLoopGroup worker = new NioEventLoopGroup();
        try {
            ServerBootstrap server = new ServerBootstrap();
            server.group(boss, worker).channel(NioServerSocketChannel.class).childHandler(new HeartBeatServerInitializer());
            ChannelFuture channelFuture = server.bind(9999).sync();
            channelFuture.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            boss.shutdownGracefully();
            worker.shutdownGracefully();
        }
    }
}
public class HeartBeatServerInitializer extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        pipeline.addLast(new LineBasedFrameDecoder(1024));
        pipeline.addLast(new StringDecoder());
        pipeline.addLast(new StringEncoder());
        pipeline.addLast(new IdleStateHandler(5, 0, 0, TimeUnit.SECONDS));
        pipeline.addLast(new HeartBeatServerHandler());
    }
}

public class HeartBeatServerHandler extends SimpleChannelInboundHandler<String> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
        System.out.println("接收到信息: " + msg);
    }
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        IdleStateEvent event = (IdleStateEvent) evt;
        switch (event.state()) {
            case READER_IDLE:
                System.out.println("读空闲");
                break;
            case WRITER_IDLE:
                System.out.println("写空闲");
                break;
            case ALL_IDLE:
                System.out.println("读写空闲");
                break;
            default:
                throw new IllegalStateException("非法状态!");
        }
    }
}

    假设我们写一个客户端不进行数据的发送,看看会触发什么效果:

public class HeartBeatClient {
    public static void main(String[] args) {
        EventLoopGroup worker = new NioEventLoopGroup();
        try {
            Bootstrap client = new Bootstrap();
            client.group(worker).channel(NioSocketChannel.class).handler(new HeartBeatClientInitializer());
            Channel channel = client.connect("localhost", 9999).sync().channel();
            channel.closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            worker.shutdownGracefully();
        }
    }
}
public class HeartBeatClientInitializer extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        pipeline.addLast(new LineBasedFrameDecoder(1024));
        pipeline.addLast(new StringEncoder());
        pipeline.addLast(new StringDecoder());
        pipeline.addLast(new HeartBeatClientHandler());
    }
}
public class HeartBeatClientHandler extends SimpleChannelInboundHandler<String> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {

    }
}

    启动服务端和客户端,等过一段时间你会发现:


读空闲
读空闲
读空闲

    也许你会疑问,被触发了之后该怎么办呢?其实,想怎么办就怎办!也许你已经发现了,在事件被触发的方法中有一个比较重要的“ChannelHandlerContext”。这个“ChannelHandlerContext”是连接ChannelHandler和ChannelPipeline的重要上下文,有了它你就可以对channel进行操作了!
Netty-心跳检测的作用和实现方法
    那么将之前的代码进行一下修改,没有读取到数据就断开连接。

public class HeartBeatServerHandler extends SimpleChannelInboundHandler<String> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
        System.out.println("接收到信息: " + msg);
    }
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        IdleStateEvent event = (IdleStateEvent) evt;
        switch (event.state()) {
            case READER_IDLE:
                System.out.println("读空闲");
                ctx.channel().close();
                break;
            case WRITER_IDLE:
                System.out.println("写空闲");
                break;
            case ALL_IDLE:
                System.out.println("读写空闲");
                break;
            default:
                throw new IllegalStateException("非法状态!");
        }
    }
}

    你会发现就添加了一行代码。

ctx.channel().close();

    再来试着运行一下服务端和客户端:都运行的时候可以到两个进程在运行

Netty-心跳检测的作用和实现方法

    等过了5秒服务端打印出一句话之后就不会再继续打印了。

Netty-心跳检测的作用和实现方法

    再看看进程就剩一个客户端了。

Netty-心跳检测的作用和实现方法
    其实不仅如此,你完全可以结合业务对心跳检测信息做出处理。因为你已经拿到了它对应的“ChannelHandlerContext”,你可以利用它写数据,广播数据,关闭连接等操作。客户端也可以根据服务端上送的心跳进行重连等操作!

    欢迎微信搜索并关注“小猴子的技术笔记”公众号 私信我 领取丰富的视频学习资料!
Netty-心跳检测的作用和实现方法

上一篇:pipeline_流水线技术


下一篇:Go语言标准库之template