压榨机器,Hack,设计极限强度的网络应用

简介:

    在《对话网友 - TCP一万连接系统设计》文后回复中,短短的评论不足以说明问题,于是单独撰文解释。

    对于一般的应用来说,操作系统足以对付,对于极限应用来说,操作系统往往就成了我们的障碍,这里的障碍有两个意义,第一个意义是,它出于某种考虑,而禁止了许多可以提高性能的机制,是不能也,另一个意义是,它限制了我们的思维,是不为也。

    由于操作系统要考虑多种方面的应用,因此在设计时做了很多防御性的措施,而对于具体的应用来说,这些措施往往不是最佳的,必要时,我们需要针对自己的应用进行定制,就像Google修改Linux的文件系统一样,为了实现极限的网络应用,我们需要对操作系统进行Hack。对极限应用来说,传统的网络编程模型根本就是不够看的。

    先列举几个应用场景:

    场景1:SmartBit是个很NB的协议测试工具,但是它只能测试一些最基本的协议。如果要测试一些更广泛的协议或者自定义协议,只能自己编写测试工具了,但是自己编写的测试工具受操作系统限制,如果使用传统的编程模型,很难达到超高的性能。

    场景2:有一些应用,这些应用的逻辑比较固定,而同时对性能要求又极高,比如说,各种专门的服务器,时间服务器啊,DNS服务器啊,大型的仿真系统啊,交换机啊,路由器啊,IDS啊等等,当然,这些都可以通过硬件来解决,但如果能用通用机器通用的软件来解决不更好吗?

    对于这些应用,就需要抛弃传统的网络应用概念了,什么Socket啊,IOCP啊,全见鬼去吧——都不够看。当然,如果常规开发就可以解决,比如《对话网友 - TCP一万连接系统设计》文中所提及的场景,自然就不需要采用专门的手段了。下面说的是常规方法解决不了时,当IOCP也只够塞牙缝时,应该怎么做的问题。

===========================

    大型应用为了提高性能,往往会放弃关系数据库,回归传统的key-value型数据库,为了极限化网络程序的性能,我们需要放弃传统的编程模型,回归最传统最原始的编程模型。

    对于操作系统来说,影响网络应用性能主要有这些方面:

    (1) 进程:现在的OS都是多任务系统,而单任务系统的性能是最佳的。

    (2) 内存:packet的复制问题。数据从到网卡,到内核,再到应用程序,要复制好几次,这些是无谓的。

    (3) 系统调用:系统调用是非常耗费资源的,Socket访问啦,内存分配啊,获取机器时间啦……都是系统调用。

    (4) 编程模型:IOCP模型不是最佳的。而传统的基于线程的编程模式,在对付大并发量时,完全不够看。比如 … 要跑1000万个线程 … 这时只能使用proto thread(或erlang所谓的轻量级线程)。假设更高呢?一亿个线程?这就要对线程进行消解了,采用完全的离散处理机制,把线程彻底的消解掉。

===========================

    下面,就以上四点来说,看看我们可以做哪些工作,把系统压榨到富士康的程度。

    (1) 进程

    为了这些,单独搞个操作系统不划算,就在现有的操作系统基础上看看可以怎样解决。很显然,我们不需要其它的应用来干扰我们的网络应用。因此需要给这个应用以最高的优先级。不Hack操作系统的情况下,最高的优先级也就是实时进程了。

    (2) (3) 内存和系统调用

    为了避免内存的复制,减少系统调用,需要和传统的Socket说ByeBye。最好的方式是自己实现一个协议栈。当然,不必实现一个完全的协议栈代码,只需实现我们需要的那部分即可,实质上,实现UDP协议只需要几百行代码,实现TCP也只需要三四千行代码,同时,又有很多开源的实现可以参考,这个工作看起来困难,实际上并不是很困难。还有个问题是内存的分配。操作系统内存的分配是低效的,因此,需要使用对象池,将内存重复使用。

    (4) 编程模型

    放弃线程,采用最原始的基于事件的离散处理模型。简单来说,我们把每一个需要做的工作分解成一个个的事件,然后放在队列中,应用程序呢,从队列中一个个取出事件并执行,执行的过程中,如果有其它后续操作,可以生成新事件,放入队列的尾部。

    如果我们需要某些事件优先执行,简单的队列就不行了,需要优先队列。更进一步,我们如果需要引入时间模型,需要安排某个事件在某个时间执行,时间在前的事件优先执行?怎么办呢?以前普遍使用的是Heap,Heap的插入的复杂度是O(logn),查找最大元素的复杂度是O(1)。Heap就是最优了吗?不是!还有比它更NB的数据结构——Calendar Queue,Calendar Queue插入的复杂度是O(1),查找最大元素的复杂度也是O(1)。因此,我们需要一个基于Calendar Queue的事件调度器。关于Calendar Queue,可以参见《Calendar queues: A fast O(1) priority queue implementation for the simulation event set problem. Communications of the ACM, 31(10):1220-1227, October 1988》这篇文章。Calendar Queue Scheduler网上也有开源的实现,几百行代码而已。这样一来,我们就将程序的调度由操作系统调度变成我们自己调度了,且调度的复杂度为O(1)。

    基于事件的处理有一个最大的问题就是每一事件的处理时间不能过长,否则容易Block住整个执行流程。具体实现中,可以将长事件分解成一个个的小事件离散的执行,这对程序员的要求较高。

