Netty学习篇④-心跳机制及断线重连

心跳检测

  1. 前言
    客户端和服务端的连接属于socket连接,也属于长连接,往往会存在客户端在连接了服务端之后就没有任何操作了,但还是占用了一个连接;当越来越多类似的客户端出现就会浪费很多连接,netty中可以通过心跳检测来找出一定程度(自定义规则判断哪些连接是无效链接)的无效链接并断开连接,保存真正活跃的连接。
  2. 什么叫心跳检测
    我理解的心跳检测应该是客户端/服务端定时发送一个数据包给服务端/客户端,检测对方是否有响应;
    如果是存活的连接,在一定的时间内应该会收到响应回来的数据包;
    如果在一定时间内还是收不到接收方的响应的话,就可以当做是挂机,可以断开此连接;
    如果检测到了掉线之后还可以进行重连;
  3. 心跳检测的实现
    • TCP自带心跳检测,协议层采用Keeplive机制默认2小时频率触发一次检测,但是它存在缺陷:检测不出网线拔出、防火墙、使用起来不灵活、依赖操作系统等
    • Netty可以通过IdleStateHandler来实现心跳检测,使用起来也非常方便清晰
  4. IdleStateHandler原理
    idleStateHandler在通道注册之后会开启一个定时任务,定时去检测通道中后续是否还有进行数据传输,如果在规定的时间内没有进行数据传输则会触发对应的超时事件,使用者可以根据对应的事件自定义规则来判别当前连接是否是活跃,是否需要关闭连接等来进行操作。
    一般idleStateHandler触发的事件IdleStateEvent会在心跳handler中的userEventTriggered方法中捕获到对应的超时事件。

    IdleStateHandler的继承关系:通过ChannelDuplexHandler类继承ChannelInboundHandler和实现ChannelOutboundHandler来实现对入站和出站的重写和监控

  5. 源码分析
    • IdleStateHandler初始化:为0代表不监控

      /**
      * @observeOutput 观察输出
      * @readerIdleTime 读超时时间 自定义时间内检测Channel通道有没有读取到数据,为0代表不监控
      * @writerIdleTime 写超时时间 自定义时间内检测Channel通道有没有write数据,为0代表不监控
      * @allIdleTime 总超时时间 自定义时间内检测Channel通道有没有读/写数据,为0代表不监控
      * @unit 时间单位
      */
      public IdleStateHandler(boolean observeOutput,
                  long readerIdleTime, long writerIdleTime, long allIdleTime,
                  TimeUnit unit) {
              if (unit == null) {
                  throw new NullPointerException("unit");
              }
      
              this.observeOutput = observeOutput;
      
             // 初始化读取空闲时间,最小值为0
              if (readerIdleTime <= 0) {
                  readerIdleTimeNanos = 0;
              } else {
                  // 定义读取超时时间为自定义设置时间
                  readerIdleTimeNanos = Math.max(unit.toNanos(readerIdleTime), MIN_TIMEOUT_NANOS);
              }
              if (writerIdleTime <= 0) {
                  writerIdleTimeNanos = 0;
              } else {
                  // 设置写超时时间
                  writerIdleTimeNanos = Math.max(unit.toNanos(writerIdleTime), MIN_TIMEOUT_NANOS);
              }
              if (allIdleTime <= 0) {
                  allIdleTimeNanos = 0;
              } else {
                  // 设置总超时时间
                  allIdleTimeNanos = Math.max(unit.toNanos(allIdleTime), MIN_TIMEOUT_NANOS);
              }
          }
    • IdleStateHandler和channel通道的关联:通过类图可以得知,idleStateHandler可以重写入站和出站的方法,不过只是通过channelRead和write方法来记录阅读的时间等不做其他操作
      @Override
      public void channelActive(ChannelHandlerContext ctx) throws Exception {
          // 初始化检测器,开启定时任务
          initialize(ctx);
          super.channelActive(ctx);
      }
      
      @Override
      public void channelInactive(ChannelHandlerContext ctx) throws Exception {
          // 在通道非活跃状态的时候销毁定时任务
          destroy();
          super.channelInactive(ctx);
      }
      
      @Override
      public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
          // 如果设置的读超时时间大于0则设置是否读操作为true
          if (readerIdleTimeNanos > 0 || allIdleTimeNanos > 0) {
              reading = true;
              firstReaderIdleEvent = firstAllIdleEvent = true;
          }
          // 记录时间和标识标志之后就直接fire当前read到下一个ChannelHandler处理类中
          ctx.fireChannelRead(msg);
      }
      
      @Override
      public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
          // 当读取完毕时,设置是否正在读取为false,设置最后读取时间为系统当前时间
          if ((readerIdleTimeNanos > 0 || allIdleTimeNanos > 0) && reading) {
              lastReadTime = ticksInNanos();
              reading = false;
          }
          // 同样直接fire掉,跳入到下一个handler中
          ctx.fireChannelReadComplete();
      }
      
      // idleStateHandler重写的write方法
      @Override
      public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
          // Allow writing with void promise if handler is only configured for read timeout events.
          if (writerIdleTimeNanos > 0 || allIdleTimeNanos > 0) {
              ctx.write(msg, promise.unvoid()).addListener(writeListener);
          } else {
              ctx.write(msg, promise);
          }
      }
      
      // ChannelOutboundHandler提供的write接口方法
      /**
          * Called once a write operation is made. The write operation will write the messages through the
           * {@link ChannelPipeline}. Those are then ready to be flushed to the actual {@link Channel} once
           * {@link Channel#flush()} is called
           * 执行一次写操作;写操作通过ChannelPipeline来传输信息;最后通过channel的flush()方法来刷新
           *
           * @param ctx               the {@link ChannelHandlerContext} for which the write operation is made 实际写操作者
           * @param msg               the message to write 写的消息
           * @param promise           the {@link ChannelPromise} to notify once the operation completes 在操作完成时立即通知(类似future异步通知)
           * @throws Exception        thrown if an error occurs
           */
          void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception;
    • 定时器初始化/销毁:将使用者自定义的超时时间设置为延迟定时任务
      初始化:
      
      private void initialize(ChannelHandlerContext ctx) {
              // Avoid the case where destroy() is called before scheduling timeouts.
              // See: https://github.com/netty/netty/issues/143
             // state; // 0 - none, 1 - 初始化, 2 - 销毁
              switch (state) {
              case 1:
              case 2:
                  return;
              }
      
              state = 1;
              initOutputChanged(ctx);
             // 最后读取时间
              lastReadTime = lastWriteTime = ticksInNanos();
             // 如果设置的空闲时间大于0则开启定时任务进行监控
              if (readerIdleTimeNanos > 0) {
                  readerIdleTimeout = schedule(ctx, new ReaderIdleTimeoutTask(ctx),
                          readerIdleTimeNanos, TimeUnit.NANOSECONDS);
              }
              // 写超时时间
              if (writerIdleTimeNanos > 0) {
                  writerIdleTimeout = schedule(ctx, new WriterIdleTimeoutTask(ctx),
                          writerIdleTimeNanos, TimeUnit.NANOSECONDS);
              }
              // 总超时时间
              if (allIdleTimeNanos > 0) {
                  allIdleTimeout = schedule(ctx, new AllIdleTimeoutTask(ctx),
                          allIdleTimeNanos, TimeUnit.NANOSECONDS);
              }
          }
      
      销毁:
      
      /**
      * @readerIdleTimeout ==> ScheduledFuture类
      * @writerIdleTimeout ==> ScheduledFuture类
      */
      private void destroy() {
             // 设置销毁状态
              state = 2;
             // 销毁线程
             // ScheduledFuture
              if (readerIdleTimeout != null) {
                  readerIdleTimeout.cancel(false);
                  readerIdleTimeout = null;
              }
              if (writerIdleTimeout != null) {
                  writerIdleTimeout.cancel(false);
                  writerIdleTimeout = null;
              }
              if (allIdleTimeout != null) {
                  allIdleTimeout.cancel(false);
                  allIdleTimeout = null;
              }
          }
    • 开启定时任务
      ScheduledFuture<?> schedule(ChannelHandlerContext ctx, Runnable task, long delay, TimeUnit unit) {
              return ctx.executor().schedule(task, delay, unit);
          }
    • 读/写定时任务:定时任务启动的时候,通过设置的超时时间和上一次触发channelRead的时间进行相减比较来判断是否超时了
      // 仅分析读取超时时间定时任务,写超时差不多就是触发的时间不一样,比对的变量换成了设置的写超时时间
      private final class ReaderIdleTimeoutTask extends AbstractIdleTask {
      
              ReaderIdleTimeoutTask(ChannelHandlerContext ctx) {
                  super(ctx);
              }
      
              @Override
              protected void run(ChannelHandlerContext ctx) {
                  // readerIdleTimeNanos:初始化IdleStateHandler设置的读取超时时间
                  long nextDelay = readerIdleTimeNanos;
                  // 如果没有任何读取操作
                  if (!reading) {
                      // 判断是否有超时
                      // nextDelay = nextDelay-(ticksInNanos() - lastReadTime)
                      // 即设置的超时时间减去距离上一次读取的时间
                      nextDelay -= ticksInNanos() - lastReadTime;
                  }
                 // 如果小于等于0 则触发读取超时事件,设置新的延迟时间
                  if (nextDelay <= 0) {
                      // Reader is idle - set a new timeout and notify the callback.
                      readerIdleTimeout = schedule(ctx, this, readerIdleTimeNanos, TimeUnit.NANOSECONDS);
                     // 标记为第一次
                      boolean first = firstReaderIdleEvent;
                      // 设置成非第一次
                      firstReaderIdleEvent = false;
      
                      try {
                          IdleStateEvent event = newIdleStateEvent(IdleState.READER_IDLE, first);
                          channelIdle(ctx, event);
                      } catch (Throwable t) {
                          ctx.fireExceptionCaught(t);
                      }
                  } else {
                      // Read occurred before the timeout - set a new timeout with shorter delay.
                      // 超时的时候发生的读取事件,则重新延迟执行
                      readerIdleTimeout = schedule(ctx, this, nextDelay, TimeUnit.NANOSECONDS);
                  }
              }
          }
  6. 项目实战(主要代码,项目以服务端作为心跳监控,也可以在客户端进行心跳监控)
    • 项目结构

      ├─src
      │  ├─main
      │  │  ├─java
      │  │  │  └─com
      │  │  │      └─hetangyuese
      │  │  │          └─netty
      │  │  │              ├─client
      │  │  │              │      MyChannelFutureListener.java
      │  │  │              │      MyClient05.java
      │  │  │              │      MyClientChannelHandler.java
      │  │  │              │      MyClientChannelInitializer.java
      │  │  │              │
      │  │  │              └─server
      │  │  │                  │  MyServer05.java
      │  │  │                  │  MyServerChannelInitializer.java
      │  │  │                  │  MyServerHandler.java
      │  │  │                  │
      │  │  │                  └─decoder

      ?

    • 在ChannelPipeline中注册IdleStateHandler
      public class MyServerChannelInitializer extends ChannelInitializer<SocketChannel> {
      
          @Override
          protected void initChannel(SocketChannel ch) throws Exception {
              ch.pipeline().
                      addLast(new StringEncoder(Charset.forName("GBK")))
                      .addLast(new StringDecoder(Charset.forName("GBK")))
                      .addLast(new LoggingHandler(LogLevel.INFO))
                     // 设置读取超时时间为5秒,写超时和总超时为0即不做监控
                      .addLast(new IdleStateHandler(5, 0, 0))
                      .addLast(new MyServerHandler());
          }
      }
    • 服务端处理handler(其中userEventTriggered为接收心跳任务触发的事件,这次做了计数三次触发读空闲超时则断开连接)
      public class MyServerHandler extends ChannelInboundHandlerAdapter {
      
          private AtomicInteger count = new AtomicInteger(1);
      
          /**
           *  心跳检测机制会进入
           * @param ctx
           * @param evt
           * @throws Exception
           */
          @Override
          public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
              System.out.println("心跳检测触发了事件, object: , time: " + evt + new Date().toLocaleString());
              super.userEventTriggered(ctx, evt);
              if (evt instanceof IdleStateEvent) {
                  IdleStateEvent e = (IdleStateEvent) evt;
                  // 客户端连接应该是请求 write
                  if (e.state() == IdleState.READER_IDLE) {
                      System.out.println("服务端监测到了读取超时");
                      count.incrementAndGet();
                      if (count.get() > 3) {
                          System.out.println("客户端还在?? 已经3次检测没有访问了,我要断开了哦!!!");
                          ctx.channel().close();
                      }
                  } else if (e.state() == IdleState.WRITER_IDLE) {
                      // 如果一直有交互则会发送writer_idle
                      System.out.println("服务端收到了写入超时");
                  } else {
                      System.out.println("服务端收到了All_idle");
                  }
              } else {
                  super.userEventTriggered(ctx,evt);
              }
          }
      
          @Override
          public void channelActive(ChannelHandlerContext ctx) throws Exception {
              System.out.println("myServerHandler is active, time: " + new Date().toLocaleString());
              ctx.writeAndFlush("成功连接服务端, 当前时间:" + new Date().toLocaleString());
          }
      
          @Override
          public void channelInactive(ChannelHandlerContext ctx) throws Exception {
              System.out.println("服务端与客户端断开了连接, time: " + new Date().toLocaleString());
          }
      
          @Override
          public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
              System.out.println("myServerHandler 收到了客户端的信息 msg:" + msg + ", time: " + new Date().toLocaleString());
              ctx.writeAndFlush("您好,客户端,我是服务端");
          }
      
          @Override
          public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
              cause.printStackTrace();
              ctx.close();
          }
      }
    • 运行结果
      myServer is start time: 2019-11-8 14:47:16
      myServerHandler is active, time: 2019-11-8 14:47:18
      十一月 08, 2019 2:47:18 下午 io.netty.handler.logging.LoggingHandler channelRegistered
      信息: [id: 0x8d924d06, L:/127.0.0.1:9001 - R:/127.0.0.1:62195] REGISTERED
      十一月 08, 2019 2:47:18 下午 io.netty.handler.logging.LoggingHandler channelActive
      信息: [id: 0x8d924d06, L:/127.0.0.1:9001 - R:/127.0.0.1:62195] ACTIVE
      十一月 08, 2019 2:47:18 下午 io.netty.handler.logging.LoggingHandler write
      信息: [id: 0x8d924d06, L:/127.0.0.1:9001 - R:/127.0.0.1:62195] WRITE: 成功连接服务端, 当前时间:2019-11-8 14:47:18
      十一月 08, 2019 2:47:18 下午 io.netty.handler.logging.LoggingHandler flush
      信息: [id: 0x8d924d06, L:/127.0.0.1:9001 - R:/127.0.0.1:62195] FLUSH
      心跳检测触发了事件, object: , time: [email protected] 14:47:23
      服务端监测到了读取超时
      心跳检测触发了事件, object: , time: [email protected] 14:47:28
      服务端监测到了读取超时
      心跳检测触发了事件, object: , time: [email protected] 14:47:33
      服务端监测到了读取超时
      客户端还在?? 已经3次检测没有访问了,我要断开了哦!!!
      十一月 08, 2019 2:47:33 下午 io.netty.handler.logging.LoggingHandler close
      信息: [id: 0x8d924d06, L:/127.0.0.1:9001 - R:/127.0.0.1:62195] CLOSE
      十一月 08, 2019 2:47:33 下午 io.netty.handler.logging.LoggingHandler channelInactive
      信息: [id: 0x8d924d06, L:/127.0.0.1:9001 ! R:/127.0.0.1:62195] INACTIVE
      服务端与客户端断开了连接, time: 2019-11-8 14:47:33
      十一月 08, 2019 2:47:33 下午 io.netty.handler.logging.LoggingHandler channelUnregistered
      信息: [id: 0x8d924d06, L:/127.0.0.1:9001 ! R:/127.0.0.1:62195] UNREGISTERED
