【转】java实现心跳监控(Netty)

先说一下实现心跳监控肯定不止一种方法,在做之前领导给的要求是用netty实现,看了一天多,用netty也算完成了一个小demo,但是对接的时候才发现服务端用的是socket io。所以我又改成了socket io 的实现方式。
肯定也还有别的实现,但是因为我没涉及所以暂时不多讲,从netty说起吧。

netty
第一步:导包

    <dependency>
        <groupId>io.netty</groupId>
        <artifactId>netty-all</artifactId>
        <version>5.0.0.Alpha2</version
    </dependency>

关于这个netty的包,网上很多人都说了netty的向下兼容问题。不过因为我没实际遇到,所以不能给太好的建议,只能说如果遇到问题了可以往这个方向考虑考虑。
代码实现。其实这个涉及到很深奥的东西,比如什么编码解码之类的!但是因为我要做的比较简单,所以也没太仔细看这方面的东西。我这里只要实现简单的心跳监控就ok了!
主要是实现SimpleChannelInboundHandler这个类。需要注意的是不同版本的netty,这个类的方法名略有不同,比如messageReceive,有的版本就叫做clientRead0。这个只能随机应变了呗。

package com.dsyl.done.netty;

import java.io.IOException;
import java.util.Date;
import java.util.concurrent.TimeUnit;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.EventLoop;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.ReferenceCountUtil;

public class HeartBeatClientHandler extends SimpleChannelInboundHandler {

  private ClientStarter clientStarter;

  public HeartBeatClientHandler(ClientStarter clientStarter) {
    this.clientStarter = clientStarter;
  }

  /**
   * 客户端监听写事件。也就是设置时间内没有与服务端交互则发送ping 给服务端
   */
  @Override
  public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
    if(evt instanceof IdleStateEvent) {
      IdleState state = ((IdleStateEvent)evt).state();
      if(state == IdleState.ALL_IDLE) {
        ctx.writeAndFlush("PING");
        System.out.println("send PING");
      }
    }
    super.userEventTriggered(ctx, evt);
  }
  /**
   * channelInactive 被触发一定是和服务器断开了。分两种情况。一种是服务端close,一种是客户端close。
   */
  @Override
  public void channelInactive(ChannelHandlerContext ctx) throws Exception {
    super.channelInactive(ctx);
    System.err.println("客户端与服务端断开连接,断开的时间为:"+(new Date()).toString());
    // 定时线程 断线重连
    final EventLoop eventLoop = ctx.channel().eventLoop();
    //设置断开连接后重连时间,此设置是断开连接一分钟(60秒)后重连
    eventLoop.schedule(() -> clientStarter.connect(), 60, TimeUnit.SECONDS);
  }

  /**
   * 在服务器端不使用心跳检测的情况下,如果客户端突然拔掉网线断网(注意这里不是客户度程序关闭,而仅是异常断网)
   */
  @Override
  public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
    if(cause instanceof IOException) {
      System.out.println("server "+ctx.channel().remoteAddress()+"关闭连接");
    }
  }

/**
 * 消息监控,监听服务端传来的消息(和netty版本有关,有的版本这个方法叫做clientRead0)
 */
  @Override
protected void messageReceived(ChannelHandlerContext ctx, String msg) throws Exception {
    if (msg.equals("PONG")) {
          System.out.println("receive form server PONG");
        }
        ReferenceCountUtil.release(msg);
     
}
}

package com.dsyl.done.netty;

import java.net.InetSocketAddress;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.timeout.IdleStateHandler;

public class ClientStarter {

private Bootstrap bootstrap;
private int times = 0;

public ClientStarter(Bootstrap bootstrap) {
    this.bootstrap = bootstrap;
    ClientStarter clientStarter = this;
    bootstrap.group(new NioEventLoopGroup())
            .channel(NioSocketChannel.class)
            .handler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel ch) throws Exception {
                    ch.pipeline().addLast(new StringEncoder());
                    ch.pipeline().addLast(new StringDecoder());
                    //发送消息频率。单位秒。此设置是60秒发送一次消息
                    ch.pipeline().addLast(new IdleStateHandler(60, 60, 60));
                    ch.pipeline().addLast(new HeartBeatClientHandler(clientStarter));
                }
            });
}

public void connect() {
    ChannelFuture channelFuture = bootstrap.connect(new InetSocketAddress("ip", 端口));
    channelFuture.addListener(future ->{
            if (future.isSuccess()) {
                System.out.println("connect to server success");
            } else {
                System.out.println("connect to server failed,try times:" + ++times);
                connect();
            }
    });
}

}
就此配置完成。测试的时候new个ClientStarter对象并调用start方法就可以了。

ClientStarter starter = new ClientStarter(new Bootstrap());
starter.connect();
然后测试的时候,在本地建个服务端就测试ok,但是真的和服务端联调出现了一个问题:

image.png

这个是因为 连接成功后服务端就把客户端踹下线了。然后重复这个过程。这个是服务端设置的问题。

socket io
这个算是一种在netty上面的封装吧,反正用起来比netty还要简单方便,主要是我们服务端用的这个,我也就改成了这种形式。
第一步,导包

    <dependency>
        <groupId>com.corundumstudio.socketio</groupId>
        <artifactId>netty-socketio</artifactId>
        <version>1.7.13</version>
    </dependency>
    <dependency>
        <groupId>io.socket</groupId>
        <artifactId>socket.io-client</artifactId>
        <version>1.0.0</version>
    </dependency>

这里说一下,下面这个是客户端需要导入的依赖,上面这个。。。服务端要导入的吧?反正我看了下我们服务端的项目代码,这两个依赖都导入了。
我直接上我本地代码吧,挺简单的:

public void connectServer() {
    String url = "http://" + serverListenIp;
    try {
        IO.Options options = new IO.Options();
        options.transports = new String[] { "websocket" };
        options.reconnectionAttempts = 1000000000;
        options.reconnectionDelay = 60000;// 失败重连的时间间隔
        options.timeout = 10000;// 连接超时时间(ms)
        // par1 是任意参数
        final Socket socket = IO.socket(url + "?userType=0", options);
        socket.on(Socket.EVENT_CONNECT, objects -> {
            System.out.println("client: 连接成功");
            System.out.println("拉取缓存的数据信息!");
            //做你想做的操作
        });
        socket.on(Socket.EVENT_CONNECTING, objects -> System.out.println("client: " + "连接中"));
        socket.on(Socket.EVENT_CONNECT_TIMEOUT, objects -> System.out.println("client: " + "连接超时"));
        socket.on(Socket.EVENT_CONNECT_ERROR, objects -> System.out.println("client: " + "连接失败"));
        socket.connect();
    } catch (Exception ex) {
        ex.printStackTrace();
    }
}

这里Socket.EVENT_CONNECT,Socket.EVENT_CONNECTING等都是给定常量。不懂意思的可以看一看定义

然后因为我们这的业务需求,是在断开连接重连后获取缓存数据,所以这里调用了一个获取缓存数据并处理的方法,你可以根据你的具体需求来写。是连接的时候做什么操作,断开的时候做什么操作之类的,可以很灵活。
然后这的serverListenIp是监听的ip,我用配置文件的形式来获取,以后改起来也方便。大概一个简单的socket io 的客户端监听就到这里了。

作者:唯有努力不欺人丶
链接:https://www.jianshu.com/p/8271507ca2e1
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以使用Netty框架来实现心跳功能。Netty是一个高性能、异步事件驱动的网络编程框架,非常适合用于构建可靠的、高性能的网络应用程序。 下面是一个简单的示例代码,演示了如何使用Netty实现心跳功能: ```java import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.logging.LogLevel; import io.netty.handler.logging.LoggingHandler; import io.netty.handler.timeout.IdleStateHandler; import java.util.concurrent.TimeUnit; public class HeartbeatClient { private final String host; private final int port; public HeartbeatClient(String host, int port) { this.host = host; this.port = port; } public void start() throws Exception { EventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap bootstrap = new Bootstrap(); bootstrap.group(group) .channel(NioSocketChannel.class) .handler(new LoggingHandler(LogLevel.INFO)) .option(ChannelOption.SO_KEEPALIVE, true) .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000) .remoteAddress(host, port); bootstrap.handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new IdleStateHandler(0, 4, 0, TimeUnit.SECONDS)); ch.pipeline().addLast(new HeartbeatClientHandler()); } }); ChannelFuture future = bootstrap.connect().sync(); future.channel().closeFuture().sync(); } finally { group.shutdownGracefully().sync(); } } public static void main(String[] args) throws Exception { String host = "localhost"; int port = 8888; HeartbeatClient client = new HeartbeatClient(host, port); client.start(); } } ``` 在上述代码中,首先创建了一个`NioEventLoopGroup`用于处理客户端的I/O操作。然后,创建了一个`Bootstrap`对象用于配置客户端的启动参数,包括连接的地址和端口、日志处理器以及心跳处理器。其中,心跳处理器使用了Netty提供的`IdleStateHandler`来实现心跳检测,通过设置心跳间隔时间来判断连接是否断开。最后,调用`connect()`方法连接到服务器,并使用`sync()`方法阻塞等待连接完成。 需要注意的是,上述示例中的`HeartbeatClientHandler`是自定义的一个处理器类,用于处理心跳相关的逻辑,你可以根据实际需求自行实现。 以上就是使用Netty框架实现心跳功能的一个简单示例。希望对你有帮助!如果还有其他问题,请随时提问。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值