===========================

    下面就把上面几点整合起来,看一个完整的工程实现。

    首选,在OS选择上,Windows不能用,不容易Hack。选择Linux。其实应该选用实时Linux的,因为普通的Linux,它的实时进程的优先级是低于系统进程的,但鉴于对实时Linux不熟悉,就选用普通的Linux了。普通的Linux已经够用了。

    然后,如何收发Packet呢?既然已经弃用传统Socket了,那就必须自己写一套收发机制了。我尝试过两种机制:

    一种是基于信号的机制,当有数据包来时,操作系统可以向应用程序发出一个信号,应用程序收到信号后可以自己去取。测试感觉,在大数据量下,这种情况丢包比较多;

    另一种是基于PF_RING Socket(http://www.ntop.org/PF_RING.html)的机制。PF_RING是在操作系统内核中建立一个环状缓冲区,然后应用程序定时扫描这个环状缓冲区,即不用复制内存,又绕过了操作系统那些复杂的Socket机制,这样可以最大化性能,同时,最小化丢包率。

DNA.png

    从环状缓冲区接收到数据之后,就需要自己来解析。关于轻量级协议栈的代码网上有不少,改吧改吧就能用。核心的几个协议没多少行代码。解析成功之后,就将它包装成一个个事件,给每个事件安排个Handler,然后把事件挂在Calendar Queue里等候处理即可。

    整个这么一套下来,那性能是超级牛——整个处理过程被消解成O(1)了,也就是说,在达到硬件限制之前,系统参数比如响应时间、丢包率等只和吞吐量有关,和并发连接数量无关——这里根本没有并发的概念,只有packet的收发和调度处理,主要的过程都是O(1)复杂度的。实际测试中,如果应用层计算量不大,那么限制主要来于网卡。我当初采用的只是奔4的普通pc机,百兆网卡,100Mbps全部跑满了,cpu依然很蛋定。而如果应用层计算量大,主要限制就是在cpu了。在这种编程模型下实现了QQ协议的客户端和服务器端,在一台机器上跑2万个QQ的客户端,按真实的基于UDP的QQ协议进行登录,获取好友,发送信息,发送群信息。每个QQ使用的ip地址和端口不同(由于绕过了系统的Socket,当然可以使用不同的ip地址了)。在另一台机器上跑个模拟的QQ的服务器,接收客户端的信息,然后发给要发到的客户端的ip。两台机器都是奔4机,单线程在跑。CPU占满了,主要cpu计算集中在编解码,应用逻辑这一块。内存占用只有30M,丢包很低。这里的限制就主要是CPU了。进行Profile,真正用在网络和调度部分的cpu只占20%。

    这种编程模式是最难的异步编程模式。但通过合理封装,可以简化操作,封装的好的话,写起来程序也是很happy的。而在现实中,每个网络协议,在正式提交之前,都会进行仿真研究,而网络协议的仿真正是采用的这种模型——因为这种模型性能极高,灵活度也最大。采用拿来主义的话,很多应用层协议只需要改改就可以用了。

===========================

    当然,这说的是极限情况下的系统设计问题。在很多情况下,我们需要极限的设计,但由于思维可能被限制住了,而打不破思维定势,从而采用普通的设计,这就是不应该的了。不能动不动就是啥IOCP只能多少啥啥啥,IOCP只是常规的极限,不是Hack的极限。而事实是,一台稍微好点的机器,可以实现1000Mbps的处理速度,算下来是100000packet每秒。这种吞吐量下,有可能是100万会话(这里就不用连接这个词了),每个会话每10秒发一个packet,也可能是10个会话,每个会话每秒发10000packet。而现有的操作系统的API针对后者比较擅长,但实际上两者的吞吐量是一样的,略有不同的是100万会话可能占有更多的内存资源而已。

    有时,不需要极限的设计,自然是不必采用这种手段了。

本文转自xiaotie博客园博客,原文链接http://www.cnblogs.com/xiaotie/archive/2010/06/13/1757483.html如需转载请自行联系原作者


xiaotie 集异璧实验室(GEBLAB)

相关文章
|
29天前
|
机器学习/深度学习 自然语言处理 数据处理
大模型开发:描述长短期记忆网络(LSTM)和它们在序列数据上的应用。
LSTM,一种RNN变体,设计用于解决RNN处理长期依赖的难题。其核心在于门控机制(输入、遗忘、输出门)和长期记忆单元(细胞状态),能有效捕捉序列数据的长期依赖,广泛应用于语言模型、机器翻译等领域。然而,LSTM也存在计算复杂度高、解释性差和数据依赖性强等问题,需要通过优化和增强策略来改进。
|
1月前
|
数据库 Android开发 开发者
构建高效Android应用:采用Kotlin协程优化网络请求处理
【2月更文挑战第30天】 在移动应用开发领域,网络请求的处理是影响用户体验的关键环节。针对Android平台,利用Kotlin协程能够极大提升异步任务处理的效率和简洁性。本文将探讨如何通过Kotlin协程优化Android应用中的网络请求处理流程,包括协程的基本概念、网络请求的异步执行以及错误处理等方面,旨在帮助开发者构建更加流畅和响应迅速的Android应用。
|
5天前
|
存储 监控 安全
网络安全与信息安全:防范漏洞、应用加密、提升意识
【4月更文挑战第18天】 在数字化时代,网络安全与信息安全保障已成为维护国家安全、企业利益和个人隐私的关键。本文深入探讨网络安全的多面性,包括识别和防御网络漏洞、应用加密技术保护数据以及提升全民网络安全意识的重要性。通过对这些关键领域的分析,文章旨在为读者提供实用的策略和建议,以增强其网络环境的安全防护能力。
7 0
|
6天前
|
数据采集 机器学习/深度学习 数据挖掘
网络数据处理中的NumPy应用实战
【4月更文挑战第17天】本文介绍了NumPy在网络数据处理中的应用,包括数据预处理、流量分析和模式识别。通过使用NumPy进行数据清洗、格式化和聚合,以及处理时间序列数据和计算统计指标,可以有效进行流量分析和异常检测。此外,NumPy还支持相关性分析、周期性检测和聚类分析,助力模式识别。作为强大的科学计算库,NumPy在处理日益增长的网络数据中发挥着不可或缺的作用。
|
14天前
|
传感器 监控 安全
|
14天前
|
安全 SDN 数据中心
|
14天前
|
安全 网络安全 网络虚拟化
虚拟网络设备与网络安全:深入分析与实践应用
在数字化时代📲,网络安全🔒成为了企业和个人防御体系中不可或缺的一部分。随着网络攻击的日益复杂和频繁🔥,传统的物理网络安全措施已经无法满足快速发展的需求。虚拟网络设备🖧,作为网络架构中的重要组成部分,通过提供灵活的配置和强大的隔离能力🛡️,为网络安全提供了新的保障。本文将从多个维度深入分析虚拟网络设备是如何保障网络安全的,以及它们的实际意义和应用场景。
|
29天前
|
机器学习/深度学习 PyTorch 算法框架/工具
卷积神经元网络中常用卷积核理解及基于Pytorch的实例应用(附完整代码)
卷积神经元网络中常用卷积核理解及基于Pytorch的实例应用(附完整代码)
20 0
|
29天前
|
存储 安全 网络安全
网络安全与信息安全:防范漏洞、应用加密技术与提升安全意识
在数字化时代,网络安全与信息安全已成为维护网络空间稳定的重要基石。本文将深入探讨网络安全中的关键问题——漏洞挖掘与利用,分析加密技术在信息保护中的核心作用,并强调提升个人与企业的安全意识的重要性。文章将提供对当前网络威胁的全面剖析,介绍前沿的防御策略,并分享如何构建一个多层次、全方位的安全防线。
|
1月前
|
机器学习/深度学习 安全 网络安全
网络安全与信息安全:防范漏洞、应用加密技术与提升安全意识
【2月更文挑战第30天】 在数字化时代,随着网络技术的迅猛发展,网络安全和信息安全问题日益突出。本文将深入探讨网络安全漏洞的成因与危害、加密技术的应用以及如何提升个人和企业的安全意识。通过分析不同类型的网络攻击手段,我们旨在提供一系列的防护策略,以增强信息系统的抵抗力。同时,文章还将介绍最新的加密技术动态,并讨论如何培养良好的网络安全习惯,为读者打造一道坚实的信息安全防线。