netty入门(十六)netty心跳监测机制

命运对每个人都是一样的,不一样的是各自的努力和付出不同,付出的越多,努力的越多,得到的回报也越多,在你累的时候请看一下身边比你成功却还比你更努力的人,这样,你就会更有动力。

导读:本篇文章讲解 netty入门(十六)netty心跳监测机制,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

1.引入

在 TCP 保持长连接的过程中,可能会出现断网等网络异常出现,异常发生的时候, client 与 server 之间如果没有交互的话,它们是无法发现对方已经掉线。

2.工作原理

在 client 与 server 之间,一定时间内没有数据交互时, 即处于 idle 状态时, 客户端或服务器就会发送一个特殊的数据包给对方, 当接收方收到这个数据报文后, 也立即发送一个特殊的数据报文, 回应发送方, 此即一个 PING-PONG 交互。所以, 当某一端收到心跳消息后, 就知道了对方仍然在线, 这就确保 TCP 连接的有效性。

TCP 实际上自带的就有长连接选项,本身也有心跳包机制,也就是 TCP 的选项:SO_KEEPALIVE。但是,TCP 协议层面的长连接灵活性不够。所以,一般情况下我们都是在应用层协议上实现自定义心跳机制的,也就是在 Netty 层面通过编码实现。通过 Netty 实现心跳机制的话,核心类是 IdleStateHandler 。

3.实现

在 Netty中, 实现心跳机制的关键是 IdleStateHandler

public IdleStateHandler(
            int readerIdleTimeSeconds,
            int writerIdleTimeSeconds,
            int allIdleTimeSeconds) {

        this(readerIdleTimeSeconds, writerIdleTimeSeconds, allIdleTimeSeconds,
             TimeUnit.SECONDS);
    }

参数的含义:

  • readerIdleTimeSeconds: 读超时. 即当在指定的时间间隔内没有从 Channel 读取到数据时, 会触发一个 READER_IDLE 的 IdleStateEvent 事件.

  • writerIdleTimeSeconds: 写超时. 即当在指定的时间间隔内没有数据写入到 Channel 时, 会触发一个 WRITER_IDLE 的 IdleStateEvent 事件.

  • allIdleTimeSeconds: 读/写超时. 即当在指定的时间间隔内没有读或写操作时, 会触发一个 ALL_IDLE 的 IdleStateEvent 事件.

这三个参数默认的时间单位是秒。

4.案例

需求:

(1)编写一个 Netty 心跳检测机制案例,当服务器超过3秒没有读时,就提示读空闲;

(2)当服务器超过5秒没有写操作时,就提示写空闲;

(3)实现当服务器超过7秒没有读或写操作时,就提示读写空闲。

服务器端代码:

/**
 * 服务器端
 */
public class MyServer {
    public static void main(String[] args) throws InterruptedException {
        // 创建两个线程组
        NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);
        NioEventLoopGroup workerGroup = new NioEventLoopGroup(8);

        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .handler(new LoggingHandler(LogLevel.INFO)) // 在bossgroup增加一个日志处理器
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            // 加入 netty 提供的 IdleStateHandler
                            /**
                             * 说明
                             * 1. IdleStateHandler 是 netty 提供的处理空闲状态的处理器
                             * 2. readerIdleTime:表示多长时间没有读,就会发送一个心跳检测包,检测是否还是连接状态
                             * 3. writerIdleTime:表示多长时间没有写,就会发送一个心跳检测包,检测是否还是连接状态
                             * 4. allIdleTime:表示多长时间没有读写,就会发送一个心跳检测包,检测是否还是连接状态
                             * 5. 当 IdleStateEvent 触发后,就会传递给管道的下一个handler进行处理,通过调用(触发)下一个 handler 的userEventTrigged 方法,
                             * 在该方法中去处理 IdleStateEvent 事件
                             */
                            pipeline.addLast(new IdleStateHandler(3,5,7, TimeUnit.SECONDS));
                            // 加入一个对空闲检测进一步处理的handler(自定义)
                            pipeline.addLast(new MyServerHandler());
                        }
                    });

            ChannelFuture channelFuture = serverBootstrap.bind(7000).sync();
            channelFuture.channel().closeFuture().sync();

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

/**
 * 心跳处理器
 */
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 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("服务器做相应处理......");
        }
    }
}

客户端代码:

/**
 * 客户端
 */
public class MyClient {
    private final String host;

    private final int port;

    public MyClient(String host, int port) {
        this.host = host;
        this.port = port;
    }

