nginx如何解决惊群效应

news2024/10/7 9:43:59

什么是惊群效应

惊群效应(thundering herd)是指多进程(多线程)在同时阻塞等待同一个事件的时候(休眠状态),如果等待的这个事件发生,那么他就会唤醒等待的所有进程(或者线程),但是最终却只能有一个进程(线程)获得这个时间的“控制权”,对该事件进行处理,而其他进程(线程)获取“控制权”失败,只能重新进入休眠状态,这种现象和性能浪费就叫做惊群效应。

惊群效应场景

在早期的Linux版本中,内核对于阻塞在epoll_wait的进程,也是采用全部唤醒的机制,所以存在和accept相似的“惊群”问题。新版本的的解决方案也是只会唤醒等待队列上的个进程或线程,所以,新版本Linux 部分的解决了epoll的“惊群”问题。所谓部分的解决,意思就是:对于部分特殊场景,使用epoll机制,已经不存在“惊群”的问题了,但是对于大多数场景,epoll机制仍然存在“惊群”。

1、场景一:epoll_create()在fork子进程之前:

如果epoll_create()调用在fork子进程之前,那么epoll_create()创建的epfd 会被所有子进程继承。接下来,所有子进程阻塞调用epoll_wait(),等待被监控的描述符(包括用于监听客户连接的监听描述符)出现新事件。如果监听描述符发生可读事件,内核将阻塞队列上排在位的进程/线程唤醒,被唤醒的进程/线程继续执行accept()函数,得到新建立的客户连接描述符connfd。这种情况下,任何一个子进程被唤醒并执行accept()函数都是没有问题的。

但是,接下来,子进程的工作如果是调用epoll_ctl(epfd, EPOLL_CTL_ADD, connfd, &ev);将新建连接的描述符connfd加入到epfd中统一监控的话,因为,当前的epfd是在fork之前创建的,此时系统中只有一个epoll监控文件,即所有子进程共享一个epoll监控文件。任何一个进程(父进程或子进程)向epoll监控文件添加、修改和删除文件描述符时,都会影响到其它进程的epoll_wait。 

后续,当connfd描述符上接收到客户端信息时,内核也无法保证每次都是唤醒同一个进程/线程,来处理这个连接描述符connfd上的读写信息(其它进程可能根本就不认识connfd;或者在不同进程中,相同的描述符对应不同的客户端连接),最终导致连接处理错误。(另外,不同的线程处理同一个连接描述符,也会导致发送的信息乱序)

所以,应该避免epoll_create()在fork子进程之前。关于这一点,据说libevent的文档中有专门的描述。

2、场景二:epoll_create()在fork子进程之后:

如果epoll_create()在fork子进程之后,则每个进程都有自己的epoll监控文件(当某个进程将新建连接的描述符connfd加入到本进程的epfd中统一监控,不会影响其它进程的epoll_wait),但是为了实现并发监听,所有的子进程都会调用 

epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev); 

将监听描述符加入到监控文件中,也就是说所有子进程都在通过epoll机制轮询同一个监听描述符。如果有新的客户端请求接入,监听描述符出现POLLIN事件(表示描述符可读,有新连接接入),此时内核会唤醒所有的进程,所以“惊群”的问题依然存在。

源码实现

int ngx_cdecl
main(int argc, char *const *argv)
{
    ...
     cycle = ngx_init_cycle(&init_cycle);
    ...
     ngx_master_process_cycle(cycle); // 创建工作进程
    
}

ngx_cycle_t *
ngx_init_cycle(ngx_cycle_t *old_cycle)
{
    ...
    if (ngx_open_listening_sockets(cycle) != NGX_OK) {
        goto failed;
    }
    
}

ngx_int_t
ngx_open_listening_sockets(ngx_cycle_t *cycle)
{
    ... 
  //ngx_socket定义
  //#define ngx_socket          socket
  //#define ngx_socket_n        "socket()"
    s = ngx_socket(ls[i].sockaddr->sa_family, ls[i].type, 0);

    if (bind(s, ls[i].sockaddr, ls[i].socklen) == -1) {
       err = ngx_socket_errno;
    }    

    if (listen(s, ls[i].backlog) == -1) {
        ...
    }
    ...
}

nginx解决方式

对于这种情况下的“惊群”问题,Nginx的解决方案和《UNP 第三版》第30章中30.7和30.8给出的加锁方案类似,大概就是通过互斥锁对每个进程从epoll_wait到accept之间的处理通过互斥量保护。需要注意的是,对于这种加锁操作,每次只有一个子进程能执行epoll_wait和accept,具体哪个进程得到执行,要看内核调度。所以,为了解决负载均衡的问题,Nginx的解决方案中,每个进程有一个当前连接计数,如果当前连接计数超过大连接的7/8,该进程就停止接收新的连接。

源码实现如下:

