2016-04-05 20:45:27.0|分类: netty|浏览量: 3175
客户端和服务器建立了长连接,一个客户端只能建立一个长连接,如果有成千上万个客户端与服务器建立连接,服务器就会保持同数量的长连接。服务器保持大量长连接需要消耗大量资源,同时服务器也要处理消息,服务器端的压力还是很大的。 国内的移动网络有一个特点,不稳定,因为我们不知道什么时候会掉线,还有延迟是非常大的,移动网络的不稳定和高延迟。 如果客户端与服务器建立长连接之后,由于网络原因,客户端与服务器这个长连接断开,但是服务器端没有收到长连接断开的命令,还会认为这个长连接连接活跃,与客户端的channel对象不会自动关闭,还在保持这个链接,造成资源浪费。为了解决这些已经失效的长连接,我们怎么处理呢? 客户端定时向服务器端发送心跳包,固定的心跳,固定3分钟、5分钟或者是10分钟来发送心跳,网络情况是比较复杂的,有时候网络情况是好的,有时候网络情况是差的,选择最短的时间。降低用户的流量问题,通过智能心跳的问题,来降低手机的电量的消耗。 客户端定期向服务器发送心跳,服务器检测连接的读、写空闲时间是否超时。如果连接在持续时间t没有读取、发送任务消息,则认为连接出现异常,将连接进行关闭,释放资源,如图5: Netty提供的空闲检测机制分为三种: 1) 读空闲,链路持续时间t没有读取到任何消息; 2) 写空闲,链路持续时间t没有发送任何消息; 3) 读写空闲,链路持续时间t没有接收或者发送任何消息。 Netty的默认读写空闲机制是发生超时异常,关闭连接,但是,我们可以定制它的超时实现机制,以便支持不同的用户场景 服务端增加对空闲时间处理pipeline.addLast("ping", new IdleStateHandler(MAX_IDLE_TIME, MAX_IDLE_TIME, MAX_IDLE_TIME)) 然后在业务逻辑的Handler里面,重写 userEventTriggered(ChannelHandlerContext ctx, Object evt),如果获取到IdleState.ALL_IDLE则定时向客户端发送心跳包;客户端在业务逻辑的Handler里面,如果接到心跳包,则向服务器发送一个心跳反馈;服务端如果长时间没有接受到客户端的信息,即IdleState.READER_IDLE被触发,则关闭当前的channel。 调用代码是: socketChannel.pipeline().addLast("IdleStateHandler", new IdleStateHandler( MAX_IDLE_TIME, MAX_IDLE_TIME, MAX_IDLE_TIME )); socketChannel.pipeline().addLast("IdlChannelHandlerAdapter", new IdlChannelHandlerAdapter()); /***** * 空闲连接处理逻辑 * ******/ public class IdlChannelHandlerAdapter extends ChannelHandlerAdapter { @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { super.userEventTriggered(ctx, evt); if (evt instanceof IdleStateEvent) { IdleStateEvent event = (IdleStateEvent) evt; if (event.state().equals(IdleState.READER_IDLE)) { System.out.println("READER_IDLE"); // 超时关闭channel ctx.disconnect(); ctx.channel().close(); } else if (event.state().equals(IdleState.WRITER_IDLE)) { System.out.println("WRITER_IDLE"); } else if (event.state().equals(IdleState.ALL_IDLE)) { System.out.println("ALL_IDLE"); // 发送心跳 // ctx.channel().write("ping\n"); } } } } |