前言

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

缘起,内核已经成为瓶颈

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

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

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

内核3.9之前的Linux 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显然成为了一个瓶颈

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连接,也是其高效的主要原因。其架构图可以看出其改进:

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

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内核模块为其去除了锁竞争)

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

  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的吞吐量

实际部署环境的成绩

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

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

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

长连接的支持正在开发中

长连接支持,还是需要等一等的。但是要支持什么类型长连接?百万级别应用服务器类型,还是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尽快发布对长连接的支持,还有更高性能的提升咯 :))

资源引用

  • fastsocket github
  • fastsocket优化网络性能原理

Fastsocket学习笔记之小结篇相关推荐

  1. Noah的学习笔记之Python篇:装饰器

    Noah的学习笔记之Python篇: 1.装饰器 2.函数"可变长参数" 3.命令行解析 注:本文全原创,作者:Noah Zhang  (http://www.cnblogs.co ...

  2. 4、Latex学习笔记之数学公式篇

    目录 数学公式 1.基础操作 1.1插入公式 1.2 编号 1.3对齐 1.4上下标 2.希腊字母 3.字体 4.括号 4.1括号 4.2大括号 5.运算符 5.1关系运算符 5.2集合运算符 5.3 ...

  3. OracleDesigner学习笔记1――安装篇

    OracleDesigner学习笔记1――安装篇   QQ:King MSN:qiutianwh@msn.com Email:qqking@gmail.com 一.       前言 Oracle是当 ...

  4. 设计模式学习笔记(目录篇)

    设计模式学习笔记(目录篇) 为了方便查看,特此将设计模式学习笔记系列单独做一个目录. 1   设计模式学习笔记(一:命令模式) 2   设计模式学习笔记(二:观察者模式) 3   设计模式学习笔记(三 ...

  5. Redis学习笔记1-理论篇

    目录 1,Redis 数据类型的底层结构 1.1,Redis 中的数据类型 1.2,全局哈希表 1.3,数据类型的底层结构 1.4,哈希冲突 1.5,rehash 操作 2,Redis 的 IO 模型 ...

  6. Redis学习笔记(实战篇)(自用)

    Redis学习笔记(实战篇)(自用) 本文根据黑马程序员的课程资料与百度搜索的资料共同整理所得,仅用于学习使用,如有侵权,请联系删除 文章目录 Redis学习笔记(实战篇)(自用) 1.基于Sessi ...

  7. 树莓派4B学习笔记——IO通信篇(UART)

    文章目录 UART简介 树莓派使用UART与串口屏通信 串口屏简介 硬件连接 配置串口接口 树莓派打开UART接口 树莓派安装串口调试助手 编程实现 wiringSerial.h Serial简介 C ...

  8. JavaScript学习笔记之入门篇

    JavaScript学习笔记之入门篇 JavaScript引入 1. 页面级 js: 2. 外部js文件: JavaScript变量 1. 变量的作用: 2. 声明变量: 3. 变量赋值: 4. 单一 ...

  9. 《软技能-代码之外的生存指南》学习笔记之理财篇

    <软技能–代码之外的生存指南>学习笔记之理财篇 作者:[美] John Z. Sonmez 摘要:这是⼀本真正从"⼈"(⽽⾮技术也⾮管理)的⾓度关注软件开发⼈员⾃⾝发展 ...

最新文章

  1. python 100题
  2. Python——随机法(蒙特卡罗方法)计算圆周率
  3. 【Scala谜题】继承
  4. javascript:设置URL参数的方法,适合多条件查询
  5. uva 1612——Guess
  6. 前端学习(2748):uniapp创建项目和演示
  7. php识别字符编码,PHP自动识别字符集编码并完成转码_PHP教程
  8. Vue之脚手架第一个项目
  9. 数据结构和算法基础(6)——常用十种算法
  10. 轮廓(图形)之凹点切分
  11. Java 图形用户界面编程
  12. ubuntu安装nvidia驱动
  13. 第6章 索引和数据完整性
  14. 未能加载文件或程序集“Microsoft.Web.Infrastructure”
  15. MATLAB机械臂建模
  16. Charles安装及使用教程
  17. TCP-IP详解卷一(一)概述
  18. 手把手教你从零搭建深度学习项目(附链接)
  19. gets(), fgets(), scanf()总结
  20. Spring 6.0 堪称最强!新特性,惊爆了!

热门文章

  1. 机器学习十大算法(二)
  2. shell中判断空字符串和有趣的空字符串
  3. 团队开发冲刺第二阶段_4
  4. 细数人体十大最“无用”的器官。
  5. u-boot-1.3.4移植到mini2440+128M nand boot(3)
  6. odoo API装饰器one、model、multi的区别
  7. Hyper-V安装(摘自本站)
  8. HTML中的转义字符 (转)
  9. 2013杭电warm up1 hdu 4712 Hamming Distance
  10. FuncT,TResult的使用方法(转载)