void
ngx_process_events_and_timers(ngx_cycle_t *cycle)
{
   ...
   //对监听的sockets事件进行处理

    if (ngx_use_accept_mutex) {
        if (ngx_accept_disabled > 0) {
            ngx_accept_disabled--;

        } else {
            // 加锁成功进行后续处理
            if (ngx_trylock_accept_mutex(cycle) == NGX_ERROR) {
                return;
            }
            //设置网络读写事件延迟处理标志,即在释放锁后处理
            if (ngx_accept_mutex_held) {
                flags |= NGX_POST_EVENTS;

            } else {
                if (timer == NGX_TIMER_INFINITE
                    || timer > ngx_accept_mutex_delay)
                {
                    timer = ngx_accept_mutex_delay;
                }
            }
        }
    }

    ...

    //这里面epollwait等待网络事件
    //网络连接事件,放入ngx_posted_accept_events队列
    //网络读写事件,放入ngx_posted_events队列
    (void) ngx_process_events(cycle, timer, flags);
    ...

    //先处理网络连接事件,只有获取到锁,这里才会有连接事件
    ngx_event_process_posted(cycle, &ngx_posted_accept_events);

    //释放锁,让其他进程也能够拿到
    if (ngx_accept_mutex_held) {
        ngx_shmtx_unlock(&ngx_accept_mutex);
    }

    ngx_event_expire_timers();
     //处理网络读写事件
    ngx_event_process_posted(cycle, &ngx_posted_events);
}

  • SO_REUSEPORT

nginx 在 1.9.1 版本加入了这个功能,其本质是利用了 Linux 的 reuseport 的特性, 内核允许多个进程 listening socket 到同一个端口上,从内核层面做了负载均衡,每次只唤醒其中一个进程。

修改参数如下,只需要在listen 端口后面加上 reuseport即可 :

server {
        listen 80 reuseport;
        listen 443 ssl http2 reuseport;
        server_name   xxx.xxx.com;
        charset utf-8;

官方测试情况如下:

网上这方面的内容也非常多,大家可以自行搜索。详细参考 The SO_REUSEPORT socket option [LWN.net]  https://blog.n0p.me/2018/02/2018-02-20-portsharding/

注意:启用REUSEPORT也可能导致一些意想不到的问题,比如单个请求处理的最大延迟可能会增加,可以参考 https://blog.cloudflare.com/the-sad-state-of-linux-socket-balancing/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1887030.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

web权限到系统权限 内网学习第一天 权限提升 使用手工还是cs???msf可以不??

现在开始学习内网的相关的知识了,我们在拿下web权限过后,我们要看自己拿下的是什么权限,可能是普通的用户权限,这个连添加用户都不可以,这个时候我们就要进行权限提升操作了。 权限提升这点与我们后门进行内网渗透是乘…

用AI,每天创作200+优质内容,2分钟教会你操作!

前段时间发布了这篇“寻找爆款文案及标题的9大渠道,直接搬运都能搞流量!”,里面我讲到如何寻找爆款标题。最近不少朋友问我,如何创作这个标题相关的内容。 多数平台都有风控规则,有些平台内容也会有字数要求。为了让大…

【D3.js in Action 3 精译】1.2.2 可缩放矢量图形(三)

当前内容所在位置 第一部分 D3.js 基础知识 第一章 D3.js 简介 1.1 何为 D3.js?1.2 D3 生态系统——入门须知 1.2.1 HTML 与 DOM1.2.2 SVG - 可缩放矢量图形 ✔️ 第一部分第二部分【第三部分】✔️ 1.2.3 Canvas 与 WebGL(精译中 ⏳)1.2.4 C…

10.8K star!史上最强Web应用防火墙雷池WAF

长亭雷池SafeLine是长亭科技耗时近 10 年倾情打造的WAF(Web Application Firewall), 一款敢打出口号 “不让黑客越雷池一步” 的 WAF,愿称之为史上最强的一款Web应用防火墙,足够简单、足够好用、足够强的免费且开源的 WAF,基于业…

全球首款商用,AI为视频自动配音配乐产品上线

近日,海外推出了一款名为Resona V2A的产品,这是全球首款商用视频转音频 (V2A) 技术产品。这项突破性技术利用AI,仅凭视频数据即可自动生成高质量、与上下文相关的音频,包括声音设计、音效、拟音和环境音,为电影制作人、…

单向链表结构

链表结构简介 链表结构是一种用比较特殊的数据结构类型,它也是线性数据结构中的一种,但是与栈结构等线性数据结构不同,它的内部结构并不是一个简单的存储空间,而是一个带有指向性质的单元。要理解链表结构要弄清楚两个问题&#x…

react_后台管理_项目

目录 1.运行项目 2. 项目结构 ①项目顶部导航栏 ②项目左侧导航栏 ③主页面-路由切换区 本项目使用的是 reacttsscss 技术栈。 1.运行项目 在当前页面顶部下载本项目,解压后使用编辑器打开,然后再终端输入命令: npm i 下载依赖后&am…

使用Python绘制动态螺旋线:旋转动画效果

文章目录 引言准备工作前置条件 代码实现与解析导入必要的库初始化Pygame绘制螺旋线函数主循环 完整代码 引言 螺旋线是一个具有美学和数学魅力的图形。通过编程,我们可以轻松创建动态旋转的螺旋线动画。在这篇博客中,我们将使用Python和Pygame库来实现…

【python脚本】批量检测sql延时注入

文章目录 前言批量检测sql延时注入工作原理脚本演示 前言 SQL延时注入是一种在Web应用程序中利用SQL注入漏洞的技术,当传统的基于错误信息或数据回显的注入方法不可行时,例如当Web应用进行了安全配置,不显示任何错误信息或敏感数据时&#x…

解决卡顿发热,超帧技术焕发中重载游戏动力

近几年,中国手游市场规模不断扩大,开发者通过在画面、玩法等方面的持续创新和打磨,推出更加精品化的产品。然而愈发精美的画质和复杂的玩法,也给硬件带来超高的负载,导致玩家在游戏过程中,频繁出现掉帧卡顿…

动态规划算法,完全零基础小白教程!不是计算机的都能学会!万字吐血详解。

目录 一、动态规划算法概念 题一 1、算法解析 1)确定状态: ​2)状态转移方程: ​3)初始化: 4)填表顺序: 5)返回值: 2、代码 题二 1、算法解析 1、确…

