Fastsocket学习笔记之小结篇

标签: fastsocket 学习 笔记 | 发表时间:2015-02-05 15:21 | 作者:nieyong
出处:http://www.blogjava.net/

前言

前面啰啰嗦嗦的几篇文字,各个方面介绍了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架构图

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

Fastsocket Throughput

实际部署环境的成绩

Fastsocket Online

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尽快发布对长连接的支持,还有更高性能的提升咯 :))

资源引用



nieyong 2015-02-05 15:21 发表评论

相关 [fastsocket 学习 笔记] 推荐:

Fastsocket学习笔记之小结篇

- - BlogJava-首页技术区
前面啰啰嗦嗦的几篇文字,各个方面介绍了Fastsocket,盲人摸象一般,能力有限,还得继续深入学习不是. 使用Linux作为服务器,在请求量很小的时候,是不用担心其性能. 但在海量的数据请求下,Linux内核在TCP/IP网络处理方面,已经成为瓶颈. 比如新浪在某台HAProxy服务器上取样,90%的CPU时间被内核占用,应用程序只能够分配到较少的CPU时钟周期的资源.

shell 学习笔记

- tiger - 游戏人生
将脚本目录加到 PATH 中. 在 dash 中如何进行字符串替换. 将 rst 格式文档转换为 blog 可用的 html 代码. shell 脚本虽然不是非常复杂的程序, 但对于首次接触的我来讲, 多少还是有些忌惮. 不过, 接触任何新事物都需要勇敢面对, 逐步树立信心. 我是冲着把脚本写好去的, 所以, 我的目标是能够写出友好, 健壮, 优美的脚本..

OAuth学习笔记

- 宋大妈 - FeedzShare
来自: 标点符 - FeedzShare  . 发布时间:2011年08月29日,  已有 2 人推荐. OAuth(开放授权)是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用. OAuth允许用户提供一个令牌,而不是用户名和密码来访问他们存放在特定服务提供者的数据.

Vim学习笔记

- 临池学书 - C++博客-首页原创精华区
最近在学习Vimtutor中的相关内容,Vim的使用博大精深,很多命令一旦不使用就会忘记,下面把其中的没有使用到的相关命令做一个简单的总结,供以后复习使用. 至于常见的保存,插入等等命令,则不予记录,在以后的使用中加深练习即可. To change until the end of a word, type  ce (ce + 修正的单词).

OAuth学习笔记

- jiaosq - 标点符
OAuth(开放授权)是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用. OAuth允许用户提供一个令牌,而不是用户名和密码来访问他们存放在特定服务提供者的数据. 每一个令牌授权一个特定的网站(例如,视频编辑网站)在特定的时段(例如,接下来的2小时内)内访问特定的资源(例如仅仅是某一相册中的视频).

HTML学习笔记

- - CSDN博客推荐文章
超文本标记语言( 英文:HyperText Markup Language,HTML)是为“ 网页创建和其它可在 网页浏览器中看到的信息”设计的一种 标记语言. HTML被用来结构化信息——例如标题、段落和列表等等  点击打开链接. w3schools  点击打开链接 {语法大全,超赞.

jQuery学习笔记

- - ITeye博客
什么是jQuery,它能为我们做什么. jQuery是一个javascript类库或称之为javascript框架. 无需刷新页面从服务器获取信息. 简化常见的javascript任务. 为什么会如此流行或说得到大量用户群的支持:. 多重操作集于一行(避免使用临时变量或不必要的重复代码). jQuery利用了CSS选择符的能力,在DOM中快捷而轻松地获取元素或元素集合.

JdbcTemplate学习笔记

- - SQL - 编程语言 - ITeye博客
1、使用JdbcTemplate的execute()方法执行SQL语句. 2、如果是UPDATE或INSERT,用update()方法.    JdbcTemplate将我们使用的JDBC的流程封装起来,包括了异常的捕捉、SQL的执行、查询结果的转换等等. spring大量使用Template Method模式来封装固定流程的动作,XXXTemplate等类别都是基于这种方式的实现.

Disruptor 学习笔记

- - 开源软件 - ITeye博客
Disruptor 是一个高性能异步处理框架,也可以认为是一个消息框架,它实现了观察者模式. Disruptor 比传统的基于锁的消息框架的优势在于:它是无锁的、CPU友好;它不会清除缓存中的数据,只会覆盖,降低了垃圾回收机制启动的频率. Disruptor 为什么快. 通过内存屏障和原子性的CAS操作替换锁.

Activiti学习笔记

- - 企业架构 - ITeye博客
第一个Activiti的HelloWorld. 获取核心ProcessEngine对象 2. 根据需求,获取对应的服务实例 3. 使用服务方法,做事情 * * @author Administrator * */ public class HelloWorld {. // 加载核心API ProcessEngine.