netty實現心跳監控


首先說一下業務場景:

不同於netty常用的im,我這里只是單純的實現服務端與客戶端做一個心跳檢測,查看客戶端是否在線即可。因為是領導指定用netty,所以簡單的看了下demo,又因為業務需求的簡單,所以也只是淺顯的了解了一下,還有一點:正常來講客戶端和服務端監聽都可以。但是我們這是領導覺得少占用服務端資源,所以選擇了客戶端監聽。

1.導包。(雖然我沒用過,但是網上很多人都說了netty的向下兼容問題。所以有遇到的可以考慮一下這個問題)

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

2.代碼實現。其實這個涉及到很深奧的東西,比如什么編碼解碼之類的!但是因為我要做的比較簡單,所以也沒太仔細看這方面的東西。然后再重申!我這里只要實現簡單的心跳監控就ok了!

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

  這個是客戶端實現的代碼(ip和端口自己按照情況填寫)。測試 的時候以下兩行代碼:

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

使用的時候服務端還需要一些額外的設置,不然可能發生下圖這種情況:

 

 連接成功后服務端就把客戶端踹下線了。然后重復這個過程。這個是服務端設置的問題。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM