Netty学习(七):心跳检测机制

一、什么是心跳检测机制

所谓心跳, 即在 TCP 长连接中, 客户端和服务器之间定期发送的一种特殊的数据包, 通知对方自己还在线, 以确保 TCP 连接的有效性.

心跳机制主要是客户端和服务端长时间连接时,客户端需要定时发送心跳包来保证自己是存活的,否则一个连接长时间没有作用就会浪费服务端的资源。

二、心跳检测机制的适用场景

长连接的应用场景非常的广泛,比如监控系统,IM系统,即时报价系统,推送服务等等。像这些场景都是比较注重实时性,如果每次发送数据都要进行一次DNS解析,建立连接的过程肯定是极其影响体验。

而长连接的维护必然需要一套机制来控制。比如 HTTP/1.0 通过在 header 头中添加 Connection:Keep-Alive参数,如果当前请求需要保活则添加该参数作为标识,否则服务端就不会保持该连接的状态,发送完数据之后就关闭连接。HTTP/1.1以后 Keep-Alive 是默认打开的。

Netty 是基于 TCP 协议开发的,在四层协议 TCP 协议的实现中也提供了 keepalive 报文用来探测对端是否可用。TCP 层将在定时时间到后发送相应的 KeepAlive 探针以确定连接可用性。

所以,心跳检测一般存在于建立长连接 或者 需要保活的场景。

三、netty的心跳检测机制

基础协议对应用来说不是那么尽善尽美,一个 Netty 服务端可能会面临上万个连接,如何去维护这些连接是应用应该去处理的事情。在 Netty 中提供了 IdleStateHandler 类专门用于处理心跳。

IdleStateHandler 的构造函数如下:

CopypublicIdleStateHandler(long readerIdleTime, long writerIdleTime, 
                            long allIdleTime,TimeUnit unit){  
}

说明:

  1. IdleStateHandler 是netty 提供的处理空闲状态的处理器

  1. long readerIdleTime : 表示多长时间没有读, 就会发送一个心跳检测包检测是否连接

  1. long writerIdleTime : 表示多长时间没有写, 就会发送一个心跳检测包检测是否连接

  1. long allIdleTime : 表示多长时间没有读写, 就会发送一个心跳检测包检测是否连接

IdleStateHandler 的文档说明:

triggers an {@link IdleStateEvent} when a {@link Channel} has not performed
read, write, or both operation for a while.

四、Netty心跳检测机制实例

我用即将编写的一个例子来解释什么是心跳检测机制。我将要实现这样的功能:

  1. 当服务器超过3秒没有读时,就提示读空闲

  1. 当服务器超过5秒没有写操作时,就提示写空闲

  1. 当服务器超过7秒没有读或者写操作时,就提示读写空闲

服务端:

public class MyServer {
    public static void main(String[] args) throws Exception{


        //创建两个线程组
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup(); //8个NioEventLoop
        try {

            ServerBootstrap serverBootstrap = new ServerBootstrap();

            serverBootstrap.group(bossGroup, workerGroup);
            serverBootstrap.channel(NioServerSocketChannel.class);
            serverBootstrap.handler(new LoggingHandler(LogLevel.INFO));
            serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {

                @Override
                protected void initChannel(SocketChannel ch) throws Exception {
                    ChannelPipeline pipeline = ch.pipeline();
                    //加入一个netty 提供 IdleStateHandler
                    /*
                     *当 IdleStateEvent 触发后 , 就会传递给管道 的下一个handler去处理
                     *通过调用(触发)下一个handler 的 userEventTiggered , 在该方法中去处理 IdleStateEvent(读空闲,写空闲,读写空闲)
                     */
                    pipeline.addLast(new IdleStateHandler(7000,7000,10, TimeUnit.SECONDS));
                    //加入一个对空闲检测进一步处理的handler(自定义)
                    pipeline.addLast(new MyServerHandler());
                }
            });

            //启动服务器
            ChannelFuture channelFuture = serverBootstrap.bind(7000).sync();
            channelFuture.channel().closeFuture().sync();

        }finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

自定义handler(在该handler中决定如何处理):

public class MyServerHandler extends ChannelInboundHandlerAdapter {

    /**
     *
     * @param ctx 上下文
     * @param evt 事件
     * @throws Exception
     */
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {

        if(evt instanceof IdleStateEvent) {

            //将  evt 向下转型 IdleStateEvent
            IdleStateEvent event = (IdleStateEvent) evt;
            String eventType = null;
            switch (event.state()) {
                case READER_IDLE:
                  eventType = "读空闲";
                  break;
                case WRITER_IDLE:
                    eventType = "写空闲";
                    break;
                case ALL_IDLE:
                    eventType = "读写空闲";
                    break;
            }
            //这里已经可以知道浏览器所处的空闲是何种空闲,可以执行对应的处理逻辑了
            System.out.println(ctx.channel().remoteAddress() + "--超时时间--" + eventType);
            System.out.println("服务器做相应处理..");

            //如果发生空闲,我们关闭通道
           // ctx.channel().close();
        }
    }
}

猜你喜欢

转载自blog.csdn.net/m0_49499183/article/details/129492785