本篇博客讲解:
关于netty使用的一些实例
netty-introduction-demo:Netty的入门实例
netty-serialization-demo:netty传输序列化对象
netty-heartbeat-detection-demo:使用netty进行服务器和客户端的心跳检测
netty-not-sticky-pack-demo:netty解决粘包问题,传输序列化对象
挺久没写博客的了。实在是因为时间不够,对大家说声抱歉了。之前临近毕业,学校的事情多得一批,我也很绝望。
最近来到了杭州一家支付公司工作,有时候也得加加班的。所以平时上班肯定是没啥时间写博客的。并且在7月份,已经和一家出版社签好了合同,将会在明年出版我的第一本书。希望大家多多支持。
给大家透露一下书的信息吧,是关于Spring,SpringMVC和MyBatis的。其中前面几个章节会对于反射,动态代理,设计模式等学习框架前的基础知识进行一些比较深入的讲解,同时配合一些实例。中间章节的部分主要是针对SSM框架进行逐步分析了。书本中会涉及到比较多的源码分析。很适合想进一步学习的朋友。后面的章节会讲到Redis,然后最后会进行一个企业级项目的整合实例开发。书写的不好,大家可以尽管拿书砸我(书本可能会有800页以上,所以我也在考虑是不是作为两本书出),也希望在书出版前后得到大家的建议。(广告插入很僵硬,表介意)
虽然自己平时都在学习,但是写博客的时间和学习的时间相比,需要的时间实在要多些,所以种种原因,也就导致了我现在更新博客会比较慢。
现在对于一些基础的知识可能以后会很少见了,更多的会使用实例加注释,开源的项目为大家演示怎么使用框架。以及简单实现框架的功能。
比如说这里的Netty,我会进行一些实例项目的分享,另外,可能会有自己使用NIO实现简单类似Netty的功能。
下一次的博客应该会是讲解搭建RPC框架,以及Java高并发类的使用, SpringBoot集成Shiro的种种实例 (我的GitHub中已经有)。很多实例其实在空闲时间,我也写了很多,但是还不足以用博客的方式分享出来,大家如果想知道我分享的实时动态的话,建议关注我的github,欢迎多来点star。
后面我的博客方式将不再以博客展示的方式为重点。对于很多知识点,我将会在我的github中开源项目中进行展示。实在是因为时间有限,每天每个人只有24小时,不多不少。望大家谅解。希望大家都能进步,学到知识。
Netty是基于NIO的客户端、服务端通讯编程框架
Netty的一些介绍就不多说了。大家可以谷歌百度看官网都行的、
后面我可能会针对netty再上一篇高级应用或者原理分析
Netty系列文章我是为后面我会讲解RPC原理而做的铺垫
接下来就是直接上项目例子了、
构建并运行第一个Netty的客户端和服务器
采用ECHO协议,也就是客户端请求什么,服务器就返回什么。
ChannelOption.SO_BACKLOG:
服务器的TCP内核维护两个队列A和B
客户端向服务端请求connect时, 发送SYN(第一次握手)
服务端收到SYN后, 向客户端发送SYN ACK(第二次握手), TCP内核将连接放入队列A
客户端收到后向服务端发送ACK(第三次握手), TCP内核将连接从A->B, accept返回, 连接完成
A/B队列的长度和即为BACKLOG, 当accept速度跟不上(也就是同时握手过多), A/B队列使得BACKLOG满了, 客户端连接就会被TCP内核拒绝
可以调大SO_BACKLOG缓解这一现象.默认值为50.
一个最简单的入门实例,在代码中都带了大部分注释,请结合注释和项目进行理解。
对象序列化, 目的是为了实现对象的网络传输和本地持久化
如果使用java的序列化, 码流较大. 因此多用FastjsonSerialize, KryoSerialize,FSTSerialize等
在本例中使用FSTSerialize进行编解码传输javabean
使用netty进行服务器和客户端的心跳检测
或者是主服务器和从服务器之间的心跳检测,让主服务器知道从服务器的状态
客户端每隔5-10s给服务器进行发送心跳包
通过netty和定时任务实现
使用Sigar工具获取服务器的信息,cpu,内存等
比较主流的解决方法由如下几种:
1、消息定长,报文大小固定长度,例如每个报文的长度固定为200字节,如果不够空位补空格;
2、包尾添加特殊分隔符,例如每条报文结束都添加回车换行符(例如FTP协议)或者指定特殊字符作为报文分隔符,接收方通过特殊分隔符切分报文区分;
3、将消息分为消息头和消息体,消息头中包含表示信息的总长度(或者消息体长度)的字段;
4、自定义更复杂的应用层协议。
Netty提供了多个解码器,可以进行分包的操作,分别是:
LineBasedFrameDecoder
DelimiterBasedFrameDecoder(添加特殊分隔符报文来分包)
FixedLengthFrameDecoder(使用定长的报文来分包)
LengthFieldBasedFrameDecoder
本实例使用LengthFieldBasedFrameDecoder屏蔽TCP底层的拆包和粘包问题
使用对象进行传输
LengthFieldBasedFrameDecoder的构造函数:
public class LengthFieldBasedFrameDecoder extends ByteToMessageDecoder {
//...
public LengthFieldBasedFrameDecoder(ByteOrder byteOrder,
int maxFrameLength,
int lengthFieldOffset,
int lengthFieldLength,
int lengthAdjustment,
int initialBytesToStrip,
boolean failFast) {
}
//...
}
byteOrder:表示字节流表示的数据是大端还是小端,因为Netty要读取Length字段的值,所以大端小端要设置好,默认Netty是大端序ByteOrder.BIG_ENDIAN。
maxFrameLength:表示的是包的最大长度,超出包的最大长度netty将会报错;
lengthFieldOffset:指的是长度域(Length)的偏移量,表示跳过指定长度个字节之后的才是长度域,也就是length前面的字节,也就是头部信息;
lengthFieldLength:记录该帧数据长度的字段本身的长度;
lengthAdjustment:该字段加长度字段等于数据帧的长度,包体长度调整的大小,长度域的数值表示的长度加上这个修正值表示的就是带header的包;
initialBytesToStrip:从数据帧中跳过的字节数,表示获取完一个完整的数据包之后,忽略前面的指定的位数个字节,应用解码器拿到的就是不带长度域的数据包;
failFast:如果为true,则表示读取到长度域,TA的值的超过maxFrameLength,就抛出一个 TooLongFrameException,而为false表示只有当真正读取完长度域的值表示的字节之后,才会抛出 TooLongFrameException,默认情况下设置为true,建议不要修改,否则可能会造成内存溢出。
对于什么是粘包、拆包问题,我想先举两个简单的应用场景:
客户端和服务器建立一个连接,客户端发送一条消息,客户端关闭与服务端的连接。
客户端和服务器简历一个连接,客户端连续发送两条消息,客户端关闭与服务端的连接。
对于第一种情况,服务端的处理流程可以是这样的:当客户端与服务端的连接建立成功之后,服务端不断读取客户端发送过来的数据,当客户端与服务端连接断开之后,服务端知道已经读完了一条消息,然后进行解码和后续处理…。
对于第二种情况,如果按照上面相同的处理逻辑来处理,那就有问题了
我们来看看第二种情况下客户端发送的两条消息递交到服务端有可能出现的情况:
第一种情况:
服务端一共读到两个数据包,第一个包包含客户端发出的第一条消息的完整信息,第二个包包含客户端发出的第二条消息,那这种情况比较好处理,服务器只需要简单的从网络缓冲区去读就好了,第一次读到第一条消息的完整信息,消费完再从网络缓冲区将第二条完整消息读出来消费。
第二种情况:
服务端一共就读到一个数据包,这个数据包包含客户端发出的两条消息的完整信息,这个时候基于之前逻辑实现的服务端就蒙了,因为服务端不知道第一条消息从哪儿结束和第二条消息从哪儿开始,这种情况其实是发生了TCP粘包。
TCP粘包示意图
第三种情况:
服务端一共收到了两个数据包,第一个数据包只包含了第一条消息的一部分,第一条消息的后半部分和第二条消息都在第二个数据包中,或者是第一个数据包包含了第一条消息的完整信息和第二条消息的一部分信息,第二个数据包包含了第二条消息的剩下部分,这种情况其实是发送了TCP拆,因为发生了一条消息被拆分在两个包里面发送了,同样上面的服务器逻辑对于这种情况是不好处理的。
TCP拆包示意图
本段文字参考链接 https://my.oschina.net/andylucc/blog/625315
GITHUB源码地址: 【点我进行访问】
本文章由[谙忆]编写, 所有权利保留。
欢迎转载,分享是进步的源泉。
转载请注明出处:http://chenhaoxiang.cn
本文源自【谙忆的博客】