netty-实现心跳机制

netty基础概念

Channel

管道,其是对 Socket 的封装,其包含了一组 API,大大简化了直接与 Socket 进行操作的 复杂性。

EventLoopGroup

EventLoopGroup 是一个 EventLoop 池,包含很多的 EventLoop。 Netty 为每个 Channel 分配了一个 EventLoop,用于处理用户连接请求、对用户请求的处 理等所有事件。EventLoop 本身只是一个线程驱动,在其生命周期内只会绑定一个线程,让 该线程处理一个 Channel 的所有 IO 事件。 一个 Channel 一旦与一个 EventLoop 相绑定,那么在 Channel 的整个生命周期内是不能 改变的。一个 EventLoop 可以与多个 Channel 绑定。即 Channel 与 EventLoop 的关系是 n:1, 而 EventLoop 与线程的关系是 1:1。

ServerBootStrap

用于配置整个 Netty 代码,将各个组件关联起来。服务端使用的是 ServerBootStrap,而 客户端使用的是则 BootStrap。

ChannelHandler 与 ChannelPipeline

ChannelHandler 是对 Channel 中数据的处理器,这些处理器可以是系统本身定义好的编 解码器,也可以是用户自定义的。这些处理器会被统一添加到一个 ChannelPipeline 的对象中, 然后按照添加的顺序对 Channel 中的数据进行依次处理。

ChannelFuture

Netty 中所有的 I/O 操作都是异步的,即操作不会立即得到返回结果,所以 Netty 中定义 了一个 ChannelFuture 对象作为这个异步操作的“代言人”,表示异步操作本身。如果想获取 到该异步操作的返回值,可以通过该异步操作对象的 addListener()方法为该异步操作添加监听器,为其注册回调:当结果出来后马上调用执行。 Netty 的异步编程模型都是建立在 Future 与回调概念之上的。

Netty 执行流程

image-20210222003356475

心跳机制的实现

需求:客户端和服务端通过心跳机制保持连接,本案例由客户端向服务端发送心跳,通过随机数的方式每隔1-8秒发送一次心跳,服务器端定义读空闲时间为5秒,即五秒没有收到心跳则主动断开连接,同时客户端检测到断开连接后,会尝试重连。

pom文件

<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-all</artifactId>
    <version>4.1.36.Final</version>
</dependency>

服务端

定义服务端启动类

// 定义服务端启动类
public class SomeServer {
    public static void main(String[] args) throws InterruptedException {
        NioEventLoopGroup parentGroup = new NioEventLoopGroup();
        NioEventLoopGroup childGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(parentGroup, childGroup)
                     .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {

                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            // StringDecoder:字符串解码器,将Channel中的ByteBuf数据解码为String
                            pipeline.addLast(new StringDecoder());
                            // StringEncoder:字符串编码器,将String编码为将要发送到Channel中的ByteBuf
                            pipeline.addLast(new IdleStateHandler(5,0,0));
                            pipeline.addLast(new SomeServerHandler());
                        }
                    });
            ChannelFuture future = bootstrap.bind(8888).sync();
            System.out.println("服务器已启动");
            future.channel().closeFuture().sync();
        } finally {
            parentGroup.shutdownGracefully();
            childGroup.shutdownGracefully();
        }
    }
}

定义读操作处理器

public class SomeServerHandler extends SimpleChannelInboundHandler {
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, Object o) throws Exception {
        System.out.println("接收到客户端发送的心跳:"+o);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.close();
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (evt instanceof IdleStateEvent){
            IdleState state = ((IdleStateEvent) evt).state();
            if (state==IdleState.READER_IDLE){
                System.out.println("将连接断开");
                //断开连接
                ctx.disconnect();
            }else {
                super.userEventTriggered(ctx, evt);
            }
        }
    }
}

客户端

定义客户端启动类

public class SomeClient {
    public static void main(String[] args) throws InterruptedException {
        NioEventLoopGroup group = new NioEventLoopGroup();

        Bootstrap bootstrap = new Bootstrap();
        bootstrap.group(group)
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        ChannelPipeline pipeline = ch.pipeline();
//                            pipeline.addLast(new StringDecoder());
                        pipeline.addLast(new StringEncoder());
                        pipeline.addLast(new SomeClientHandler(bootstrap));
                    }
                });
        bootstrap.connect("localhost", 8888).sync();
    }
}

定义随机发送心跳处理器

public class SomeClientHandler extends ChannelInboundHandlerAdapter {

    private Bootstrap bootstrap;

    public SomeClientHandler(Bootstrap bootstrap) {
        this.bootstrap = bootstrap;
    }

    // 当Channel被激活后会触发该方法的执行
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
//        ctx.channel().writeAndFlush("from client:begin talking");
        //随机发送心跳
        randomSendHeartBeat(ctx.channel());
    }

    //只要chanel被钝化(关闭),就会触发该方法
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        ctx.channel().eventLoop().schedule(()->{
            System.out.println("正在尝试重连~~~~~~");
            try {
                bootstrap.connect("localhost", 8888).sync();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },1,TimeUnit.SECONDS);

    }

    private void randomSendHeartBeat(Channel channel) {
        //生成一个1-8的随机数,作为心跳发送间隔
        int heartBeatInternal = new Random().nextInt(7) + 1;
        System.out.println(heartBeatInternal + "秒后将发送下一次心跳");
        ScheduledFuture<?> schedule = channel.eventLoop().schedule(() -> {
            if (channel.isActive()) {
                channel.writeAndFlush("PING~~~");
            } else {
                System.out.println("与服务的连接已经断开");
                channel.closeFuture();
            }
        }, heartBeatInternal, TimeUnit.SECONDS);
        //为异步定时任务添加监听器
        schedule.addListener((future -> {
            //若异步定时任务执行成功,则重新随机发送心跳
            if (future.isSuccess()){
                randomSendHeartBeat(channel);
            }
        }));
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.close();
    }
}

执行效果

image-20210222004100005

image-20210222004111361

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值