Netty4服务端心跳机制

Netty4与Netty3.x的心跳机制略有不同,在Netty4中已经去掉了IdleStateAwareChannelHandler这个类,但IdleStateHandler依旧保留,只是心跳超时的触发事件的写法略有不同,Netty底层实现了一套类似信号和槽的事件通信机制。
这里且看实现。

首先是在 SocketChannel.pipeline 中注册 IdleStateHandler 进行心跳时间的定制:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
        // Configure the server.
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 100)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch)
                                throws Exception {
                            ch.pipeline()
                                    .addLast(
                                            new LoggingHandler(LogLevel.INFO),
                                            new IdleStateHandler(30,0,0),// 心跳控制
                                            new ServerHandler());
                        }
                    });

            // Start the server.
            ChannelFuture f = b.bind(port).sync();

            // Wait until the server socket is closed.
            f.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // Shut down all event loops to terminate all threads.
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }

这里要注意IdleStateHandler()要放在ServerHandler()的前面,否则心跳管理无法触发ServerHandler中的userEventTriggered()方法。

此时已经成功在服务器中创建了心跳控制器:
IdleStateHandler(int readerIdleTimeSeconds, int writerIdleTimeSeconds, int allIdleTimeSeconds)

Property Meaning
readerIdleTime an IdleStateEvent whose state is IdleState.READER_IDLE will be triggered when no read was performed for the specified period of time. Specify 0to disable.
writerIdleTime an IdleStateEvent whose state is IdleState.WRITER_IDLE will be triggered when no write was performed for the specified period of time. Specify 0 to disable.
allIdleTime an IdleStateEvent whose state is IdleState.ALL_IDLE will be triggered when neither read nor write was performed for the specified period of time. Specify 0 to disable.


这里readerIdleTime为读超时时间(即服务器一定时间内未接受到客户端消息)

writerIdleTime为写超时时间(即服务器一定时间内向客户端发送消息)

allIdleTime为全体超时时间(即同时没有读写的时间)

对于以上几种Time的使用根据用户自己的需求来决定。这里我们作为验证客户端心跳的机制,只需要设置readerIdleTime这一个参数就够了。

接下来我们需要在ServerHandler中实现其继承自ChannelInboundHandlerAdapter的userEventTriggered()方法来做心跳超时事件的触发实现,代码如下
ServerHandler.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class ServerHandler extends ChannelInboundHandlerAdapter {

...
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg)
            throws Exception {
        ...
    }

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

        /*心跳处理*/
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent event = (IdleStateEvent) evt;
            if (event.state() == IdleState.READER_IDLE) {
                /*读超时*/
                System.out.println("READER_IDLE 读超时");
                ctx.disconnect();
            } else if (event.state() == IdleState.WRITER_IDLE) {
                /*写超时*/  
                System.out.println("WRITER_IDLE 写超时");
            } else if (event.state() == IdleState.ALL_IDLE) {
                /*总超时*/
                System.out.println("ALL_IDLE 总超时");
            }
        }
    }
}

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {

        // 这里加入玩家的掉线处理
        ctx.close();

    }

...

通过对IdleStateEvent的判断即可分别实现不同超时情况的处理,这里我们针对的是READER_IDLE的超时判断,因此在其判断成功后执行ctx.disconnect(),即关闭客户端连接。
此时会触发channelInactive()方法,我们将玩家的断线处理如保存玩家信息等内容写在channelInactive()中即可。
客户端主动断开的连接同理,均会触发其Channel所属的Handler中的channelInactive()方法。

以上即实现了一套简单的服务器心跳机制,只要保证客户端在每单位时间(这里设置的是30秒)发送一次消息给服务器即可保证其不被踢下线。
本篇到此,欢迎大家提出更优化的心跳机制实现方案。
谢谢关注。

BeiTown
2014-05-17

本文链接:Netty4服务端心跳机制

转载声明:BeiTown原创,转载请注明来源:BeiTown's Coder 编码之源,谢谢


Tags: , , ,