netty4心跳处理 (包括自己写的nio框架跟nginx)

  任何有关TCL、UDP的话题,都逃不过心跳包处理的命。

  比如nginx或者自己写的nio框架都需要处理。

  笔者就曾经自己写过基于nio的框架,心跳是这样处理的:服务端会启动一个特定的线程处理所有合法登陆的用户对象,并且指定时间内扫描客户端对象(向每一个客户端发送心跳包,客户端收到之后需要回复一个心跳),如果在指定时间内客户端没有返回任何数据,服务端会认为该客户端已经死掉了,然后踢掉它。

  nginx的心跳处理呢?

  下面我们来看看一下示例:

  upstream name {
                server 10.1.1.110:8080 max_fails=1 fail_timeout=10s;
                server 10.1.1.122:8080 max_fails=1 fail_timeout=10s;

   }

  fail_timeout表示判断失去链接的时间,max_fails表示失去链接多少次后归为链接无效(即:timeout),简单来说上面配置的意思是,如果10秒钟内还是连不上则统计失效链接数+1,因为max_fails配置是1,所以失效一次就任务心跳失败。

  

  现在重点来了,netty又是怎么处理的呢?

  netty使用的是IdleStateHandler,配置参考如下:

//TODO 心跳包
//服务端如果长期没有收到客户端信息,就给客户端发送心跳包”ok”保持连接;如果服务器未收到客户端的反馈数据就主动断开客户端连接
//这个就表示 如果60秒未收到客户端信息 ,服务端就主动断掉客户端; 如果15秒没有信息,服务器就向客户端 发送心跳信息
//第一个参数   表示读操作空闲时间
//第二个参数   表示写操作空闲时间
//第三个参数   表示读写操作空闲时间
//第四个参数 单位
ch.pipeline().addLast("ping", new IdleStateHandler(60, 20, 15, TimeUnit.SECONDS));

注释写得很清楚,这里我就不再解释了,对应的Handler需要重写userEventTriggered方法,这里我用服务端配置作为示例:

NioSocketServerHandler server=new NioSocketServerHandler();
pipeline.addLast(server);

NioSocketServerHandler的userEventTriggered:

@Override
	public void userEventTriggered(ChannelHandlerContext ctx, Object evt)
			throws Exception {
    	//TODO 重写心跳包
		if (evt instanceof IdleStateEvent) {
			IdleStateEvent event = (IdleStateEvent) evt;
			if (event.state().equals(IdleState.READER_IDLE)) {
				log.info("READER_IDLE");
				// 超时关闭channel
				ctx.close();
			} else if (event.state().equals(IdleState.WRITER_IDLE)) {
				log.info("WRITER_IDLE");
			} else if (event.state().equals(IdleState.ALL_IDLE)) {
				log.info("ALL_IDLE");
				// 发送心跳
				Message msg=new Message("ok",ModuleID.HEART_BEAT,(byte)0xAF);
				ctx.channel().writeAndFlush(msg);
			}
		}
		super.userEventTriggered(ctx, evt); 
	}

15秒之内如果还没有收到客户端的任何信息,就发送一个心跳给客户端,然后客户端的处理是:

@Override
    public void channelRead(ChannelHandlerContext ctx, Object ob)
            throws Exception {
        logger.info("client read msg:{}, ", JSON.toJSONString(ob));
        
        //TODO to do something...
        if(ob instanceof Message){
        	Message msg=(Message)ob;
        	// 收到心跳包
        	if(msg.getType()==(byte) 0xAF){
        		// 回复服务端一个心跳
				Message rs=new Message("ok",ModuleID.HEART_BEAT,(byte)0xAF);
				ctx.channel().writeAndFlush(rs);
        	}
        }
    }

对应的控制台信息 ->

服务端:

11:19:21.505 [nioEventLoopGroup-3-1] INFO  c.k.s.n.s.NioSocketServerHandler - ALL_IDLE
11:19:21.507 [nioEventLoopGroup-3-1] INFO  c.k.s.n.s.NioSocketServerHandler - server read msg:{"cmdId":2,"data":"ok","type":-81,"zip":0}
11:19:36.509 [nioEventLoopGroup-3-1] INFO  c.k.s.n.s.NioSocketServerHandler - ALL_IDLE
11:19:36.511 [nioEventLoopGroup-3-1] INFO  c.k.s.n.s.NioSocketServerHandler - server read msg:{"cmdId":2,"data":"ok","type":-81,"zip":0}
11:19:51.513 [nioEventLoopGroup-3-1] INFO  c.k.s.n.s.NioSocketServerHandler - ALL_IDLE
11:19:51.515 [nioEventLoopGroup-3-1] INFO  c.k.s.n.s.NioSocketServerHandler - server read msg:{"cmdId":2,"data":"ok","type":-81,"zip":0}

客户端:

11:21:51.549 [nioEventLoopGroup-2-1] INFO  test.nio.NioSocketClientHandler - client read msg:{"cmdId":2,"data":"ok","type":-81,"zip":0}, 
11:22:06.553 [nioEventLoopGroup-2-1] INFO  test.nio.NioSocketClientHandler - client read msg:{"cmdId":2,"data":"ok","type":-81,"zip":0}, 
11:22:21.556 [nioEventLoopGroup-2-1] INFO  test.nio.NioSocketClientHandler - client read msg:{"cmdId":2,"data":"ok","type":-81,"zip":0}, 
11:22:36.560 [nioEventLoopGroup-2-1] INFO  test.nio.NioSocketClientHandler - client read msg:{"cmdId":2,"data":"ok","type":-81,"zip":0}, 
11:22:51.563 [nioEventLoopGroup-2-1] INFO  test.nio.NioSocketClientHandler - client read msg:{"cmdId":2,"data":"ok","type":-81,"zip":0}, 
11:23:06.567 [nioEventLoopGroup-2-1] INFO  test.nio.NioSocketClientHandler - client read msg:{"cmdId":2,"data":"ok","type":-81,"zip":0}, 
11:23:21.571 [nioEventLoopGroup-2-1] INFO  test.nio.NioSocketClientHandler - client read msg:{"cmdId":2,"data":"ok","type":-81,"zip":0}, 


好了,说到这里基本上完了,网站很多帖子跟论坛上基本到这里就结束了,然而这里还有一个值得思考的地方:

这个配置主动发送的配置是放在服务端好呢(上面示例)?

还是放在客户端好呢?

放在服务端的好处是,失效的链接可以及时剔除,释放资源,当然对应的代价是需要浪费一点点资源(毕竟主动推送给所有客户端),如果客户端链接数量小倒是没关系,但是如果很大的话,就不能不慎重对待做调优了。

放在客户端的好处是,服务端就省去了这部分扫描浪费的资源,坏处是服务端无法自动感知失效的客户端对象,导致客户端资源一直占用。

关于笔者的建议,个人认为可以服务端跟客户端结合,客户端主动发送心跳,然后服务端启动一个独立的线程统计客户端发来的心跳,并且服务端设置判断失去链接的时间。



你可能感兴趣的:(netty4心跳处理 (包括自己写的nio框架跟nginx))