Java implements heartbeat monitoring

First of all, let me tell you that there must be more than one way to realize heartbeat monitoring. Before doing it, the requirement given by the leader was to use netty to implement it. After watching it for more than a day, a small demo was completed with netty, but it was discovered when the server was connected. socket io. So I changed to the implementation of socket io.
There must be other implementations, but because I haven't covered it, I won't talk about it for the time being, let's start with netty.

netty

The first step: guide package

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

Regarding this netty package, many people on the Internet have said that netty is backward compatible. But because I haven't actually encountered it, I can't give too good advice. I can only say that if you encounter problems, you can think about it in this direction.
Code. In fact, this involves very esoteric things, such as encoding and decoding! But because what I have to do is relatively simple, I haven't looked at this aspect too carefully. As long as I implement simple heartbeat monitoring here, it will be ok!
Mainly implement the SimpleChannelInboundHandler<String> class. It should be noted that different versions of netty have slightly different method names, such as messageReceive, and some versions are called clientRead0. This is just random.

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<String> {
 
      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();
                }
        });
    }
}

This completes the configuration. When testing, just create a new ClientStarter object and call the start method.

ClientStarter starter = new ClientStarter(new Bootstrap());
starter.connect();

Then when testing, build a server locally and test ok, but there is a problem with the joint debugging with the server:

This is because the server kicks the client offline after the connection is successful. Then repeat the process. This is a server setup issue.

socket io

This can be regarded as a kind of encapsulation on netty. Anyway, it is simpler and more convenient to use than netty. It is mainly used by our server, so I changed it to this form.
The first step is to guide the package

<!-- netty socketio -->
        <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>

Let me talk about it here, the following is the dependency that the client needs to import, and the above. . . Should the server be imported? Anyway, I looked at the project code of our server, and both dependencies were imported.
Let me go directly to my local code, it's quite simple:

    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();
        }
    }

Here Socket.EVENT_CONNECT, Socket.EVENT_CONNECTING, etc. are given constants. If you don't understand, you can look at the definition


 

Guess you like

Origin blog.csdn.net/m0_69824302/article/details/130544738