2020-12-23高性能网络编程笔记

网速一直在提升,但单机网络IO的能力有限,传统的网络业务(IP层以下)需要一套软件架构的高性能IO开发框架

网卡IO和内核的瓶颈

  1. 传统的收发报文方式都必须采用硬中断来做通讯,每次硬中断大约消耗100微秒,这还不算因为终止上下文所带来的Cache Miss。
  2. 数据必须从内核态用户态之间切换拷贝带来大量CPU消耗,全局锁竞争。
  3. 收发包都有系统调用的开销。
  4. 内核工作在多核上,为可全局一致,即使采用Lock Free,也避免不了锁总线、内存屏障带来的性能损耗。
  5. 从网卡到业务进程,经过的路径太长,有些其实未必要的,例如netfilter框架,这些都带来一定的消耗,而且容易Cache Miss

主流解决方案都是旁路网卡IO,绕过内核直接在用户态收发包来解决内核的瓶颈

服务器处理客户端请求:1. 建立连接,发送请求 2. 构建响应 3. 返回数据

阻塞与非阻塞:调用者在接收到返回之前是否等待

同步与异步:被调用者得到返回结果后才返回/被调用者先返回应答,再去计算结果

进程与线程

进程是由内核来管理和调度的,进程的切换只能发生在内核态,因此虚拟内存、栈、全局变量等用户空间的资源,以及内核堆栈、寄存器等内核空间的状态,叫做进程上下文。

线程是操作系统调度的最小单位,线程会共享父进程的虚拟内存和全局变量等资源,因此父进程的资源加上线程自己的私有数据叫做线程的上下文。

对于线程的上下文切换来说,如果是同一进程的线程,因为有资源共享,所以会比多进程间的切换消耗更少的资源


百万并发服务器

假设单台服务器最多只能支持万级并发连接,为了提供百万并发,现在的集群、分布式技术提出通过将并发负载分配到多台服务器上水平扩展实现。

  1. 进程限制: ulimit -n 一个进程最多能打开的文件数  (永久修改 /etc/rc.local)
  2. 全局限制:文件描述符个数(cat /proc/sys/fs/file-nr) 返回已分配的fd、已分配但未使用的fd、最大fd个数

端口号不会限制连接数:1024-65535是用户可用端口号,一个TCP连接由四元组{local ip, local port,remote ip,remote port}标识,对于同一个port和ip的服务器可以支持很多并发连接,所以端口号不是限制

C10K问题

网络通信是互联网的基础,最初的服务器都是基于进程/线程模型的,新到来一个TCP连接,就需要分配1个进程(或者线程)。而进程又是操作系统最昂贵的资源,一台机器无法创建很多进程。如果是C10K就要创建1万个进程,那么单机而言操作系统是无法承受的(往往出现效率低下甚至完全瘫痪)。如果是采用分布式系统,维持1亿用户在线需要10万台服务器,成本巨大。如何突破单机性能局限,是高性能网络编程所必须要直面的问题。些局限和问题最早被Dan Kegel 进行了归纳和总结,并首次成系统地分析和提出解决方案,后来这种普遍的网络现象和技术局限都被大家称为 C10K 问题。

C10M:单机1000万网络并发连接和数据处理能力

C10K:单机1万网络并发连接和数据处理能力

关键:CPU。创建的进程线程太多,数据拷贝过于频繁(缓存IO、内核数据拷贝到用户进程空间、阻塞),进程/线程上下文切换消耗大,造成大量CPU核心计算资源消耗,榨干单机性能。

网卡角度:

Linux 协议栈是复杂和繁琐的,数据包经过它无非会导致性能的巨大下降,并且会占用大量的内存资源。可以在 Linux 系统
启动时把业务所需内存直接预留出来,脱离 Linux 内核的管理。其次, Linux 一般采用 4K每页,而我们可以采用更大内存分页,比如 2M,这样能在一定程度上减少地址转换。

随着网络技术的不断创新和市场的发展,越来越多的网络设备基础架构开始向基于通用处理器平台的架构方向融合,期望用更低的成本和更短的产品开发周期来提供多样的网络单元和丰富的功能,如应用处理、控制处理、包处理、信号处理等。DPDK 已经发展成支持多种高性能网卡和多通用处理器平台的开源软件工具包。采用DPDK/Netmap等驱动程序接管网卡数据,远离操作系统,可以替代内核的工作,提高效率。

DPDK / netmap 

将 NIC 的 rx_ring_buffer 和 tx_ring_buffer,映射到 user-level,直接在 user-level 进行 packet RX/TX

传统的流程:网卡 -> 驱动 -> 协议栈 -> Socket接口 -> 业务

DPDK流程:网卡 -> DPDK轮询模式-> DPDK基础库 -> 业务

系统采用中断机制协同处理CPU与其他设备工作,CPU以来网卡的中断默认由cpu0处理,在大量小包的网络环境下可能出现cpu0负载高,而其他cpu空闲的情况。

内核角度:

具有多核的多线程核间绑定,采用传统的内核方法协调应用程序是行不通的,Linux 管理前两个 CPU,应用程序管理其余的 CPU,中断只发生在允许的CPU 上,可以最大化核心 CACHE 利用、实现无锁设计、避免进程切换消耗等等。
内存角度:
首先,可以在 Linux 系统启动时把业务所需内存直接预留出来,脱离 Linux 内核的管理。其次, Linux 一般采用 4K每页,而我们可以采用更大内存分页,比如 2M,这样能在一定程度上减少地址转换等的性能消耗

网络编程角度:

  1. 为每个连接分配独立进程/线程
  2. 用同一个进程/线同时处理若干连接:IO多路复用

循环处理每个socket(阻塞问题)、select监控每个socket(fd过高、fd_set初始化、逐个排查效率问题)、poll(避免select重复初始化、但仍然有效率问题)、epoll(异步非阻塞回调Reactor,只返回状态变化的fd、以依赖linux,nginx、libevent、node.js都是epoll的产物)、libevent库(跨平台)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值