    public void run() throws InterruptedException {
        EventLoopGroup group = new NioEventLoopGroup();

        try {
            Bootstrap bootstrap = new Bootstrap();

            bootstrap.group(group)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            // 得到 pipeline
                            ChannelPipeline pipeline = ch.pipeline();
                            // 向 pipeline 加入一个解码器
                            pipeline.addLast("decoder", new StringDecoder());
                            // 向 pipeline 加入一个编码器
                            pipeline.addLast("encoder", new StringEncoder());
                            // 加入自己的业务处理 handler
                            pipeline.addLast(new GroupChatClientHandler());
                        }
                    });

            ChannelFuture channelFuture = bootstrap.connect(host, port).sync();
            // 得到channel
            Channel channel = channelFuture.channel();
            System.out.println("--------" + channel.localAddress() + "----------");

            // 客户端需要输入信息,创建扫描器 scanner
            Scanner scanner = new Scanner(System.in);
            while (scanner.hasNextLine()){
                String msg = scanner.nextLine();
                // 通过 channel 发送到服务器端
                channel.writeAndFlush(msg + "\r\n");
            }
        }finally {
            group.shutdownGracefully();
        }

    }

    public static void main(String[] args) {
        MyClient groupChatClient = new MyClient("127.0.0.1", 7000);
        try {
            groupChatClient.run();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public class MyClientHandler extends SimpleChannelInboundHandler<String> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
        System.out.println(msg.trim());
    }
}

测试:先启动服务器端,在启动客户端,测试结果如下。

netty入门(十六)netty心跳监测机制

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由半码博客整理,本文链接:https://www.bmabk.com/index.php/post/142584.html

(0)

及时掌握行业动态,欢迎加入几百人的后端技术交流群:


相关推荐

  • 并发工具类使用详解及区别(CountDownLatch、CyclicBarrier、Semaphore、Exchanger)

    导读:本篇文章讲解 并发工具类使用详解及区别(CountDownLatch、CyclicBarrier、Semaphore、Exchanger),希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

    Java 2022年5月24日
    0042
  • @RequestBody与@RequestParam

    导读:本篇文章讲解 @RequestBody与@RequestParam,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

    后端开发 2023年1月20日
    0026
  • mkdir与mkdirs的区别

    导读:本篇文章讲解 mkdir与mkdirs的区别,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

    后端开发 2022年5月14日
    0053
  • 10-Nginx资源分离场景实践

    1、Nginx通过负载均衡实现手机与PC调度至不同的后端节点应用案例 (1)根据IPhone、安卓、PC跳转不同的页面环境规划     主机…

    2022年12月25日
    0045
  • 在K8S中,静态、动态、自主式Pod有何区别?

    如果你不相信努力和时光,那么成果就会是第一个选择辜负你的。不要去否定你自己的过去,也不要用你的过去牵扯你现在的努力和对未来的展望。不是因为拥有希望你才去努力,而是去努力了,你才有可能看到希望的光芒。在K8S中,静态、动态、自主式Pod有何区别?,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

    后端开发 2024年2月2日
    0050
  • 线程死锁(文学家与艺术家例子)

    导读:本篇文章讲解 线程死锁(文学家与艺术家例子),希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

    2022年5月12日
    0031
  • Windows下 QT5.13+OpenCV安装

    生活中,最使人疲惫的往往不是道路的遥远,而是心中的郁闷;最使人痛苦的往往不是生活的不幸,而是希望的破灭;最使人颓废的往往不是前途的坎坷,而是自信的丧失;最使人绝望的往往不是挫折的打击,而是心灵的死亡。所以我们要有自己的梦想,让梦想的星光指引着我们走出落漠,走出惆怅,带着我们走进自己的理想。

    导读:本篇文章讲解 Windows下 QT5.13+OpenCV安装,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

    Go语言 2023年4月23日
    00103
  • 千峰商城-springboot项目搭建-19-vue的基本语法

    追求适度,才能走向成功;人在顶峰,迈步就是下坡;身在低谷,抬足既是登高;弦,绷得太紧会断;人,思虑过度会疯;水至清无鱼,人至真无友,山至高无树;适度,不是中庸,而是一种明智的生活态度。

    导读:本篇文章讲解 千峰商城-springboot项目搭建-19-vue的基本语法,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

    Android 2023年3月27日
    0039
  • Java实现远程服务生产与消费(RPC)的4种方法-RMI,WebService,HttpClient,RestTemplate

    导读:本篇文章讲解 Java实现远程服务生产与消费(RPC)的4种方法-RMI,WebService,HttpClient,RestTemplate,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

    2022年5月22日
    0039
  • MySQL基础(七)单行函数

    命运对每个人都是一样的,不一样的是各自的努力和付出不同,付出的越多,努力的越多,得到的回报也越多,在你累的时候请看一下身边比你成功却还比你更努力的人,这样,你就会更有动力。

    导读:本篇文章讲解 MySQL基础(七)单行函数,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

    后端开发 2023年5月10日
    0033

发表回复

登录后才能评论