Netty通过心跳保持长链接
Netty自带心跳检测功能,IdleStateHandler,客户端在写空闲时主动发起心跳请求,服务器接受到心跳请求后给出一个心跳响应。当客户端在一定时间范围内不能够给出响应则断开链接。
- public class NettyClient {
- public void connect(String remoteServer, int port) throws Exception {
- EventLoopGroup workerGroup = new NioEventLoopGroup();
- try {
- Bootstrap b = new Bootstrap();
- b.group(workerGroup).channel(NioSocketChannel.class).remoteAddress(remoteServer, port)
- .handler(new ChildChannelHandler());
- ChannelFuture f = b.connect();
- System.out.println("Netty time Client connected at port " + port);
- f.channel().closeFuture().sync();
- } finally {
- try {
- TimeUnit.SECONDS.sleep(5);
- try {
- System.out.println("重新链接。。。");
- connect(remoteServer, port);
- } catch (Exception e) {
- e.printStackTrace();
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
- public static class ChildChannelHandler extends ChannelInitializer<SocketChannel> {
- @Override
- protected void initChannel(final SocketChannel ch) throws Exception {
- // -8表示lengthAdjustment,让解码器从0开始截取字节,并且包含消息头
- ch.pipeline().addLast(new RpcEncoder(NettyMessage.class)).addLast(new RpcDecoder(NettyMessage.class))
- .addLast(new IdleStateHandler(120, 10, 0, TimeUnit.SECONDS)).addLast(new HeartBeatReqHandler());
- }
- }
- public static void main(String[] args) {
- try {
- new NettyClient().connect("127.0.0.1", 12000);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
- public class SerializationUtil {
- private static Map<Class<?>, Schema<?>> cachedSchema = new ConcurrentHashMap<Class<?>, Schema<?>>();
- private static Objenesis objenesis = new ObjenesisStd(true);
- private static <T> Schema<T> getSchema(Class<T> clazz) {
- @SuppressWarnings("unchecked")
- Schema<T> schema = (Schema<T>) cachedSchema.get(clazz);
- if (schema == null) {
- schema = RuntimeSchema.getSchema(clazz);
- if (schema != null) {
- cachedSchema.put(clazz, schema);
- }
- }
- return schema;
- }
- /**
- * 序列化
- *
- * @param obj
- * @return
- */
- public static <T> byte[] serializer(T obj) {
- @SuppressWarnings("unchecked")
- Class<T> clazz = (Class<T>) obj.getClass();
- LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
- try {
- Schema<T> schema = getSchema(clazz);
- byte result[] = ProtostuffIOUtil.toByteArray(obj, schema, buffer);
- return result;
- } catch (Exception e) {
- throw new IllegalStateException(e.getMessage(), e);
- } finally {
- buffer.clear();
- }
- }
- /**
- * 反序列化
- *
- * @param data
- * @param clazz
- * @return
- */
- public static <T> T deserializer(byte[] data, Class<T> clazz) {
- try {
- T obj = objenesis.newInstance(clazz);
- Schema<T> schema = getSchema(clazz);
- ProtostuffIOUtil.mergeFrom(data, obj, schema);
- return obj;
- } catch (Exception e) {
- throw new IllegalStateException(e.getMessage(), e);
- }
- }
- }
- @SuppressWarnings("rawtypes")
- public class RpcEncoder extends MessageToByteEncoder {
- private Class<?> genericClass;
- public RpcEncoder(Class<?> genericClass) {
- this.genericClass = genericClass;
- }
- @Override
- public void encode(ChannelHandlerContext ctx, Object in, ByteBuf out) throws Exception {
- if (genericClass.isInstance(in)) {
- System.out.println("发送的请求是:"+in);
- byte[] data = SerializationUtil.serializer(in);
- out.writeInt(data.length);
- out.writeBytes(data);
- }
- }
- }
- public class RpcDecoder extends ByteToMessageDecoder {
- private Class<?> genericClass;
- public RpcDecoder(Class<?> genericClass) {
- this.genericClass = genericClass;
- }
- @Override
- public final void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out)
- throws Exception {
- if (in.readableBytes() < 4) {
- return;
- }
- in.markReaderIndex();
- int dataLength = in.readInt();
- if (dataLength < 0) {
- ctx.close();
- }
- if (in.readableBytes() < dataLength) {
- in.resetReaderIndex();
- }
- byte[] data = new byte[dataLength];
- in.readBytes(data);
- Object obj = SerializationUtil.deserializer(data, genericClass);
- System.out.println("接收到的消息是:"+obj);
- out.add(obj);
- }
- }
- public class HeartBeatReqHandler extends ChannelDuplexHandler {
- /**
- * @see io.netty.channel.ChannelInboundHandlerAdapter#userEventTriggered(io.netty.channel.ChannelHandlerContext,
- * java.lang.Object)
- */
- @Override
- public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
- if (IdleStateEvent.class.isAssignableFrom(evt.getClass())) {
- IdleStateEvent event = (IdleStateEvent) evt;
- if (event.state() == IdleState.READER_IDLE) {
- System.out.println("read 空闲");
- ctx.disconnect();
- } else if (event.state() == IdleState.WRITER_IDLE) {
- System.out.println("write 空闲");
- ctx.writeAndFlush(buildHeartBeat(MessageType.HEARTBEAT_REQ.getType()));
- }
- }
- }
- /**
- *
- * @return
- * @author zhangwei<wei.zw@corp.netease.com>
- */
- private NettyMessage buildHeartBeat(byte type) {
- NettyMessage msg = new NettyMessage();
- Header header = new Header();
- header.setType(type);
- msg.setHeader(header);
- return msg;
- }
- }
- public class NettyServer {
- public void bind(int port) throws Exception {
- EventLoopGroup bossGroup = new NioEventLoopGroup();
- EventLoopGroup workerGroup = new NioEventLoopGroup();
- try {
- ServerBootstrap b = new ServerBootstrap();
- b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, 1024)
- .childHandler(new ChildChannelHandler());
- ChannelFuture f = b.bind(port).sync();
- System.out.println("Netty time Server started at port " + port);
- f.channel().closeFuture().sync();
- } finally {
- bossGroup.shutdownGracefully();
- workerGroup.shutdownGracefully();
- }
- }
- public static class ChildChannelHandler extends ChannelInitializer<SocketChannel> {
- @Override
- protected void initChannel(final SocketChannel ch) throws Exception {
- ch.pipeline().addLast(new RpcDecoder(NettyMessage.class)).addLast(new RpcEncoder(NettyMessage.class))
- .addLast(new IdleStateHandler(120, 0, 0, TimeUnit.SECONDS)).addLast(new HeartBeatRespHandler());
- }
- }
- public static void main(String[] args) {
- try {
- new NettyServer().bind(12000);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
- public enum MessageType {
- LOGIN_REQ((byte) 1), LOGIN_RESP((byte) 2), HEARTBEAT_REQ((byte) 3), HEARTBEAT_RESP((byte) 4);
- private byte type;
- /**
- * @param type
- */
- private MessageType(byte type) {
- this.type = type;
- }
- public byte getType() {
- return type;
- }
- public void setType(byte type) {
- this.type = type;
- }
- public static MessageType getMessageType(byte type) {
- for (MessageType b : MessageType.values()) {
- if (b.getType() == type) {
- return b;
- }
- }
- return null;
- }
- }
- public class HeartBeatRespHandler extends SimpleChannelInboundHandler<NettyMessage> {
- /**
- * @see io.netty.channel.SimpleChannelInboundHandler#channelRead0(io.netty.channel.ChannelHandlerContext,
- * java.lang.Object)
- */
- @Override
- protected void channelRead0(ChannelHandlerContext ctx, NettyMessage msg) throws Exception {
- if (msg.getHeader() != null && msg.getHeader().getType() == MessageType.HEARTBEAT_REQ.getType()) {
- NettyMessage heartBeat = buildHeartBeat(MessageType.HEARTBEAT_RESP.getType());
- ctx.writeAndFlush(heartBeat);
- } else {
- ctx.fireChannelRead(msg);
- }
- }
- /**
- * @see io.netty.channel.ChannelInboundHandlerAdapter#userEventTriggered(io.netty.channel.ChannelHandlerContext,
- * java.lang.Object)
- */
- @Override
- public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
- if (IdleStateEvent.class.isAssignableFrom(evt.getClass())) {
- IdleStateEvent event = (IdleStateEvent) evt;
- if (event.state() == IdleState.READER_IDLE) {
- System.out.println("read 空闲 关闭链接");
- ctx.disconnect();
- }
- }
- }
- /**
- *
- * @return
- * @author zhangwei<wei.zw@corp.netease.com>
- */
- private NettyMessage buildHeartBeat(byte type) {
- NettyMessage msg = new NettyMessage();
- Header header = new Header();
- header.setType(type);
- msg.setHeader(header);
- return msg;
- }
- }
- public class NettyMessage implements Serializable{
- /** */
- private static final long serialVersionUID = 1L;
- private Header header;
- private Object body;
- public Header getHeader() {
- return header;
- }
- public void setHeader(Header header) {
- this.header = header;
- }
- public Object getBody() {
- return body;
- }
- public void setBody(Object body) {
- this.body = body;
- }
- /**
- * @see java.lang.Object#toString()
- */
- @Override
- public String toString() {
- return "NettyMessage [header=" + header + ", body=" + body + "]";
- }
- }
- public class Header implements Serializable{
- /** */
- private static final long serialVersionUID = 1L;
- private int crcCode=0xabef0101;
- private int length;
- private long sessionId;
- private byte type;
- private byte priority;
- private Map<String,Object> attachment=new HashMap<>();
- public int getCrcCode() {
- return crcCode;
- }
- public void setCrcCode(int crcCode) {
- this.crcCode = crcCode;
- }
- public int getLength() {
- return length;
- }
- public void setLength(int length) {
- this.length = length;
- }
- public long getSessionId() {
- return sessionId;
- }
- public void setSessionId(long sessionId) {
- this.sessionId = sessionId;
- }
- public byte getType() {
- return type;
- }
- public void setType(byte type) {
- this.type = type;
- }
- public byte getPriority() {
- return priority;
- }
- public void setPriority(byte priority) {
- this.priority = priority;
- }
- public Map<String, Object> getAttachment() {
- return attachment;
- }
- public void setAttachment(Map<String, Object> attachment) {
- this.attachment = attachment;
- }
- /**
- * @see java.lang.Object#toString()
- */
- @Override
- public String toString() {
- return "Header [crcCode=" + crcCode + ", length=" + length + ", sessionId=" + sessionId + ", type=" + type
- + ", priority=" + priority + ", attachment=" + attachment + "]";
- }
- }
客户端的结果是:
- etty time Client connected at port 12000
- write 空闲
- 发送的请求是:NettyMessage [header=Header [crcCode=-1410399999, length=0, sessionId=0, type=3, priority=0, attachment={}], body=null]
- 接收到的消息是:NettyMessage [header=Header [crcCode=-1410399999, length=0, sessionId=0, type=4, priority=0, attachment={}], body=null]
- write 空闲
- 发送的请求是:NettyMessage [header=Header [crcCode=-1410399999, length=0, sessionId=0, type=3, priority=0, attachment={}], body=null]
- 接收到的消息是:NettyMessage [header=Header [crcCode=-1410399999, length=0, sessionId=0, type=4, priority=0, attachment={}], body=null]
- write 空闲
- 发送的请求是:NettyMessage [header=Header [crcCode=-1410399999, length=0, sessionId=0, type=3, priority=0, attachment={}], body=null]
- 接收到的消息是:NettyMessage [header=Header [crcCode=-1410399999, length=0, sessionId=0, type=4, priority=0, attachment={}], body=null]
- write 空闲
- 发送的请求是:NettyMessage [header=Header [crcCode=-1410399999, length=0, sessionId=0, type=3, priority=0, attachment={}], body=null]
- 接收到的消息是:NettyMessage [header=Header [crcCode=-1410399999, length=0, sessionId=0, type=4, priority=0, attachment={}], body=null]
- write 空闲
- 发送的请求是:NettyMessage [header=Header [crcCode=-1410399999, length=0, sessionId=0, type=3, priority=0, attachment={}], body=null]
- 接收到的消息是:NettyMessage [header=Header [crcCode=-1410399999, length=0, sessionId=0, type=4, priority=0, attachment={}], body=null]
Netty通过心跳保持长链接的更多相关文章
- netty长链接保存方案
架构 client router server zk redis 对于router: 保存客户端和服务器对 redis clientid : serverip & port 对于server ...
- Netty学习(八)-Netty的心跳机制
版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/a953713428/article/details/69378412我们知道在TCP长连接或者Web ...
- 【Netty】利用Netty实现心跳检测和重连机制
一.前言 心跳机制是定时发送一个自定义的结构体(心跳包),让对方知道自己还活着,以确保连接的有效性的机制. 我们用到的很多框架都用到了心跳检测,比如服务注册到 Eureka Server 之后会维 ...
- 纯Socket(BIO)长链接编程的常见的坑和填坑套路
本文章纯属个人经验总结,伪代码也是写文章的时候顺便白板编码的,可能有逻辑问题,请帮忙指正,谢谢. Internet(全球互联网)是无数台机器基于TCP/IP协议族相互通信产生的.TCP/IP协议族分了 ...
- Netty之心跳检测技术(四)
Netty之心跳检测技术(四) 一.简介 "心跳"听起来感觉很牛X的样子,其实只是一种检测端到端连接状态的技术.举个简单的"栗子",现有A.B两端已经互相连接, ...
- Netty实现心跳机制
netty心跳机制示例,使用Netty实现心跳机制,使用netty4,IdleStateHandler 实现.Netty心跳机制,netty心跳检测,netty,心跳 本文假设你已经了解了Netty的 ...
- python+uwsgi导致redis无法长链接引起性能下降问题记录
今天在部署python代码到预生产环境时,web站老是出现redis链接未初始化,无法连接到服务的提示,比对了一下开发环境与测试环境代码,完全一致,然后就是查看各种日志,排查了半天也没有查明是什么原因 ...
- PHP实现新浪长链接转化成短链接API
我们经常收到类似于这样的短信(如下图),发现其中的链接并不是常规的网址链接,而是个短小精悍的短链接,产品中经常需要这样的需求,如果在给用户下发的短信中是一个很长的连接,用户体验肯定很差,因此我们需要实 ...
- 长链接转换成短链接(iOS版本)
首先需要将字符串使用md5加密,添加NSString的md5的类别方法如下 .h文件 #import <CommonCrypto/CommonDigest.h> @interface NS ...
随机推荐
- HDU5726 GCD
Give you a sequence of N(N≤100,000)N(N≤100,000) integers : a1,...,an(0<ai≤1000,000,000)a1,...,an( ...
- Redis主从复制简单介绍
由于本地环境的使用,所以搭建一个本地的Redis集群,本篇讲解Redis主从复制集群的搭建,使用的平台是Windows,搭建的思路和Linux上基本一致! (精读阅读本篇可能花费您15分钟,略读需5分 ...
- IntelliJ IDEA 使用技巧一览表
IntelliJ IDEA使用技巧一览表 在使用 InelliJ IDEA 的过程中,通过查找资料以及一些自己的摸索,发现这个众多 Java 程序员喜欢的 IDE 里有许多值得一提的小窍门,如果能熟练 ...
- Docker分层原理与内部结构
转自:1 : https://www.csdn.net/article/2015-08-21/2825511 2: http://blog.51cto.com/wzlinux/2044797 ...
- 恶补一下DP+背包专题(刷刷水题)L2
开心的金明 题目大意 就是求一定背包容量的最大值 思路 想必大家都知道,一看到这种题目,就会想起01背包 虽然特别简单但是还是讲一下吧 状态设置 由于这题差不多是一个01背包的版子题,那么我们就只需要 ...
- 浏览器的 16ms 渲染帧
标签 归档 关于arttle Land 浏览器的 16ms 渲染帧 DOM JavaScript 异步 性能 重绘 由于现在广泛使用的屏幕都有固定的刷新率(比如最新的一般在 60Hz), 在两次硬件刷 ...
- mdf 与 mdb的对比
下面的内容从网上搜索而来,未经过本人严格验证,仅供参考. 1.问:mdb数据库能否脱离Access运行?即,没有安装Access,可以打开mdb吗? 答:可以,脱离Access运行,可以到微软的同类产 ...
- android的布局-----GridLayout(网格布局)
学习导图 (一)简介 网格布局由GridLayout所代表,在android4.0之后新增加的布局管理器,因此需要android4.0之后的版本中使用,如果在更早的平台使用该布局管理器,则需要导入相应 ...
- UnionFind(PYthon实现)
UnionFind用于解决图的连通性问题,不需要给出具体路径的情况,可用来计算连通分支数 参考链接: https://blog.csdn.net/dm_vincent/article/details/ ...
- 涂色问题(Python)
题目:将一个圆形等分成N个小扇形,将这些扇形标记为1,2,3,-,N.现在使用M种颜色对每个扇形进行涂色,每个扇形涂一种颜色,且相邻的扇形颜色不同,问有多少种不同的涂法?(N≥1,M≥3) 参考:ht ...