服务器大并发的思考

    xiaoxiao2021-03-25  185

    1.不要让内核执行所有繁重的任务。将数据包处理,内存管理,处理器调度等任务从内核转移到应用程序高效地完成。让Linux内核只处理控制层,数据层完全交给应用程序来处理。 2.用事件驱动服务器(如Nginx和Node)代替线程服务器(Apache) 3.实现数据包可扩展——编写自己的个性化驱动来绕过堆栈

    数据包的问题是它们需经Unix内核的处理。网络堆栈复杂缓慢,数据包最好直接到达应用程序,而非经过操作系统处理之后。 做到这一点的方法是编写自己的驱动程序。所有驱动程序将数据包直接发送到应用程序,而不是通过堆栈。你可以找到这种驱动程序:PF_RING,NETMAP,Intel DPDK(数据层开发套件)。Intel不是开源的,但有很多相关的技术支持。 速度有多快?Intel的基准是在一个相当轻量级的服务器上,每秒处理8000万个数据包(每个数据包200个时钟周期)。这也是通过用户模式。将数据包向上传递,使用用户模式,处理完毕后再返回。Linux每秒处理的数据包个数不超过百万个,将UDP数据包提高到用户模式,再次出去。客户驱动程序和Linux的性能比是80:1。 对于每秒1000万个数据包的目标,如果200个时钟周期被用来获取数据包,将留下1400个时钟周期实现类似DNS / IDS的功能。 通过PF_RING得到的是原始数据包,所以你必须做你的TCP堆栈。人们所做的是用户模式栈。Intel有现成的可扩展TCP堆栈 4.多核的可扩展性

    多核可扩展性不同于多线程可扩展性。

    多线程 每个CPU内核中不止一个线程 用锁来协调线程(通过系统调用) 每个线程有不同的任务 多核 每个CPU内核中只有一个线程 当两个线程/内核访问同一个数据时,不能停下来互相等待 同一个任务的不同线程 要解决的问题是怎样将一个应用程序分布到多个内核中去

    解决方案: 在每个核心中保存数据结构,然后聚合的对数据进行读取。 原子性。CPU支持可以通过C语言调用的指令,保证原子性,避免冲突发生。开销很大,所以不要处处使用。 无锁的数据结构。线程无需等待即可访问,在不同的架构下都是复杂的工作,请不要自己做。 线程模型,即流水线与工作线程模型。这不只是同步的问题,而是你的线程如何架构。 处理器关联。告诉操作系统优先使用前两个内核,然后设置线程运行在哪一个内核上,你也可以通过中断到达这个目的。所以,CPU由你来控制而不是Linux。 5.内存的可扩展性

    如果你有20G的RAM,假设每次连接占用2K的内存,如果你还有20M的三级缓存,缓存中会没有数据。数据转移到主存中处理花费300个时钟周期,此时CPU没有做任何事情。 每个数据包要有1400个时钟周期(DNS / IDS的功能)和200个时钟周期(获取数据包)的开销,每个数据包我们只有4个高速缓存缺失,这是一个问题。 联合定位数据 不要通过指针在满内存乱放数据。每次你跟踪一个指针,都会是一个高速缓存缺失:[hash pointer] -> [Task Control Block] -> [Socket] -> [App],这是四个高速缓存缺失。 保持所有的数据在一个内存块:[TCB |socket| APP]。给所有块预分配内存,将高速缓存缺失从4减少到1。 分页 32GB的数据需占用64MB的分页表,不适合都存储在高速缓存。所以存在两个高速缓存缺失——分页表和它所指向的数据。这是开发可扩展的软件不能忽略的细节。 解决方案:压缩数据,使用有很多内存访问的高速缓存架构,而不是二叉搜索树 NUMA架构加倍了主存访问时间。内存可能不在本地socket,而是另一个socket上。 内存池 启动时立即预先分配所有的内存 在对象,线程和socket的基础上进行分配。 超线程 每个网络处理器最多可以运行4个线程,英特尔只能运行2个。 在适当的情况下,我们还需要掩盖延时,比如内存访问中一个线程在等待另一个全速的线程。 大内存页 减小页表规模。从一开始就预留内存,让你的应用程序管理内存。

    转载请注明原文地址: https://ju.6miu.com/read-1487.html

    最新回复(0)