Netty——心跳机制与断线重连

news/2024/5/18 8:23:08 标签: java, 开发语言, Netty, 心跳机制, 断线重连

心跳机制断线重连


为了保证系统的稳定性,心跳机制断线重连可是必不可少的,而这两个在Netty中也是非常好实现的

心跳机制

我们以客户端发送心跳为例,平时我们的心跳实现方式可能是搞个定时器,定时发送是吧,但是在Netty中却不一样,心跳被称为空闲检测,因为心跳的最主要作用就是判断是否存活、是否假死等情况,所以Netty中并不是定时发送,而是空闲的情况下才发送,空闲指的是一段时间内无读写事件的发生,也就是没有信息接收和信息发送啦,这时候我就要检测一下了

所以Netty的机制就是通过监测一段时间内是否有信息的读、写发生,然后产生一个事件,我们可以处理这个事件来达到我们的目的

IdleStateHandler

心跳与其他的数据处理Handler一样,都是建立连接后的数据处理,所以同样都是处理管道中的,IdleStateHandler就是Netty帮我们封装好了的处理类,我们可以直接使用并将它放入管道中,就可以自动检测一段时间内是否有读、写发生了,常用的构造有4个参数:

  • readerIdleTime:读事件检测,该时间内没有信息读取就会产生一个读信息的事件
  • writerIdleTime:写事件检测,该时间内没有信息发送就会产生一个写信息的事件
  • allIdleTime:读写事件检测,该时间内没有信息发送或者读取就会产生一个读写信息的事件
  • unit:时间单位
public IdleStateHandler(long readerIdleTime, long writerIdleTime, long allIdleTime, TimeUnit unit) {
    this(false, readerIdleTime, writerIdleTime, allIdleTime, unit);
}

使用

// 管道中直接添加
// 时间配置为0则代表不检测对应事件
channel.pipeline().addLast(new IdleStateHandler(5,0,0, TimeUnit.SECONDS))

触发的事件类型

  • IdleState.READER_IDLE:读信息的事件
  • IdleState.WRITER_IDLE:写信息的事件
  • IdleState.ALL_IDLE:读写信息的事件

举个例子:假设我们是客户端主动发送心跳,那客户端就要检测一段时间内是否有写信息的操作,如果没有就会触发IdleState.WRITER_IDLE事件,那我们监听这个事件,就做出自己的处理,那就是主动发送心跳包嘛

下面我们就以客户端发送心跳包为例!

客户端

像上面那样直接使用IdleStateHandler,那还需要单独写一个事件处理的类,所以这里我们直接继承IdleStateHandler

ClientHeartbeatHandler 如下:

我们设置5秒,5秒内没有写过信息,我们就发送心跳包(这里消息很随意,图方便)

@Slf4j
public class ClientHeartbeatHandler extends IdleStateHandler {

    // 设置写事件为5s
    // 如果5s没有写事件发生 就会触发下面的IdleStateEvent
    private static final int WRITE_IDLE_TIME = 5;

    public ClientHeartbeatHandler() {
        super(0, WRITE_IDLE_TIME, 0, TimeUnit.SECONDS);
    }

    @Override
    protected void channelIdle(ChannelHandlerContext ctx, IdleStateEvent evt) throws Exception {
        // 指定时间内没有写事件发送 就会触发 IdleState.WRITER_IDLE 类型事件
        // 我们就可以对该连接进行处理 主动发送心跳
        if(evt.state()== IdleState.WRITER_IDLE){
            log.info("{} 秒内没有发送数据,再不发送小心和服务端断开连接", WRITE_IDLE_TIME);
            ctx.writeAndFlush(new NettyMsg(ServiceCodeEnum.TEST_TYPE,"我还活着,不要断开连接"));
        }
    }
}

NettyClient

管道中加入这个即可,注意要放在编解码之后

在这里插入图片描述

服务端

基本跟客户端一样,但是由于是客户端给我发心跳,所以服务端要监测读事件

ServerHeartbeatHandler

这里我们设置10s,一般会更多因为要给客户端一些容忍度,适量就好,一旦没满足就和客户端断开连接

@Slf4j
public class ServerHeartbeatHandler extends IdleStateHandler {

    // 设置读取事件为10s
    // 如果10s没有读事件发生 就会触发下面的IdleStateEvent
    private static final int READER_IDLE_TIME = 10;

    public ServerHeartbeatHandler() {
        super(READER_IDLE_TIME, 0, 0, TimeUnit.SECONDS);
    }

    @Override
    protected void channelIdle(ChannelHandlerContext ctx, IdleStateEvent evt) throws Exception {
        // 指定时间内没有读事件发送 就会触发 IdleState.READER_IDLE 类型事件
        // 我们就可以对该连接进行处理 这里是直接关闭
        if(evt.state()== IdleState.READER_IDLE){
            log.info("{} 秒内没有读取到数据,关闭连接", READER_IDLE_TIME);
            ctx.channel().close();
        }
    }
}

NettyServer

在这里插入图片描述

测试

正常情况

服务端

在这里插入图片描述

客户端

在这里插入图片描述

异常情况

我们将客户端心跳拉长

在这里插入图片描述

可以发现时间一到自动断连

在这里插入图片描述

总结

