流水不争先,争的是滔滔不绝

云聊IM如何实现心跳机制

IM源码 macgrady 189℃

1、为何需要心跳机制?

IM即时通讯中,有时需要记录客户端状态,以确保用户正常使用。在某些长连接场景下,客户端和服务端并不是一直处于通信状态,,这时引入心跳机制就可以方便我们实现

1、服务端检测到某个客户端规定时间段无心跳包发送可以主动断开连接,使其下线。

2、客户端检测到某个服务端迟迟没有响应心跳也能重连获取一个新的连接。

所谓心跳, 即在 TCP 长连接中, 客户端和服务器之间定期发送的一种特殊的数据包, 通知对方自己在线, 以确保 TCP 连接的有效性。云聊IM应用中心跳机制是其网络通信技术底层中非常重要的一环,从而使其实时效率、断线重连机制、以及弱网必达等等都有不俗表现。

2、如何用Netty实现心跳机制?

核心Handler:IdleStateHandler


在 Netty 中, 实现心跳机制的关键是 IdleStateHandler, 那么这个 Handler 如何使用呢?

先看下它的构造器:

1 2 3public IdleStateHandler(int readerIdleTimeSeconds, int writerIdleTimeSeconds, int allIdleTimeSeconds) {     this((long)readerIdleTimeSeconds, (long)writerIdleTimeSeconds, (long)allIdleTimeSeconds, TimeUnit.SECONDS); }


这里解释下三个参数的含义:

  • readerIdleTimeSeconds: 读超时. 即当在指定的时间间隔内没有从 Channel 读取到数据时, 会触发一个 READER_IDLE 的 IdleStateEvent 事件.
  • writerIdleTimeSeconds: 写超时. 即当在指定的时间间隔内没有数据写入到 Channel 时, 会触发一个 WRITER_IDLE 的 IdleStateEvent 事件.
  • allIdleTimeSeconds: 读/写超时. 即当在指定的时间间隔内没有读或写操作时, 会触发一个 ALL_IDLE 的 IdleStateEvent 事件.
  • 注:这三个参数默认的时间单位是秒。若需要指定其他时间单位,可以使用另一个构造方法:IdleStateHandler(boolean observeOutput, long readerIdleTime, long writerIdleTime, long allIdleTime, TimeUnit unit)
  • 下面将直接上代码,需要注意的地方,会在代码中通过注释进行说明。

使用IdleStateHandler实现心跳(客户端篇)

下面将使用IdleStateHandler来实现心跳,Client端连接到Server端后,会循环执行一个任务:随机等待几秒,然后ping一下Server端,即发送一个心跳包。
当等待的时间超过规定时间,将会发送失败,以为Server端在此之前已经主动断开连接了。
实现代码如下。
ClientIdleStateTrigger —— 心跳触发器:
类ClientIdleStateTrigger也是一个Handler,只是重写了userEventTriggered方法,用于捕获IdleState.WRITER_IDLE事件(未在指定时间内向服务器发送数据),然后向Server端发送一个心跳包

/**  *
   用于捕获{@link IdleState#WRITER_IDLE}事件(未在指定时间内向服务器发送数据),然后向Server端发送一个心跳包。  
 */ public class ClientIdleStateTrigger extends ChannelInboundHandlerAdapter {  

        IdleState state = ((IdleStateEvent) evt).state();         

 public static final String HEART_BEAT = “heart beat!”;     

  @Override   

 public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {

   if (evt instanceof IdleStateEvent) {     

    if (state == IdleState.WRITER_IDLE) {           

      // write heartbeat to server       

          ctx.writeAndFlush(HEART_BEAT);             }      

   }

else {             super.userEventTriggered(ctx, evt);         }     }   }

Pinger —— 心跳发射器:

/**  * <p>客户端连接到服务器端后,会循环执行一个任务:随机等待几秒,然后ping一下Server端,即发送一个心跳包。

public class Pinger extends ChannelInboundHandlerAdapter {

    private Random random = new Random();

    private int baseRandom = 8;

    private Channel channel;

    @Override

    public void channelActive(ChannelHandlerContext ctx) throws Exception {

        super.channelActive(ctx);

        this.channel = ctx.channel();

        ping(ctx.channel());

    }

    private void ping(Channel channel) {

        int second = Math.max(1, random.nextInt(baseRandom));

        System.out.println("next heart beat will send after " + second + "s.");

        ScheduledFuture<?> future = channel.eventLoop().schedule(new Runnable() {

            @Override

            public void run() {

                if (channel.isActive()) {

                    System.out.println("sending heart beat to the server...");

                    channel.writeAndFlush(ClientIdleStateTrigger.HEART_BEAT);

                } else {

                    System.err.println("The connection had broken, cancel the task that will send a heart beat.");

                    channel.closeFuture();

                    throw new RuntimeException();

                }

            }

        }, second, TimeUnit.SECONDS);

        future.addListener(new GenericFutureListener() {

            @Override

            public void operationComplete(Future future) throws Exception {

                if (future.isSuccess()) {

                    ping(channel);

                }

            }

        });

    }

    @Override

    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {

        // 当Channel已经断开的情况下, 仍然发送数据, 会抛异常, 该方法会被调用.

        cause.printStackTrace();

        ctx.close();

    }

本文来自作者sprinkle_liz的原创技术分享,原文链接:jianshu.com/p/1a28e48edd92,感谢作者的无私。

版权声明:部分文章、图片等内容为用户发布或互联网整理而来,仅供学习参考。如有侵犯您的版权,请联系我们,将立刻删除。
点击这里给我发消息