F-Stack是一个全用户态(kernel bypass)的高性能的网络接入开发包,基于DPDK、FreeBSD协议栈、微线程接口等,适用于各种需要网络接入的业务,用户只需要关注业务逻辑,简单的接入F-Stack即可实现高性能的网络服务器。 本文介绍F-Stack的详细架构及如何解决内核协议栈面临的问题。
传统内核协议栈的性能瓶颈
在传统的内核协议栈中,网络包处理存在诸多瓶颈,严重影响网络包的收发性能。性能瓶颈主要包括以下几个方面
- 局部性失效 - 一个数据包的处理可能跨多个CPU核心、缓存失效、NUMA不友好 一个数据包可能中断在cpu0,内核态处理在cpu1,用户态处理在cpu2, 这样跨越多个核心,造成局部性失效,CPU缓存失效, 同时可能存在跨NUMA访问内存,性能受到很大影响。
- 中断处理 - 硬件中断、软中断、上下文切换 当网络中数据量很大时,大量的数据包产生频繁的硬件中断请求, 这些硬件中断可以打断之前较低优先级的软中断或者系统调用的执行过程, 如果这种打断频繁进行的话,将产生较高的性能开销。 用户态内核态的上下文切换和软中断都增加了额外的开销。
- 内存拷贝 - 内核态和用户态之间的内存拷贝 网络数据包从网卡到应用程序需要经过如下的过程: 数据从网卡通过DMA等方式传到内核开辟的缓冲区; 数据从内核空间复制到用户态空间。 在Linux内核协议栈中,这个耗时甚至占到了数据包整个处理流程的一半。
- 系统调用 - 软中断、上下文切换、锁竞争 频繁到达的硬件中断或者软中断都可能随时抢占系统调用的运行,这也将产生大量的上下文切换开销。 内核中一些资源如PCB表等都需要加锁处理,大量的并发操作造成很大的性能浪费,特别是大量短连接的创建。
F-Stack总体架构
无共享架构
F-Stack使用了多进程的无共享架构,每个进程CPU、网卡队列绑定,具有无竞争、零拷贝、线性扩展、NUMA友好等特点。
- 各进程绑定独立的网卡队列和CPU,每个NUMA节点使用独立的内存池,请求通过设置网卡RSS散落到各进程进行处理,解决了局部性失效的问题。
- 使用DPDK的轮询模式,排除中断处理造成的性能影响。
- 使用DPDK作为网络I/O模块,将数据包从网卡直接接收到用户态,减少内核态到用户态的内存拷贝。
- 请求平均分配到每个核上,通过设置DPDK的rss hash函数保证相同ip、port的请求落到同一个核上。
- 各进程拥有独立的协议栈、PCB表等资源,消除了协议处理过程中的各种资源竞争。
- 进程之间不共享内存,通过无锁环形队列(rte_ring)传递通信,如ARP包等。
用户态协议栈
移植FreeBSD协议栈至用户态。 通过外加头文件、宏控制、以及hook相关实现进行的移植,对FreeBSD源代码的修改不到100行, 对后续的跟进社区,升级版本非常友好。
- 内存分配相关函数重新hook实现。(当前使用mmap和malloc,后续会替换成rte_mempool和rte_malloc)
- 定时器使用rte_timer驱动,ticks定时更新,timecounter定时执行。
- 移除内核线程、中断线程等,统一进行轮询处理。
- 移除文件系统相关的绑定。
- 移除FreeBSD内核中的所有锁,用空的宏替换掉。
- 其他glue code。
类posix接口和微线程框架
提供了类posix接口和微线程框架,方便现有应用接入,替换接口。 后续我们会提供类似LD_PRELOAD的方式,使得现有程序尽量无改动迁移到F-Stack。 微线程框架,移植自腾讯开源的毫秒服务MSEC里使用的spp_rpc。 具有同步编程、异步执行的特点,无需处理复杂的异步逻辑。
问题及优化
- CPU 100% 由于使用的DPDK轮询模式,cpu使用率会一直是100%, 后续会引入DPDK的轮询+中断模式,当连续几次轮询没有收到包后, 转为中断模式,有包后持续轮询,直到又没包进来。
- 常规网络工具(如tcpdump、ifconfig、netstat等)无法使用 由于DPDK接管了网卡,所有的数据包都运行在用户态,常规的网络工具都无法使用, 为此我们对一些工具进行了移植,目前已经完成了sysctl、ifconfig。 抓包可以在config.ini里配置开启,抓包文件也可以在wireshark里直接分析。
- Nginx reload 当前F-Stack的Nginx是运行在NGX_PROCESS_SINGLE模式下的, 各个进程互不关联,无法使用原有的reload命令。后续会进行修复。
最佳实践
- 使用性能高的多核CPU,配置config.ini里的lcore_mask(进程运行在哪些cpu上)运行多个进程。
- 使用10G、25G、40G的多队列网卡,支持硬件卸载功能,支持的RSS队列数越多越好。
- 配置尽可能多的Hugepage。
- 配置config.ini关闭抓包。
Roadmap:
- 继续移植各种网络配置工具,方便F-Stack在各种网络环境(如GIF/GRE/VxLAN等隧道)下的部署使用。
- 移植SPDK的用户态驱动至F-Stack,提升磁盘I/O性能。
- 增加对数据流的HOOK点/镜像等,方便对数据包进行自定义处理。
- 提供协议栈的相关优化模块,如TCP加速、防护等。
- 类posix接口提供LD_RRELOAD方式,简化已有应用的接入方式。
- 提供PHP/Python等语言的接口封装,方便相关WEB服务的快速接入。