以上就是心跳监控的所有流程了,合理的利用Netty的心跳机制可以有效的剔除一些无用的连接释放些资源

Netty断线重连:在长连接中有时候出现断开的时候可以重新连接

出现断线重连的情况:

  • 首次连接但是连接不上,通过ChannelFutureListener增加监控进行重连
  • 由于网络原因等、自动断开等 通过在channelInactive中进行重连即可
  1. ChannelFutureListener进行重连

    public class MyChannelFutureListener implements ChannelFutureListener {
    
        @Override
        public void operationComplete(ChannelFuture future) throws Exception {
            if (future.isSuccess()) {
                System.out.println("当前已连接");
                return;
            }
            System.out.println("启动连接客户端失败,开始重连");
            final EventLoop loop = future.channel().eventLoop();
            loop.schedule(new Runnable() {
                @Override
                public void run() {
                    try {
                       MyClient05.reConnection();
                       System.out.println("客户端重连成功");
                    } catch (Exception e){
                        e.printStackTrace();
                    }
                }
            }, 1L, TimeUnit.SECONDS);
        }
    }

    client启动 不知道为啥想要测试listener的效果时,connect的sync()不能带,是由于同步阻塞的原因?

    public void start() {
            EventLoopGroup group = new NioEventLoopGroup();
            try {
                bootstrap = getBootstrap();
    
                bootstrap.group(group)
                        .option(ChannelOption.AUTO_READ, true)
                        .option(ChannelOption.TCP_NODELAY, true)
                        .channel(NioSocketChannel.class)
                        .handler(new MyClientChannelInitializer());
    
                // ChannelFuture future = bootstrap.connect(new InetSocketAddress(ip, port)).sync();
                 ChannelFuture future = bootstrap.connect(new InetSocketAddress(ip, port));
                // 增加监听
                future.addListener(new MyChannelFutureListener());
                future.channel().closeFuture().sync();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                group.shutdownGracefully();
            }
        }

    直接启动客户端,可以看到listener输出:启动连接客户端失败,开始重连

  2. channelInactive进行重连(我是直接new了一个线程去重连)
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("客户端断开了连接, time: " + new Date().toLocaleString());
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    new MyClient05("127.0.0.1", 9001).start();
                    System.out.println("客户端重新连接了服务端 time:" + new Date().toLocaleString());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    测试方法:

    1.可以直接通过上面的心跳机制断开连接后,客户端的channelInactive检测到断开会自动执行重连

    测试结果:

    服务端:
    ------
    心跳检测触发了事件, object: , time: [email protected] 16:43:12
    服务端监测到了读取超时
    心跳检测触发了事件, object: , time: [email protected] 16:43:17
    服务端监测到了读取超时
    心跳检测触发了事件, object: , time: [email protected] 16:43:22
    服务端监测到了读取超时
    客户端还在?? 已经3次检测没有访问了,我要断开了哦!!!
    十一月 08, 2019 4:43:22 下午 io.netty.handler.logging.LoggingHandler close
    信息: [id: 0x6bfc0d90, L:/192.168.0.118:9001 - R:/192.168.0.118:51031] CLOSE
    十一月 08, 2019 4:43:22 下午 io.netty.handler.logging.LoggingHandler channelInactive
    信息: [id: 0x6bfc0d90, L:/192.168.0.118:9001 ! R:/192.168.0.118:51031] INACTIVE
    十一月 08, 2019 4:43:22 下午 io.netty.handler.logging.LoggingHandler channelUnregistered
    信息: [id: 0x6bfc0d90, L:/192.168.0.118:9001 ! R:/192.168.0.118:51031] UNREGISTERED
    服务端与客户端断开了连接, time: 2019-11-8 16:43:22
    十一月 08, 2019 4:43:22 下午 io.netty.handler.logging.LoggingHandler channelRegistered
    信息: [id: 0xbcb2ec62, L:/127.0.0.1:9001 - R:/127.0.0.1:51061] REGISTERED
    十一月 08, 2019 4:43:22 下午 io.netty.handler.logging.LoggingHandler channelActive
    信息: [id: 0xbcb2ec62, L:/127.0.0.1:9001 - R:/127.0.0.1:51061] ACTIVE
    myServerHandler is active, time: 2019-11-8 16:43:22
    十一月 08, 2019 4:43:22 下午 io.netty.handler.logging.LoggingHandler write
    信息: [id: 0xbcb2ec62, L:/127.0.0.1:9001 - R:/127.0.0.1:51061] WRITE: 成功连接服务端, 当前时间:2019-11-8 16:43:22
    十一月 08, 2019 4:43:22 下午 io.netty.handler.logging.LoggingHandler flush
    信息: [id: 0xbcb2ec62, L:/127.0.0.1:9001 - R:/127.0.0.1:51061] FLUSH
    十一月 08, 2019 4:43:26 下午 io.netty.handler.logging.LoggingHandler channelReadComplete
    ------------------------------------------------------------------------------------- 
    
    客户端:
    -------
    当前已连接
    客户端与服务端建立了连接 time: 2019-11-8 16:43:07
    客户端接收到了服务的响应的数据 msg: 成功连接服务端, 当前时间:2019-11-8 16:43:07, time: 2019-11-8 16:43:07
    客户端断开了连接, time: 2019-11-8 16:43:22
    当前已连接
    客户端与服务端建立了连接 time: 2019-11-8 16:43:22
    客户端接收到了服务的响应的数据 msg: 成功连接服务端, 当前时间:2019-11-8 16:43:22, time: 2019-11-8 16:43:22

