最近开始学习网络编程这一块,特此总结
直接内存为什么比堆内内存要快?
JVM在发送堆内数据给远程时,首先会把这部分数据复制到堆外的一块内存空间(防止GC过程中文件引用地址发生变化带来的问题),然后再发送给远程应用。而直接内存省去了这个复制步骤,好处就是更快速并且减少了GC
直接内存的缺点就是难以控制,发生内存泄漏时难以排查。比较适合存简单对象扁平化
Linux常见的零拷贝有哪些?
mmap内存映射
直接将文件从硬盘拷贝到用户空间,不再有文件内容从硬盘拷贝到内核空间的缓冲区
扩展:Kafka中有一个Broker用来管理消息,Producer需要把消息发送给Broker,Broker收到消息以后肯定是要做持久化的。Kafka号称单机百万级吞吐量,为什么持久化速度这么快呢?就是使用了磁盘顺序写和mmap的零拷贝技术
java.nio.MappedByteBuffer
这个类就是mmap在 Java中应用
sendfile
Linux2.1开始支持
DMA模块直接将数据从内核缓冲区传递给协议引擎,注意需要硬件设备支持
DMA:Direct Memory Access,直接内存存取
扩展:同样是Kafka的Broker,Consumer订阅消息时,Broker把消息的数据从磁盘发送到网卡的发送缓冲区这个过程就使用了sendfile技术
FileChannel.transferTo
和FileChannel.transferFrom
就是在Java中的应用
splice
Linux2.6.17开始支持
相比sendfile不需要硬件支持
splice直接将两个内核空间的buffer进行pipe(sendfile在DMA硬件不支持的情况下,这里需要一次CPU拷贝)
Netty的零拷贝实现
网络通信
Netty的发送和接收ByteBuffer使用直接内存,减少了堆内存到直接内存的拷贝
缓存操作
Netty提供了CompositeByteBuf类,将多个ByteBuf合并成一个逻辑上的ByteBuf,防止它们之间的互相拷贝
文件传输
Netty通过FileRegion包装的FileChannel.transferTo实现文件传输,直接将文件缓冲区的数据发送到目标Channel
1、阻塞IO(blocking IO),BIO
2、非阻塞IO(nonblocking IO)
3、IO复用(select、epoll和poll,IO multiplexing),NIO
4、信号驱动IO(Single Driven IO,SIGIO)
5、异步IO(asynchronous IO)
常见的就是BIO和NIO,严格意义上前四种都是阻塞的,过程中间都存在阻塞,只不过阻塞时可以做其他的事情
通常都会说同步阻塞和异步非阻塞,但是并不代表同步非阻塞和异步阻塞不能用
同步非阻塞:获取资源不存在,线程阻塞了,线程去做其他事,线程会频繁的查询资源是否存在,消耗很多CPU
异步阻塞:Java中创建Future,然后马上调用get方法获取资源,没有多大的意义
总给:同步非阻塞和异步阻塞这两种方式太傻,所以不会有人去使用
接下来主要学习的是NIO
NIO的优势:可以用很少的线程服务很多的用户
select、poll 和 epoll 模型的区别?
// fd数组存储,有连接上限
int select (int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
// fd链表存储,无连接上限
int poll (struct pollfd *fds, unsigned int nfds, int timeout);
// 创建EventPoll对象
int epoll_create(int size);
// 增加或删除监听事件
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
// 阻塞直到有事件发生
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
select | poll | epoll | |
---|---|---|---|
支持一个进程能打开的最大连接数 | FD_SETSIZE定义,32位机器是32*32 大小,64位机器是32*64 大小。可以修改但不建议 |
本质上和select相同,没有最大连接数限制,链表存储 | 连接数受限机器内存大小 |
FD剧增带来的IO效率问题 | 对连接线性遍历,FD增加后效率线性下降 | 同select | 根据fd的callback函数实现,活跃的socket才会调用callback。活跃socket多的情况下也有性能问题 |
消息传递方式 | 内核将消息传递到用户空间,经过拷贝 | 内核将消息传递到用户空间,经过拷贝 | 内核和用户空间共享一块内存 |
总结:
使用时根据场合以及这三种方式各自的特点
1、表面上epoll效率最高,但是在连接数少并且都很活跃的情况下,select和poll的性能更高,epoll通知机制需要很多回调
2、select低效是因为轮询,但是低效也是相对的,也可以通过良好的设计改善
Netty的优势
1、API使用简单,开发门槛低;
2、功能强大,预置了多种编解码功能,支持多种主流协议;
3、定制能力强,可以通过ChannelHandler对通信框架进行灵活地扩展;
4、性能高,通过与其他业界主流的NIO框架对比,Netty的综合性能最优;
5、成熟、稳定,Netty修复了已经发现的所有JDK NIO BUG,业务开发人员不需要再为NIO的BUG而烦 恼;
6、社区活跃,版本迭代周期短,发现的BUG可以被及时修复,同时,更多的新功能会加入;
7、经历了大规模的商业应用考验,质量得到验证
为什么不用Netty5
已经停止开发了,其底层的AIO模型,参考了NIO的epoll模型,性能并没有提高很多,而且还会带来很多的异步回调问题。
而且操作系统对AIO的支持不够成熟,处理回调结果速度跟不上处理需求
作者原话: (为什么使用NIO为不是AIO)
Not faster than NIO (epoll) on unix systems (which is true)
There is no daragram suppport
Unnecessary threading model (too much abstraction without usage)
为什么不用Mina
Mina几乎不更新了。Mina也是Netty的作者做出来的,后来作者继续回去做Netty了。