你喜欢波段交易吗?

波段交易的核心在于精准捕捉市场中的长期趋势波动,以实现更为稳健的收益。与剥头皮和日内交易不同,波段交易者更倾向于持有交易头寸数日乃至数周,以更宽广的视角把握市场动态。 这种交易方式的优势在于,它降低了对即时市场反应的…

C - Popcorn(abs358)

题意&#xff1a;有n个摊子&#xff0c;m个爆米花&#xff0c;想花费最少去的店铺买到所有的口味的爆米花&#xff0c;找到每一列都为‘o’的最少行数。 分析&#xff1a;用dfs寻找最少路径 #include<bits/stdc.h> using namespace std; typedef long long ll; char x;…

【面试系列】AI研究员高频面试题及详细解答

欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;欢迎订阅相关专栏&#xff1a; ⭐️ 全网最全IT互联网公司面试宝典&#xff1a;收集整理全网各大IT互联网公司技术、项目、HR面试真题. ⭐️ AIGC时代的创新与未来&#xff1a;详细讲解AIGC的概念、核心技术、…

内网渗透第四天!!!冲冲冲!!怎么绕过uac以及你会all劫持???不安全的服务路径以及服务权限,你会吗???

在第三天我们简单的说了一下绕过uac&#xff0c;但是我们使用的msf模块ask要对方管理员跟我们一起来进行操作&#xff0c;才可以进行提权的操作&#xff0c;这点就限制住了我们。我们今天来讲一下不用钓鱼的绕过的操作。 绕过uac&#xff1a; 使用uacme项目和msf联动来进行绕过…

和小红书一起参会! 了解大模型与大数据融合的技术趋势

在过去的两年中&#xff0c;“大模型”无疑成为互联网行业的焦点话题&#xff0c;曾经炙手可热的大数据架构似乎淡出公众视野。然而&#xff0c;大数据领域并未停滞不前&#xff0c;反而快速演进&#xff0c;传统依赖众多开源组件的大数据平台正逐步过渡到以融合与简化为核心特…

Xorbits inference操作实战

1.操作环境 序号软件版本备注1Windows1.版本&#xff1a;Windows 10 专业版2.版本号&#xff1a;21H23.操作系统内部版本&#xff1a;19044.18892Docker Desktop4.24.2 (124339)3WSLUbuntu 22.04 LTS4Python3.105CUDA12.16Dify0.6.6 Xorbits inference 是一个强大且通用的分布…

那些好用的 Vue3 的工具搭子!!【送源码】

2020 年 9 月 18 日 Vue3 的正式发布已经过去了大约 3 年 9 个月左右&#xff01;&#xff01;&#xff01; 随着 Vue3 版本的逐渐成熟&#xff0c;我们的前端世界也迎来了一系列令人振奋的更新和工具。Vue 生态圈的持续扩大&#xff0c;无疑为前端开发人员带来了前所未有的便…

C盘清理和管理

本篇是C盘一些常用的管理方法&#xff0c;以及定期清理C盘的方法&#xff0c;大部分情况下都能避免C盘爆红。 C盘清理和管理 C盘存储管理查看存储情况清理存储存储感知清理临时文件清理不需要的 迁移存储 磁盘清理桌面存储管理应用存储管理浏览器微信 工具清理 C盘存储管理 查…

鸿蒙开发设备管理:【@ohos.multimodalInput.touchEvent (触摸输入事件)】

触摸输入事件 设备上报的触屏事件。 说明&#xff1a; 本模块首批接口从API version 9开始支持。后续版本的新增接口&#xff0c;采用上角标单独标记接口的起始版本。 导入模块 import {Action,ToolType,SourceType,Touch,TouchEvent} from ohos.multimodalInput.touchEvent;…