原文地址:https://www.cnblogs.com/hetangyuese/p/11821519.html

时间: 2024-07-31 08:29:31

Netty学习篇④-心跳机制及断线重连的相关文章

浅析 Netty 实现心跳机制与断线重连

基础 何为心跳 顾名思义, 所谓 心跳, 即在 TCP 长连接中, 客户端和服务器之间定期发送的一种特殊的数据包, 通知对方自己还在线, 以确保 TCP 连接的有效性. 为什么需要心跳 因为网络的不可靠性, 有可能在 TCP 保持长连接的过程中, 由于某些突发情况, 例如网线被拔出, 突然掉电等, 会造成服务器和客户端的连接中断. 在这些突发情况下, 如果恰好服务器和客户端之间没有交互的话, 那么它们是不能在短时间内发现对方已经掉线的. 为了解决这个问题, 我们就需要引入 心跳 机制. 心跳机制

netty心跳机制和断线重连(四)

心跳是为了保证客户端和服务端的通信可用.因为各种原因客户端和服务端不能及时响应和接收信息.比如网络断开,停电 或者是客户端/服务端 高负载. 所以每隔一段时间 客户端发送心跳包到客户端  服务端做出心跳的响应: 1.如果客户端在指定时间没有向服务端发送心跳包.则表示客户端的通信出现了问题. 2.如果客户端发送心跳包到服务端没有收到响应 则表示服务端的通信出现了问题. netty提供IdleStateHandle 在监听距离上一次写的时间和距离上一次读的时间 如果超时则调用 源码: public

