聂永的博客

记录工作/学习的点点滴滴。

Fastsocket学习笔记之小结篇

前言

前面啰啰嗦嗦的几篇文字,各个方面介绍了Fastsocket,盲人摸象一般,能力有限,还得继续深入学习不是。这不,到了该小结收尾的时候了。

缘起,内核已经成为瓶颈

使用Linux作为服务器,在请求量很小的时候,是不用担心其性能。但在海量的数据请求下,Linux内核在TCP/IP网络处理方面,已经成为瓶颈。比如新浪在某台HAProxy服务器上取样,90%的CPU时间被内核占用,应用程序只能够分配到较少的CPU时钟周期的资源。

经过Haproxy系统详尽分析后,发现大部分CPU资源消耗在kernel里,并且在多核平台下,kernel在网络协议栈处理过程中存在着大量同步开销。

同时在多核上进行测试,HTTP CPS(Connection Per Second)吞吐量并没有随着CPU核数增加呈现线性增长:

Image(2)

内核3.9之前的Linux TCP调用

Image(13)

  • 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显然成为了一个瓶颈

Linux网络堆栈所存在问题

  • TCP处理&多核

    • 一个完整的TCP连接,中断发生在一个CPU核上,但应用数据处理可能会在另外一个核上
    • 不同CPU核心处理,带来了锁竞争和CPU Cache Miss(波动不平衡)
    • 多个进程监听一个TCP套接字,共享一个listen queue队列
    • 用于连接管理全局哈希表格,存在资源竞争
    • epoll IO模型多进程对accept等待,惊群现象

  • Linux VFS的同步损耗严重

    • Socket被VFS管理
    • VFS对文件节点Inode和目录Dentry有同步需求
    • SOCKET只需要在内存中存在即可,非严格意义上文件系统,不需要Inode和Dentry
    • 代码层面略过不必须的常规锁,但又保持了足够的兼容性

Fastsocket所作改进

  1. TCP单个连接完整处理做到了CPU本地化,避免了资源竞争
  2. 保持完整BSD socket API

CPU之间不共享数据,并行化各自独立处理TCP连接,也是其高效的主要原因。其架构图可以看出其改进:

20150205215656_12

Fastsocket架构图可以很清晰说明其大致结构,内核态和用户态通过ioctl函数传输。记得netmap在重写网卡驱动里面通过ioctl函数直接透传到用户态中,其更为高效,但没有完整的TCP/IP网络堆栈支持嘛。

Fastsocket的TCP调用图

Image

  • 多个进程可以同时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内核模块为其去除了锁竞争)

从下表测试图片中,可以看到:

  1. Fastsocket在24核服务器达到了475K Connection/Second,获得了21倍的提升
  2. Centos 6.5在CPU核数增长到12核时并没有呈现线性增长势头,反而在24核时下降到159k CPS
  3. 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的吞吐量

Fastsocket Throughput

实际部署环境的成绩

Fastsocket Online

8核服务器线上环境运行了24小时的成绩,图a展示了部署fastsocket之前CPU利用率,图b为部署了fastsocekt之后的CPU利用率。 Fastsocket带来的收益:

  • 每个CPU核心负载均衡
  • 平均CPU利用率降低10%
  • HAProxy处理能力增长85%

20150205215658_398

其实吧,这一块期待新浪公布更多的数据。

长连接的支持正在开发中

长连接支持,还是需要等一等的。但是要支持什么类型长连接?百万级别应用服务器类型,还是redis,可能是后者。虽然目前正做,但目前没有时间表,但目前所做特性总结如下:

  1. 网络堆栈的定制
    • SKB-Pool,每一CPU核对应一个预分配skb pool,替换内核缓冲区kernel slab
      • Percore skb pool
      • 合并skb头部和数据
      • 本地Pool和重复循环使用的Pool(Flow-Director)
    • Fast-Epoll
      • 多进程之间TCP连接共享变得稀少
      • 在file结构体中保存Epoll entry,用以节省调用epoll_ctl时红黑树查询的开销
  2. 跨层的设计
    • 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功能

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的服务器应用程序就很受益了,因为没有具体的时间表,只能够慢慢等待了。

以后一些优化措施

  1. 在上下文切换时,避免拷贝操作,Zero-Copy
  2. 中断机制完善,减少中断
  3. 支持批量提交,降低系统函数调用
  4. 提交到Linux kernel主分支上去
  5. 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尽快发布对长连接的支持,还有更高性能的提升咯 :))

资源引用

posted on 2015-02-05 15:21 nieyong 阅读(10401) 评论(5)  编辑  收藏 所属分类: Socket

评论

# re: Fastsocket学习笔记之小结篇 2015-02-06 09:12 京山游侠

太高大上了,370k的并发连接,猛。  回复  更多评论   

# re: Fastsocket学习笔记之小结篇 2015-02-08 12:46 xanpeng

"epoll IO模型多进程对accept等待,惊群现象"
——貌似2.6.32以后的内核就没有惊群现象了吧  回复  更多评论   

# re: Fastsocket学习笔记之小结篇 2015-02-09 09:40 nieyong

@xanpeng
在Linux内核版本 2.6.18 以前,所有监听进程都会被唤醒,但是只有一个进程 accept 成功,其余失败。这就是所谓的惊群效应 。在2.6.18 以后,已得到修复,仅有一个进程被唤醒并accept成功。  回复  更多评论   

# re: Fastsocket学习笔记之小结篇 2015-10-05 16:22 computer_xu

最后的那个表格 FastSocket 的Kernel Modification这一项应该是yes吧?看github上的代码,有专门定制的2.6内核,还需要配合kernel模块,再加上这个PRELOAD的so库三个部分,才能跑起来。  回复  更多评论   

# re: Fastsocket学习笔记之小结篇[未登录] 2016-08-16 10:40 astrid

你好,请问你是用什么方法或工具测试CPS的?
最近在做这方面的工作,希望你能提供一些思路。非常感谢!  回复  更多评论   


只有注册用户登录后才能发表评论。


网站导航:
 

公告

所有文章皆为原创,若转载请标明出处,谢谢~

新浪微博,欢迎关注:

导航

<2016年8月>
31123456
78910111213
14151617181920
21222324252627
28293031123
45678910

统计

常用链接

留言簿(58)

随笔分类(130)

随笔档案(151)

个人收藏

最新随笔

搜索

最新评论

阅读排行榜

评论排行榜