Fastsocket学习笔记之小结篇
原文 http://www.blogjava.net/yongboy/archive/2015/02/05/422760.html
前言
前面啰啰嗦嗦的几篇文字,各个方面介绍了Fastsocket,盲人摸象一般,能力有限,还得继续深入学习不是。这不,到了该小结的时候了。
Fastsocket架构
Fastsocket架构图可以很清晰说明其大致结构,内核态和用户态通过ioctl函数传输。记得netmap在重写网卡驱动里面通过ioctl函数直接透传到用户态中,其更为高效,但没有完整的TCP/IP网络堆栈支持嘛。
原先的TCP调用图
- kernel 3.9之前的tcp socket实现
- bind系统调用会将socket和port进行绑定,并加入全局tcp_hashinfo的bhash链表中
- 所有bind调用都会查询这个bhash链表,如果port被占用,内核会导致bind失败
- listen则是根据用户设置的队列大小预先为tcp连接分配内存空间
- 一个应用在同一个port上只能listen一次,那么也就只有一个队列来保存已经建立的连接
- nginx在listen之后会fork处多个worker,每个worker会继承listen的socket,每个worker会创建一个epoll fd,并将listen fd和accept的新连接的fd加入epoll fd
- 但是一旦新的连接到来,多个nginx worker只能排队accept连接进行处理
- 对于大量的短连接,accept显然成为了一个瓶颈
起因:内核已经成为瓶颈
-
TCP处理&多核
- 一个完整的TCP连接,中断发生在一个CPU核上,但应用数据处理可能会在另外一个核上
- 不同CPU核心处理,带来了锁竞争和CPU Cache Miss(波动不平衡)
- 多个进程监听一个TCP套接字,共享一个listen queue队列
- 用于连接管理全局哈希表格,存在资源竞争
- epoll IO模型多进程对accept等待,惊群现象
-
Linux VFS的同步损耗严重
- Socket被VFS管理
- VFS对文件节点Inode和目录Dentry有同步需求
- SOCKET只需要在内存中存在即可,非严格意义上文件系统,不需要Inode和Dentry
- 代码层面略过不必须的常规锁,但又保持了足够的兼容性
Fastsocket所作改进
- TCP单个连接完整处理做到了CPU本地化,避免了资源竞争
- 保持完整BSD socket API
CPU之间不共享数据,并行化各自独立处理TCP连接,也是其高效的主要原因。其架构图可以看出其改进:
Fastsocket的TCP调用图
- 多个进程可以同时listen在同一个port上
- 动态链接库libfsocket.so拦截socket、bind、listen等系统调用并进入这个链接库进行处理
- 对于listen系统调用,fastsocket会记录下这个fd,当应用通过epoll将这个fd加入到epoll fdset中时,libfsocket.so会通过ioctl为该进程clone listen fd关联的socket、sock、file的系统资源
- 内核模块将clone的socket再次调用bind和listen
- bind系统调用检测到另外一个进程绑定到已经被绑定的port时,会进行相关检查
- 通过检查sock将会被记录到port相关联的一个链表中,通过该链表可以知道所有bind同一个port的sock
- 而sock是关联到fd的,进程则持有fd,那么所有的资源就已经关联到一起
- 新的进程再次调用listen系统调用的时候,fastsocket内核会再次为其关联的sock分配accept队列
- 结果是多个进程也就拥有了多个accept队列,可避免cpu cache miss
- fastsocket提供将每个listen和accept的进程绑定到用户指定的CPU核
- 如果用户未指定,fastsocket将会为该进程默认绑定一个空闲的CPU核
Fastsocket短连接性能
在新浪测试中,在24核的安装有Centos 6.5的服务器上,借助于Fastsocket,Nginx和HAProxy每秒处理连接数指标(connection/second)性能很惊人,分别 增加290%和620%。这也证明了,Fastsocket带来了TCP连接快速处理的能力。 除此之外,借助于硬件特性:
- 借助于Intel超级线程,可以获得另外20%的性能增长
- HAProxy代理服务器借助于网卡Flow-Director特性支持,吞吐量可增加15%
Fastsocket V1.0正式版从2014年3月份开始已经在新浪生产环境中使用,用作代理服务器,因此大家可以考虑是否可以采用。针对1.0版本,以下环境较为收益:
- 服务器至少不少于8个CPU核心
- 短连接被大量使用
- CPU周期大部分消耗在网络软中断和套接字系统调用上
- 应用程序使用基于epoll的非阻塞IO
- 应用程序使用多个进程单独接受连接
多线程嘛,就得需要参考示范应用所提供实践建议了。
Nginx测试服务器配置
- nginx工作进程数量设置成CPU核数个
- http keep-alive特性被禁用
- 测试端http_load从nginx获取64字节静态文件,并发量为500*CPU核数
- 启用内存缓存静态文件访问,用于排除磁盘影响
- 务必禁用accept_mutex(多核访问accept产生锁竞争,另fastsocket内核模块为其去除了锁竞争)
从下表测试图片中,可以看到:
- Fastsocket在24核服务器达到了475K Connection/Second,获得了21倍的提升
- Centos 6.5在CPU核数增长到12核时并没有呈现线性增长势头,反而在24核时下降到159k CPS
- Linux kernel 3.13在24核时获得了近乎两倍于Centos 6.5的吞吐量,283K CPS,但在12核后呈现出扩展性瓶颈
HAProxy重要配置
- 工作进程数量等同于CPU核数个
- 需要启用RFD(Receive Flow Deliver)
- http keep-alive需要禁用
- 测试端http_load并发量为500*CPU核数
- 后端服务器响应外围64个字节的消息
测试结果中:
- fastsocket呈现出了惊人的扩展性能
- 24核,Linux kernel 3.13成绩为139K CPS
- 24核,Centos 6.5借助Fastsocket,获得了370K CPS的吞吐量
实际部署环境的成绩
8核服务器线上环境运行了24小时的成绩,图a展示了部署fastsocket之前CPU利用率,图b为部署了fastsocekt之后的CPU利用率。 Fastsocket带来的收益:
- 每个CPU核心负载均衡
- 平均CPU利用率降低10%
- HAProxy处理能力增长85%
其实吧,这一块期待新浪公布更多的数据。
长连接的支持正在开发中
长连接支持,还是需要等一等的。但是要支持什么类型长连接?百万级别应用服务器类型,还是redis,可能是后者。虽然目前正做,但目前没有时间表,但目前所做特性总结如下:
- 网络堆栈的定制
- SKB-Pool ,每一CPU核对应一个预分配skb pool,替换内核缓冲区kernel slab
- Percore skb pool
- 合并skb头部和数据
- 本地Pool和重复循环使用的Pool(Flow-Director)
- Fast-Epoll
- 多进程之间TCP连接共享变得稀少
- 在file结构体中保存Epoll entry,用以节省调用epoll_ctl时红黑树查询的开销
- SKB-Pool ,每一CPU核对应一个预分配skb pool,替换内核缓冲区kernel slab
- 跨层的设计
- Direct-TCP ,数据包隶属于已建立套接字会直接跳过路由过程
- 记录TCP套接字的输入路由信息(Record input route information in TCP socket)
- 直接查找网络套接字在进入网络堆栈之前(Lookup socket directly before network stack)
- 从套接字读取输入路由信息(Read input route information from socket)
- 标记数据包被路有过(Mark the packet as routed)
- Receive-CPU-Selection 类似于RFS,但更轻巧、精准与快速
- 把当前CPU核id编码到套接字中(Application marks current CPU id in the socket)
- 直接查询套接字在进入网络堆栈之前(Lookup socket directly before network stack)
- 读取套接字中包含的CPU核,然后发送给它(Read CPU id from socket and deliver accordingly)
- RPS-Framework 数据包在进入网络堆栈之前,让开发者在内核模块之外定制数据包投递规则,扩充RPS功能
- Direct-TCP ,数据包隶属于已建立套接字会直接跳过路由过程
Redis测试结果
测试环境:
- CPU: Intel E5 2640 v2 (6 core) * 2
- NIC: Intel X520
Redis配置选项:
- TCP持久连接
- 8个Redis实例,绑定不同端口
- 使用到8个CPU核心,并且绑定CPU核
测试结果:
- 仅开启RSS:20%的吞吐量增加
- 启用网卡Flow-Director特性:45%吞吐量增加
但需要注意:
- 仅为实验测试阶段
- 为V1.0补充,Nginx和HAProxy同样会收益
Fastsocket v1.1
V1.1版本要增加长连接的支持,那么类似于Redis的服务器应用程序就很受益了,因为没有具体的时间表,只能够慢慢等待了。
以后一些优化措施
- 在上下文切换时,避免拷贝操作,Zero-Copy
- 中断机制完善,减少中断
- 支持批量提交,降低系统函数调用
- 提交到Linux kernel主分支上去
- HugeTLB/HugePage等
Fastsocket和mTCP等简单对比
说是对比,其实是我从mTCP论文中摘取出来,增加了Fastsocket一栏,可以看出人们一直努力的脚步。
Types | Accept queue | Conn. Locality | Socket API | Event Handling | Packet I/O | Application Mod- ification | Kernel Modification |
PSIO , DPDK , PF RING , netmap | No TCP stack | Batched | No interface for transport layer | No(NIC driver) | |||
Linux-2.6 | Shared | None | BSD socket | Syscalls | Per packet | Transparent | No |
Linux-3.9 | Per-core | None | BSD socket | Syscalls | Per packet | Add option SO REUSEPORT | No |
Affinity-Accept | Per-core | Yes | BSD socket | Syscalls | Per packet | Transparent | Yes |
MegaPipe | Per-core | Yes | lwsocket | Batched syscalls | Per packet | Event model to completion I/O | Yes |
FlexSC,VOS | Shared | None | BSD socket | Batched syscalls | Per packet | Change to use new API | Yes |
mTCP | Per-core | Yes | User-level socket | Batched function calls | Batched | Socket API to mTCP API | No(NIC driver) |
Fastsocket | Per-core | Yes | BSD socket | Ioctl + kernel calls | Per packet | Transparent | No |
有一个大致的印象,也方便对比,但这只能是一个暂时的摘要而已,谁都说不准明天又有人提出更为高效的方案来。
部署尝试
怎么说呢,Fastsocket是为大家耳熟能详服务器程序Nginx,HAProxy等而开发的。但若应用环境为大量的短连接,并且是小文件 类型请求,不需要强制支持Keep-alive特性(短连接要的是快速请求-相应,然后关闭),那么管理员可以尝试一下Fastsocket,至于部署策 略,选择性部署几台作为实验看看结果。
小结
本系列到此算是告一段落啦。以后呢,自然是希望Fastsocket尽快发布对长连接的支持,还有更高性能的提升咯 :))