webSocket使用心跳包实现断线重连

首先new一个webscoket的连接 this.noticeSocketLink = new WebSocket(‘webSocket的地址’) 这里是连接成功之后的操作 linkNoticeWebsocket(){ this.noticeSocketLink.onopen = ()=>{ 在连接成功打开的时候调用断线重连的函数 this.webCloseLink(this.noticeSocketLink) } this.noticeSocketLink.onmessage = res =>

Netty学习篇--整合springboot

经过前面的netty学习,大概了解了netty各个组件的概念和作用,开始自己瞎鼓捣netty和我们常用的项目的整合(很简单的整合) 项目准备 工具:IDEA2017 jar包导入:maven 项目框架:springboot+netty 项目操作 右键创建一个maven项目,项目名称: hetangyuese-netty-03(项目已上传github) 项目完整结构 ? maven导包 <!-- netty start --> <dependency> <groupId>

Netty学习篇--02

Channel.ChannelPipeline.ChannelHandlerContent发送数据的不同 // channel往回写数据 Channel channel = ctx.channel(); channel.writeAndFlush(Unpooled.copiedBuffer(new String("123").toCharArray(), CharsetUtil.UTF_8)); // pipeline写回数据 ChannelPipeline pipeline = ct

基于netty实现的长连接,心跳机制及重连机制

技术:maven3.0.5 + netty4.1.33 + jdk1.8 概述 Netty是由JBOSS提供的一个java开源框架.Netty提供异步的.事件驱动的网络应用程序框架和工具,用以快速开发高性能.高可靠性的网络服务器和客户端程序. 也就是说,Netty 是一个基于NIO的客户.服务器端编程框架,使用Netty 可以确保你快速和简单的开发出一个网络应用,例如实现了某种协议的客户.服务端应用.Netty相当于简化和流线化了网络应用的编程开发过程,例如:基于TCP和UDP的socket服务

NETTY 心跳机制

最近工作比较忙,但闲暇之余还是看了阿里的冯家春(fengjiachun)的github上的开源代码Jupiter,写的RPC框架让我感叹人外有人,废话不多说,下面的代码全部截取自Jupiter,写了一个比较完整的例子,供大家一起学习分享,再次对@Luca抱拳,Jupiter的Github地址: https://github.com/fengjiachun/Jupiter 今天研究的是,心跳和重连,虽然这次是大神写的代码,但是万变不离其宗,我们先回顾一下Netty应用心跳和重连的整个过程: 1)客

golang+sse+angular的心跳机制、angullar的轮询机制、time.Duration和time.NewTicker的学习

长连接断开的原因 连接超时,浏览器自动断开连接 进程被杀死 不可抗拒因素 根据不同情况,高效保活的方式 连接超时:心跳机制 进程保活 断线重连 重点心跳机制 产物 心跳包 心跳应答 轮询与心跳区别 轮询一次相当于:建立一次TCP连接+断开连接 心跳:在已有的连接上进行保活 心跳设计要点 心跳包的规格(内容&大小) 心跳发送间隔时间(按照项目的特性进行判断) 断线重连机制(核心= 如何判断长连接的有效性) 心跳具体实现(基于sse的长连接) 客户端做心跳机制:客户端长时间没有反应,使用心跳机制,证

netty之心跳机制

1.心跳机制,在netty3和netty5上面都有.但是写法有些不一样. 2.心跳机制在服务端和客户端的作用也是不一样的.对于服务端来说:就是定时清除那些因为某种原因在一定时间段内没有做指定操作的客户端连接.对于服务端来说:用来检测是否断开连接,然后尝试重连等问题.游戏上面也可以来监控延时问题. 3.我这边只写了服务端的心跳用法,客户端基本差不多. 1)netty3的写法 import org.jboss.netty.bootstrap.ServerBootstrap; import org.j