这里是以客户端发送心跳为例,如果是以服务端发送心跳也是一样,主要就是对读写事件的监听,然后做出相应处理,上面其实是很随意的哈,正常情况下心跳包是会用专门的消息类型的,而且服务端也需要做出应答的,心跳应答也会有对应的消息类型

断线重连

这个就很简单了,肯定是断线后,客户端发起重连嘛,就是在客户端检测到连接断开的时候,重新连接就好了

ClientHeartbeatHandler加上一段:

在这里插入图片描述

看看效果,客户端心跳时间还是12s哈

在这里插入图片描述

能这么随意吗?当然不行啊,别学我,正常会搞多次重连,而且谨慎起见,每次重连还可以加一个时间的间隔,可以学一下nacos里面的高级玩法,随着重连次数的增加,间隔重试的时间也随之增加,比如:

第一次重连是4s,第二次重连是8s,第三次是16s,以此类推
也就是设置重连间隔时间基数为4s,每尝试一次,左移一位(4<<1),连接成功后基数复位为4s

需要设置最大重试次数,最长间隔时间(一直左移后面会很长时间,所以要设置一个上限,超过了就以上限为准)

伪代码如下

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        log.info(ctx.channel().remoteAddress() + " 已断开连接,开始重连");
        int maxRetryNum=6; //最大重连次数
        int maxRetryTime=60; // 最大重连时间
        int baseRetryTime=4; // 间隔时间基数
        // 开始重连
        for (int i = 1; i <= maxRetryNum; i++) {
            log.info("第{}次重连,间隔{}s后开始",i,baseRetryTime > maxRetryTime ? maxRetryTime:baseRetryTime);
            ctx.channel().eventLoop().schedule(()->{
                // 重连操作
                // .......

                // 判断是否超过隔间上限
            },baseRetryTime > maxRetryTime ? maxRetryTime:baseRetryTime,TimeUnit.SECONDS);
            // 左移扩大一倍
            baseRetryTime = baseRetryTime << 1;

        }
        log.info("这么多次重连都不行寄了吧");
    }

伪代码哈,伪代码!

结果如下

建立连接后,手动关闭服务端,可以看到重连了6次,每次间隔时间翻倍,达到间隔时间上限后,就以上限的时间为准
在这里插入图片描述


http://www.niftyadmin.cn/n/115611.html

相关文章

数据库基础-数据库的基本操作(1-2)

你好&#xff0c;欢迎来到数据库基础系列专栏&#xff0c;欢迎留言互动哦~ 点击访问上一篇数据库基本概念&#xff08;1-1&#xff09; 目录2. 数据库的基本操作2.1 数据库基础1. 系统数据库2. 用户数据库2.2 新建数据库&#xff08;sql 语句&#xff09;2.3 查询和选择数据库…

SpringCloud源码探析(三)-Nacos集群搭建与配置管理

1.概述 上一篇文章SpringCloud源码探析&#xff08;二&#xff09;-Nacos注册中心分析了nacos单机版的部署以及SpringBoot整合nacos&#xff0c;nacos不仅仅可以作为注册中心&#xff0c;也可以作为配置中心。本文将在上文的基础上&#xff0c;进行nacos集群的搭建&#xff0c…

邮件发送,正文内容格式h5,模板字符串拼接使用及赋值,原生js

一、邮件发送 需求不难&#xff0c;传指定的参数给后端&#xff0c;调接口就直接发送了 就是参数里有个正文部分&#xff0c;一定要用h5写 写vue写习惯了&#xff0c;原生的都快忘光了&#xff0c;走了些弯路 二、h5 在页面上有个邮件发送的按钮&#xff0c;点击按钮打开弹窗…

Kafka详解(一)

kafka使用场景 canal同步mysqlelk日志系统业务系统Topic kafka基础概念 Producer: 消息生产者&#xff0c;向kafka发送消息Consumer: 从kafka中拉取消息消费的客户端Consumer Group: 消费者组&#xff0c;消费者组是多个消费者的集合。消费者组之间互不影响&#xff0c;所有…

企业电子招标采购系统之项目说明和开发类型

项目说明 随着公司的快速发展&#xff0c;企业人员和经营规模不断壮大&#xff0c;公司对内部招采管理的提升提出了更高的要求。在企业里建立一个公平、公开、公正的采购环境&#xff0c;最大限度控制采购成本至关重要。符合国家电子招投标法律法规及相关规范&#xff0c;以及…

数据结构刷题(十五):513找树左下角的值、112路径总和、113路径总和II、106从中序与后序遍历序列构造二叉树

1.找树左下角的值题目链接思路&#xff1a;层序遍历迭代法&#xff1b;只需要记录最后一行第一个节点的数值就可以了注意&#xff1a;使用queue解法&#xff1a;public int findBottomLeftValue(TreeNode root){Queue<TreeNode> queue new LinkedList<>();queue.o…

华为OD机试用Python实现 -【解压缩算法】

华为OD机试题 最近更新的博客华为 OD 机试 300 题大纲解压缩算法题目描述输入描述输出描述说明示例一输入输出说明示例二输入输出说明代码编写思路Python 代码实现最近更新的博客 华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单

js实现翻盘抽奖

<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>礼物编辑</title><style>* {margin: 0;padding: 0;box-sizing: border-box;list-style-type: none;text-decoration: none;}.container {d…