全用户态网络开发套件 F-Stack 架构分析

F-Stack是一个全用户态(kernel bypass)的高性能的网络接入开发包,基于DPDK、FreeBSD协议栈、微线程接口等,适用于各种需要网络接入的业务,用户只需要关注业务逻辑,简单的接入F-Stack即可实现高性能的网络服务器。

本文介绍F-Stack的详细架构及如何解决了内核协议栈面临的问题。

传统内核协议栈的性能瓶颈

在传统的内核协议栈中,网络包处理存在诸多瓶颈,严重影响网络包的收发性能。性能瓶颈主要包括以下几个方面

  1. 局部性失效 - 一个数据包的处理可能跨多个CPU核心、缓存失效、NUMA不友好 一个数据包可能中断在cpu0,内核态处理在cpu1,用户态处理在cpu2, 这样跨越多个核心,造成局部性失效,CPU缓存失效, 同时可能存在跨NUMA访问内存,性能受到很大影响。
  2. 中断处理 - 硬件中断、软中断、上下文切换 当网络中数据量很大时,大量的数据包产生频繁的硬件中断请求, 这些硬件中断可以打断之前较低优先级的软中断或者系统调用的执行过程, 如果这种打断频繁进行的话,将产生较高的性能开销。 用户态内核态的上下文切换和软中断都增加了额外的开销。
  3. 内存拷贝 - 内核态和用户态之间的内存拷贝 网络数据包从网卡到应用程序需要经过如下的过程: 数据从网卡通过DMA等方式传到内核开辟的缓冲区;数据从内核空间复制到用户态空间。在Linux内核协议栈中,这个耗时甚至占到了数据包整个处理流程的一半。
  4. 系统调用 - 软中断、上下文切换、锁竞争频繁到达的硬件中断或者软中断都可能随时抢占系统调用的运行,这也将产生大量的上下文切换开销。 内核中一些资源如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服务的快速接入。

原创声明,本文系作者授权云+社区-专栏发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

编辑于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏架构师之路

Cache Aside Pattern

在《究竟先操作缓存,还是数据库?》,有同学在评论提出,相关方案违背了“Cache Aside Pattern”的原则,故今天聊一聊Cache Aside Pat...

912
来自专栏大闲人柴毛毛

缓存世界中的三大问题及解决方案

目前的IO设备远不能满足互联网应用海量的读写请求。于是便出现了缓存,利用内存的高速读写性能来应付海量的查询请求。然而内存资源非常宝贵,将全量数据存储在内存中显...

2705
来自专栏Java Edge

zookeeper分布式锁1 分布式锁的概念与数据最终不一致性的场景zookeeper分布式锁小结

随着互联网技术的不断发展,数据量的不断增加,业务逻辑日趋复杂,在这种背景下,传统的集中式系统已经无法满足我们的业务需求,分布式系统被应用在更多的场景,而在分布式...

1431
来自专栏用户画像

2.5.5 作业和进程的关系

进程是系统资源的使用者,系统的资源的大部分都是以进程为单位分配的。而用户使用计算机是为了实现一串相关的任务,通常把用户要求计算机完成的这一串任务成为作业。

691
来自专栏张善友的专栏

SQL Server 2005的负载均衡

SQL Server 2005仍然不直接地支持负载均衡——但是它为以前SQL Server版本中可用的所有负载均衡方法提供了令人激动的改善和支持。   目录 1...

18710
来自专栏PHP技术

不同场景下 MySQL 的迁移方案

原文出处: 温国兵(@dbarobin) 一 为什么要迁移 MySQL 迁移是 DBA 日常维护中的一个工作。迁移,究其本义,无非是把实际存在的物体挪走,...

3955
来自专栏杨建荣的学习笔记

海量数据迁移之分区表批量insert性能改进(r2笔记67天)

在平时的工作中接触到的分区表一般都比较大,而且分区也少则几十,多则几百,上千。 在数据迁移的时候,分区表的迁移更是块大骨头,因为数据量太大,而且有些分区表中还有...

2015
来自专栏IT技术精选文摘

RabbitMQ关于吞吐量,延迟和带宽的一些理论

你在Rabbit有一个队列,然后一些消费者从这个队列中消费。如果你根本没有设置QoS(basic.qos),那么Rabbit会把所有的队列消息都按照网络和客户端...

3068
来自专栏AlgorithmDog的专栏

Akka 使用系列之二: 测试

通过上一篇文章,我们已经大致了解怎么使用 Akka,期待细致用法。这篇文章将介绍如何用 Akka-testkit 对 Akka 程序进行测试。 ? ...

1817
来自专栏JAVA技术zhai

分享30道Redis面试题,面试官能问到的我都找到了

Redis本质上是一个Key-Value类型的内存数据库,很像memcached,整个数据库统统加载在内存当中进行操作,定期通过异步操作把数据库数据flush到...

1002

扫码关注云+社区