admin管理员组

文章数量:1794759

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> {@Overrideprotected 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> {@Overrideprotected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {System.out.println("接收到信息: " + msg);}@Overridepublic 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> {@Overrideprotected 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> {@Overrideprotected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {}
}

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


读空闲
读空闲
读空闲

    也许你会疑问,被触发了之后该怎么办呢?其实,想怎么办就怎办!也许你已经发现了,在事件被触发的方法中有一个比较重要的“ChannelHandlerContext”。这个“ChannelHandlerContext”是连接ChannelHandler和ChannelPipeline的重要上下文,有了它你就可以对channel进行操作了!

    那么将之前的代码进行一下修改,没有读取到数据就断开连接。

public class HeartBeatServerHandler extends SimpleChannelInboundHandler<String> {@Overrideprotected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {System.out.println("接收到信息: " + msg);}@Overridepublic 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();

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

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

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


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

    欢迎微信搜索并关注“小猴子的技术笔记”公众号 私信我 领取丰富的视频学习资料!

本文标签: Netty