网络系统
零拷贝
DMA技术
直接内存访问(DMA):在进行io设备和内存数据传输时,数据搬运的工作全部交给DMA控制器,而cpu不再参与任何与数据搬运相关的事情。
目的就是为了减少cpu搬运数据次数。在原来的基础上,只需cpu向DMA发送指令即可。
传统文件传输与优化方式
其中的弊端是上下文切换次数过多且拷贝次数也过多。
优化的方向是:1.减少切换次数,可以通过减少系统调用次数来实现。2.减少拷贝次数,去掉用户的缓冲区,因为用户空间不对数据再加工。
如何实现零拷贝
基于pagecache实现的,实现方式有两种:1.mmap + write 2.sendfile
- mmap + write: 用mmap代替read可以减少一次拷贝,mmap直接将内核数据映射到用户空间中。
- sendfile : 可以代替read和write,减少两次切换,即一次系统调用。在有SG-DMA技术下,可直接将内核缓冲区拷贝到
网卡中,减少一次拷贝。
总而言之,就是在没有内核层面参与拷贝数据的情况下,全程由dma完成数据拷贝。这样可以提高传输性能一倍以上。
大文件如何传输
对于小文件,可以使用pagecache传输,基于内存,读取也快,但大文件也如此的话会造成性能下降。
因此在高并发的场景下,针对大文件传输,应该使用异步io+直接io的方式代替零拷贝技术。
I/O多路复用
时分多路复用,进程可以通过一个系统调用函数从内核中获取多个事件
select/poll
都是使用线性结构存储进程关注的socket集合,因此都需要遍历文件描述集合来找到可读或者可写的socket,时间复杂度为O(n),
而且也需要在用户态和内核态之间拷贝文件描述符集合。
- 存在问题:客户端越多,集合越大,遍历的开销也就越大。
epoll
- 在内核里使用红黑树,高效检测待检测的socket,减少了内核和用户空间大量的数据拷贝和内存分配。
- 内核里维护了一个链表,只将有事件发生的socket集合传递给应用程序。
- 支持边缘触发和水平触发的方式。边缘触发效率高。
高性能网络模式:Reactor 和 Proactor
什么场景下提出了这种网络模式:
- 让服务器服务多个客户端,直接每一条创建线程不太实际,因此引入线程池,一个线程要处理多个业务,但遇到无数据可读时
会发生阻塞,解决办法是改成非阻塞,不断轮询,问题也很明显,cpu压力大,所以才有了IO多路复用。 - 这种高性能模式就是对IO多路复用的包装。
Reactor
IO多路复用监听事件,收到事件后,根据事件类型分配给某个进程/线程。是非阻塞同步网络模式
这种模式主要由Reactor 和处理资源池这两个核心部分组成:
- reactor 负责监听分发事件
- 处理资源池负责处理事件
这种模式也是灵活多变的,主要因素是:
- reactor数量可一个也可多个
- 处理资源池可一个,也可多个
这些因素排列组合有:
- 单reactor单进线程:
- 单reactor多进线程:
- 多reactor多进线程:
Proactor
是异步网络模式
对比Reactor:
- Reactor 是非阻塞同步网络模式,感知的是就绪读写事件。
- Proactor 是异步网络模式,感知的是已完成的读写事件。
一致性哈希
不同负载均衡算法适用的业务场景也不同。
轮询策略只适用于每个节点数据相同的场景,但不适用于分布式系统。
哈希算法虽然能建立数据和节点的映射,但每次节点发生变化时,会进行数据迁移。
一致性哈希算法
所以才有了一致性哈希算法。
一致性哈希算法不同于普通哈希算法对节点数量进行取模运算,它是对2^32进行取模运算,是一个固定的值。
可以将取模结果理解为一个圆环,叫做哈希环,一句话来说就是将存储节点和数据都映射到一个首尾相连的哈希环上。
- 如何找到存储的数据的节点:先进行哈希运算,确定位置,沿着此位置的第一个节点就是要找的节点。
- 增加或者减少节点带来的影响:仅仅影响该节点在哈希环上顺时针相邻的后继节点,其他数据无影响。
但这样还不能保证节点在哈希环上分布均匀,可能会有大量请求集中在一个节点上。
通过虚拟节点提高均衡度
具体做法:不将真实节点映射到哈希环上,而是将虚拟节点映射到哈希环上,并将虚拟节点映射到实际节点上。
节点数目多了,节点在环上就分布均匀了,当节点变化时,会有不同节点共同分担系统变化,稳定性也更高了。