高性能服务器网络模型详解
off999 2025-03-07 23:14 10 浏览 0 评论
作者简介:谢友鹏,目前在网络技术部任职技术专家,从事网络和云相关开发。涉及领域vpn、sdwan、全球网络加速、区块链网络加速、cdn静态缓存等。长期研究nginx、apache traffic server、frp等代理项目和基于k8s的云原生项目。不定期更新个人微信公众号—网络技术修炼。关注领域:网络、云、linux高性能服务端。
1999年Dan Kegel在发表的论文中提出了The C10K problem,这篇论文对传统服务器架构处理大规模并发连接时的挑战进行了详细描述,并提出了一些解决方案和优化技术。这里的C指的是Concurrent(并发)的缩写,C10K问题是指怎么在单台服务器上并发一万个请求。如果你分析过性能问题一定会注意到,性能极限通常受到一个或多个资源的限制,比如内存、文件句柄个数、网络带宽、CPU等。这里讨论的前提是机器的物理资源和系统配置能够满足一万个请求。在这个前提下,网络并发主要关注两个方面:一是应用程序和操作系统内核之间如何进行IO事件通知,二是应用程序进程或线程的分配方式。
I/O模型
阻塞vs非阻塞、同步vs异步
你可能经常看到某某项目用的同步非阻塞模型、某某项目用的异步非阻塞模型,所以在正式讨论I/O模型前,还是先对齐一下关于阻塞、非阻塞以及同步、异步的概念吧。
阻塞、非阻塞指的是系统调用时“等待数据准备好”这个动作。比如read时候,如果内核判定数据还没准备好:内核让应用进程一直等待,直到数据准备好才通知应用进程就是阻塞;
内核立即通知应用程序说数据没准备好,你先干别的吧,就是非阻塞。
同步、异步指的是“内核空间与用户空间复制数据”这个动作。比如read时候,如果内核判定数据还没准备,过一段时间数据来了:内核通知应用进程你来读取数据吧,应用程序再次系统调用将数据读走就是同步;
内核将数据拷贝到用户空间后再通知应用进程,数据已经拷贝好了直接用吧,就是异步。
I/O模型详解
Stevens在《UNIX 网络编程 卷1》一书的6.2章节介绍了五种 I/O 模型。以下模型以UDP接收报文为例来说明这五种I/O 的工作方式。
> 阻塞式I/O
该模型使用最简,用户进程调用读取数据系统后就一直等待,直到内核数据准备好,并将数据从内核空间拷贝到用户空间后,调用结束。这种模型效率显然的低下,因为这种模型会导致两种可能结果:为每个请求分配一个进程或线程,那么高并发意味着内核要调度的进程或线程数量很庞大,调度、上下文切换等开销会使系统性能降低。
固定数量的进程或线程处理请求,那么这些进程或线程全被被占用后,新请求就只能等了。
该模型低效的根本原因在于阻塞,内核数据没有准备好的时候,用户态进程明明可以干其他活的,现在只能白白等待。
>非阻塞式 I/O
这种模型通过非阻塞的方式与内核打交道,如果内核中数据还未准备好,就立刻返回给用户进程,用户进程就可以先干别的事情,过一段再进行读数据的系统调用,直到内核数据准备好,并将数据拷贝到用户空间。相比于阻塞的模型,这种非阻塞+主动轮询的模型避免了用户进程白白等待内核准备数据的时间,所以效率有所提升,但是因为每次轮询都是系统调用,所以上下文切换变多了,因此性能也不高。
>I/O 复用
既然不停主动查询内核数据是否准备好这件事会引起系统性能下降,那能不能通过注册+通知的方式呢?这就是大名鼎鼎的I/O复用模型。该模型允许用户态通过一个进程将所有相关的读写事件(使用select、poll或epoll)注册到内核,然后内核会主动通知用户态进程,一旦任意一个或多个请求的读写数据准备好。这种方式在单个进程或线程中同时处理多个I/O通道的就绪状态被称为I/O多路复用。使用I/O多路复用既不会阻塞处理请求的进程,也不会因为轮询内核数据是否准备好而导致过多的系统调用,因此具有高效的特点。然而,需要注意的是,一旦内核通知应用进程数据准备就绪,仍然需要通过系统调用触发数据的读取过程。
Linux内核对这种模型的支持非常完善,因此许多高性能服务器在Linux环境中广泛采用这种模式。
>信号驱动式 I/O
I/O复用模型中是用一个进程(select、poll或epoll)阻塞或轮询所有请求的数据是否准备好,从而让所有请求进程的处理都不会阻塞。信号驱动式则没有这个复用的I/O进程,每个请求进程自己去内核注册,然后等数据准备好内核通知应用进程去处理。这种模型应用套接字处理的实践场景为基于UDP的NTP服务,几乎没有在TCP上的应用,因为对于TCP来说信号产生过于频繁,而且并没有告诉应用程序发生了什么事件,比如下面条件均会导致TCP套接字产生SIGIO信号:
监听套接字某个连接请求已经完成;
某个断链请求已经发起;
某个断链请求已经完成;
某个半连接已经关闭;
数据到达套接字;
数据已经从套接字发出;
发生某个异步错误。
>异步 I/O
前面几种方式,无论是阻塞还是非阻塞,从内核空间到用户空间复制数据的动作都是在内核通知用户进程后,用户进程再通过系统调用触发完成的,因此都属于同步操作。而异步I/O模型则不同,它允许用户态进程通过系统调用读取数据后,即使内核数据未准备好,也会立即返回给用户进程,告知数据未准备好,让用户进程可以执行其他操作。当数据准备好后,内核会将数据从内核空间拷贝到用户态,并直接回调用户进程,将数据送到用户进程手中。这种模型不仅具备非阻塞特性,还能进一步减少系统调用的次数,因此在理论上相对于其他模型更加高效。需要注意的是,这种模型需要操作系统内核的支持。
在《UNIX网络编程卷1》一书中,截至书稿时,支持POSIX异步I/O的系统相对较少。由于早期Linux内核对网络异步I/O的支持不够成熟,在Linux环境下,大多数高性能网络服务器选择采用I/O复用的方式,如epoll。然而,从Linux内核5.0版本开始,引入了io_uring异步操作,随着该技术的成熟,越来越多的高性能网络服务器(例如nginx)开始支持使用这种异步I/O方式。
>如何简单理解5种I/O?
下面通过一个例子对比一下5种模型,顾客是应用进程,餐饮人员为内核,餐桌为应用进程的数据buffer:
阻塞式I/O:交完钱也要在窗口排队,等师傅做好,将饭端给你,你再端到自己餐桌。
非阻塞式I/O:交完钱你就可以离开窗口玩一会了,窗口有个屏幕,你过一会跑过来看一下自己的饭好了没,直到饭做好,自己端到自己的餐桌。
I/O复用:好几个同学都把饭卡交给你,你一个人跑到窗口排队刷卡,谁的饭好了,你就打电话给谁,让他自己将饭端到餐桌。
信号驱动式I/O:你去窗口手机刷卡后就可离开了,饭做好会通过手机通知你,然后自己过去将饭端到餐桌。
异步I/O:去窗口点餐后,告诉服务员你在哪个餐桌就可以离开了,饭做好,服务员会将饭帮你端到餐桌。
进程/线程分配
进程和线程的创建、调度都需要系统开销。在高并发系统中,为每个请求分配一个进程或线程会对性能产生不利影响。为了克服这个问题,高性能的网络模型通常采用进程池或线程池来管理进程或线程。进程/线程池的设计目的是降低创建和销毁进程/线程的频率,并限制系统中总进程/线程的数量,以减少内核调度的开销。
常用高性能模式
reactor 模式:《The Design and Implementation of the Reactor》一文详细介绍了reactor模式的工作方式,简单来说,reactor模式=I/O复用 + 进程池/线程池。
proactor模式:《Proactor: An Object Behavioral Pattern for Demultiplexing and Dispatching Handlers for Asynchronous Events》一文详细介绍了proactor模式的工作方式,简单来说,proactor模式=异步I/O+ 进程池/线程池。
惊群效应:对于TCP请求来说,最理想的情况是每个事件每次从池中唤醒一个进程或线程去执行,这样既不需要等待又不会引起竞争。用一个进程或线程专门负责处理accept事件,然后将接下来的事件继续分发给其他worker处理是可行的。有一些模型(比如nginx)存在多个进程或线程监听同一个端口的情况,如果不加处理会出现一个accept事件唤醒所有worker进程的情况,即惊群效应。为了应对惊群效应,早期nginx引入了accept_mutex的机制,竞争到锁的worker才会执行accept操作,从而避免所有worker都被唤醒。Linux3.9 版本后提供了reuseport更好的解决了多个进程或线程监听同一个端口引起的惊群问题,简单说就是内核帮你轮询,而不用在应用层面竞争了。
更快、更强大的网络模型
欲望是永无止境的,有人提出The C10K problem问题,就有人提出The C10M problem,前文中的讨论基本都是围绕用户态进程和内核的交互优化,既然和内核交互容易导致性能瓶颈,那为何不旁路掉内核协议栈呢?所以有了更快的网络方案,比如DPDK、XDP、甚至硬件加速等。
- 上一篇:13 张图解 Java 中的内存模型
- 下一篇:「后端开发」Reactor 模型详解
相关推荐
- Python自动化脚本应用与示例(python自动化脚本教程)
-
Python是编写自动化脚本的绝佳选择,因其语法简洁、库丰富且跨平台兼容性强。以下是Python自动化脚本的常见应用场景及示例,帮助你快速上手:一、常见自动化场景文件与目录操作O批量重命名文件...
- 如何使用Python实现一个APP(如何用python做一个程序)
-
要使用Python实现一个APP,你可以选择使用一些流行的移动应用开发框架,如Kivy、PyQt或Tkinter。这里以Kivy为例,它是一个跨平台的Python框架,可以用于创建漂亮的图形用户界面(...
- 免费定时运行Python程序并存储输出文档的服务推荐
-
免费定时运行Python程序并存储输出文档的服务推荐以下是几种可以免费定时运行Python程序并存储输出结果的云服务方案:1.PythonAnywhere特点:提供免费的Python托管环境支持定时...
- 【Python程序开发系列】如何让python脚本一直在后台保持运行
-
这是我的第385篇原创文章。一、引言让Python脚本在后台持续运行,有几种常见的方式,具体方式可以根据你的系统环境和需求选择。二、Linux或macOS系统2.1使用nohup命令no...
- 运行和执行Python程序(运行python的程序)
-
一、Python是一种解释型的脚本编程语言,这样的编程语言一般支持两种代码运行方式:交互式编程在命令行窗口中直接输入代码,按下回车键就可以运行代码,并立即看到输出结果;执行完一行代码,你还可以继续...
- Python 初学者指南:计算程序的运行时长
-
在编写Python程序时,了解程序的运行时长是一项很有用的技能。这不仅能帮助你评估代码的效率,还能在优化程序性能时提供关键的数据支持。对于初学者来说,计算程序运行时长其实并不复杂,接下来就让我们看...
- pyest+appium实现APP自动化测试,思路全总结在这里
-
每天进步一点点,关注我们哦,每天分享测试技术文章本文章出自【码同学软件测试】码同学公众号:自动化软件测试码同学抖音号:小码哥聊软件测试01appium环境搭建安装nodejshttp://nodej...
- 血脉觉醒后,编程小白我是如何通过Deepseek和Trae轻松开发软件的
-
以下就是作为一个编程小白的我,是如何一步步开发软件的保姆级教程,请点赞收藏:第一步:打开#deepseek#(首先关闭深度思考和联网搜索)输入或复制你要让它做一个什么样软件的要求和提示词(你可以先用...
- 我用Deepseek+Trae写的python小软件,小白也能轻松用上模型啦!
-
利用AI大模型deepseek,搭配TraeCN,用半个小时做了一个本地Ollama安装部署和一键卸载的小工具,哈哈哈!感觉还不错#deepseek#一直想做一个本地Ollama安装部署和一键卸载...
- 在安卓设备上运行Python的方法(安卓能运行python吗)
-
技术背景在安卓设备上运行Python可以为开发者提供更多的开发选择和灵活性,能够利用Python丰富的库和简洁的语法来开发各种应用,如游戏、脚本工具等。然而,由于安卓系统原生不支持Python,需要借...
- 零基础小白,DeepSeek全自动编程,超详细提示词,一键生成软件!
-
我前面发表了文章,详细说了编程零基础小白,如何利用DeepSeek进行编程的全过程,感兴趣的可以去看看:DeepSeek全自动编程很多人不会写提示词,不知道怎么开始对话。话不多说,请先看下图中的对话,...
- 小白用DeepSeek+Python编写软件(用python制作软件)
-
周末无事,用DeepSeek生成全部代码,写了一个mp3音乐播放器,几分钟搞定,DeepSeek确实太强大了。我的提示语是这么写的:“请用Python语言写一个音乐播放器,支持常见音乐格式,我是Pyt...
- 零基础使用DeepSeek开发Windows应用程序,超简单超实用!
-
你敢相信,我居然用DeepSeek开发了一个能用的Windows软件!整个过程就像和学霸同桌组队做作业,我负责提需求,DeepSeek负责写代码改bug,全程碰到任何问题直接丢给DeepSeek即可。...
- 第二篇:如何安装Python并运行你的第一个程序
-
欢迎回到我的Python入门教程系列!在上一篇中,我们讨论了为什么Python是一门值得学习的编程语言。今天,我们将迈出第一步:安装Python并运行你的第一个程序。无论你是Windows、macOS...
- Python 运行,带你找入口,快速读懂程序
-
有C或Java编程开发经验的软件开发者,初次接触python程序,当你想快速读懂python项目工程时,是否觉得python程序有些太过随意,让你看有些无所适从,进而有些茫然。这是...
你 发表评论:
欢迎- 一周热门
- 最近发表
-
- Python自动化脚本应用与示例(python自动化脚本教程)
- 如何使用Python实现一个APP(如何用python做一个程序)
- 免费定时运行Python程序并存储输出文档的服务推荐
- 【Python程序开发系列】如何让python脚本一直在后台保持运行
- 运行和执行Python程序(运行python的程序)
- Python 初学者指南:计算程序的运行时长
- pyest+appium实现APP自动化测试,思路全总结在这里
- 血脉觉醒后,编程小白我是如何通过Deepseek和Trae轻松开发软件的
- 我用Deepseek+Trae写的python小软件,小白也能轻松用上模型啦!
- 在安卓设备上运行Python的方法(安卓能运行python吗)
- 标签列表
-
- python计时 (54)
- python安装路径 (54)
- python类型转换 (75)
- python进度条 (54)
- python的for循环 (56)
- python串口编程 (60)
- python写入txt (51)
- python读取文件夹下所有文件 (59)
- java调用python脚本 (56)
- python操作mysql数据库 (66)
- python字典增加键值对 (53)
- python获取列表的长度 (64)
- python接口 (63)
- python调用函数 (57)
- python qt (52)
- python人脸识别 (54)
- python斐波那契数列 (51)
- python多态 (60)
- python命令行参数 (53)
- python匿名函数 (59)
- python打印九九乘法表 (65)
- centos7安装python (53)
- python赋值 (62)
- python异常 (69)
- python元祖 (57)