nginx 多进程 + io多路复用 实现高并发

一、nginx 高并发原理

简单介绍:nginx 采用的是多进程(单线程) + io多路复用(epoll)模型 实现高并发

二、nginx 多进程

  • 启动nginx
    o_4071177893-5bc03aa71d0f3_articlex.png
解析初始化配置文件后会 创建(fork)一个master进程 之后 这个进程会退出 
master 进程会 变为孤儿进程 由init进程托管。(可以通过python 或php 启动后创建子进程,然后杀死父进程得见子进程会由init进程托管)
如下图可以看到nginx master 进程由init(ppid 为1 )进程管理。

o_1559542370(1).png

  • master进程和worker进程
    o_20170303170640322.png
1、master 
首先nginx 创建一个master 进程,通过socket() 创建一个sock文件描述符用来监听(sockfd)
绑定端口(bind) 开启监听(listen)。
nginx 一般监听80(http) 或 443 (https)端口
(fork 多个子进程后,master 会监听worker进程,和等待信号)2、worker
然后 创建(fork)多个 worker子进程(复制master 进程的数据),
此时所有的worker进程 继承了sockfd(socket文件描述符),
当有连接进来之后 worker进程就可以accpet()创建已连接描述符,
然后通过已连接描述符与客户端通讯
  • 惊群现象
由于worker进程 继承了master进程的sockfd,当连接进来是,所有的子进程都将收到通知并“争着”与
它建立连接,这就叫惊群现象。大量的进程被激活又挂起,最后只有一个进程accpet() 到这个连接,这会消耗系统资源
(等待通知,进程被内核全部唤醒,只有一个进程accept成功,其他进程又休眠。这种浪费现象叫惊群)
  • nginx 对惊群现象的处理
原因:多个进程监听同一个端口引发的。
解决:如果可以同一时刻只能有一个进程监听端口,这样就不会发生“惊群”了,此时新连接事件只能唤醒正在监听的唯一进程。如何保持一个时刻只能有一个worker进程监听端口呢?nginx设置了一个accept_mutex锁,在使用accept_mutex锁是,只有进程成功调用了ngx_trylock_accept_mutex方法获取锁后才可以监听端口(linux 内核2.6 之后 不会出现惊群现象,只会有一个进程被唤醒)
  • 代码简单理解
    o_1559551783(1).png

三、worker进程

  • worker进程做了什么事
从上图中,我们可以看到worker进程做了
1、accept() 与客户端建立连接
2、recv()接收客户端发过来的数据
3、send() 向客户端发送数据
4、close() 关闭客户端连接
  • 如果不使用io多路复用 会是什么样的
首先 等待客户端有连接进来
accpet()  与客户端建立连接后
recv()  一直等待客户的发送过来数据(此时处于io阻塞状态)如果此时又有客户端过来建立连接,那么只能等待,需要一直等待close() 之后才可以建立连接也就是说这个worker进程会因为recv() 而处于阻塞状态,而不能处理与其他客户端建立连接,
这段时间不能做任何事,这是对性能了浪费。(进程 和线程的切换也是需要消耗 时间的。)
  • 能不能利用io堵塞的时间 accept,recv
nginx 采用了io多路复用技术实现了 

四、io多路复用

  • 什么是io复用
IO复用解决的就是并发行的问题,比如多个用户并发访问一个WEB网站,对于服务端后台而言就会产生多个请求,处理多个请求对于中间件就会产生多个IO流对于系统的读写。那么对于IO流请求操作系统内核有并行处理和串行处理的概念,串行处理的方式是一个个处理,前面的发生阻塞,就没办法完成后面的请求。这个时候我们必须考虑并行的方式完成整个IO流的请求来实现最大的并发和吞吐,这时候就是用到IO复用技术。IO复用就是让一个Socket来作为复用完成整个IO流的请求。
当然实现整个IO流的请求多线程的方式就是其中一种。
(一个socket作为复用来完成整个io流的请求连接建立(accept),而处理请求(recv,send,close)则采用多线程)
  • io复用之多线程处理
# -*- coding:utf-8 -*-
import socket
from threading import Threaddef comm(conn):data = conn.recv(1024)conn.send(data)conn.close()obj = socket.socket(socket.AF_INET,socket.SOCK_STREAM)   # sockfd (socket 文件描述符,这个描述符是用来监听的--监听socket)
obj.bind(('127.0.0.1',8082))                             # 绑定地址
obj.listen(5)                                            # 开启监听while True:conn,addr  = obj.accept()             // 每建立一个 连接 就交由线程处理t = Thread(target=comm,args=(conn,))  // 创建线程对象t.start()                             // 启动// 这样就可以处理多个io请求,不会因为一个没处理完而导致的堵塞
  • io 多路复用
多个描述符(监听描述符,已连接描述符) 的io 操作都能在一个线程内并发交替顺序完成,这就叫io多路复用,
这里的复用指的是复用同一个线程
  • io 多路复用的三种机制 select poll epoll
    • select
        #include <sys/select.h>int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);//参数nfds是需要监听的最大的文件描述符值+1//rdset,wrset,exset分别对应需要检测的可读文件描述符的集合,//可写文件描述符的集合集异常文件描述符的集合//参数timoout为结构timeval,用来设置select()的等待时间
        1、用户将自己所关心的文件描述符添加进描述符集中,并且明确关心的是读,写,还是异常事件2、select 通过轮询的方式不断扫码所有被关心的文件描述符,具体时间由参数timeout决定的3、执行成功则返回文件描述符已改变的个数4、具体哪一个或哪几个文件描述符就绪,则需要文件描述符集传出,它既是输入型参数,又是输出型参数5、fd_set 是用位图存储文件描述符的,因为文件描述符是唯一且递增的整数
        特点:1、可关心的文件描述符数量是有上限的,取决于fd_set(文件描述符集)的大小2、每次的调用select 前,都要把文件描述符重新添加进fd_set(文件描述符集)中,因为fd_set也是输出型参数在函数返回后,fd_set中只有就绪的文件描述符3、通常我们要关心的文件描述符不止一个,所有首先用数组保存文件描述符,每次调用select前再通过遍历数逐个添加进去
    
        缺点:1、每次调用select都需要手动设置fd_Set2、每次调用select 需要遍历fd_set 集合,而且要将fd_set 集合从用户态拷贝到内核态,如何fd很多时,开销会很大3、select 支持的文件描述符数量太少 32- 1024 64 -2048
    • poll
        #include <sys/poll.h>int poll(struct pollfd *fds, nfds_t nfds, int timeout);// 第一个参数是指向一个结构数组的第一个元素的指针// 第二个参数是要监听的文件描述符的个数// 第三个参数意义与select相同        //pollfd结构struct pollfd{int fd; short events;  short revents;};//events 是我们要关心的事件,revents是调用后操作系统设置的参数,//也就是表明该文件描述符是否就绪
        首先创建一个pollfd结构体变量数组fd_list,然后将我们然后将我们关心的fd(文件描述符)放置在数组中的结构变量中,并添加我们所关系的事件,调用poll函数,函数返回后我们再通过遍历的方式去查看数组中那些文件描述符上的事件就绪了。
        特点(相对于select)1、每次调用poll之前不需要手动设置文件描述符集2、poll将用户关系的实际和发生的实际进程分离3、支持的文件描述符数量理论上是无上限的,其实也有,因为一个进程能打开的文件数量是有上限的 ulimit -n 查看进程可打开的最大文件数
        1、poll 返回后,也需要轮询pollfd 来获取就绪的描述符2、同时连接的大量客户端,可能只有很少的处于就绪状态,因此随着监事的描述符数量的增长,其效率也会线性下降
    • select poll 的共同点
        都做了很多 无效的 轮询检测描述符是否就绪的操作
    • epoll
      • 代码
          #include <sys/epoll.h>int epoll_create(int size);     // 在内核里,一切皆文件。所以,epoll向内核注册了一个文件系统,//epoll_create的作用是创建一个epoll模型,该模型在底层建立了->//**红黑树,就绪队列,回调机制**//size可以被忽略,不做解释int epoll_ctl(int epfd, int op, int fd, struct epoll_events *event);//epfd:epoll_create()的返回值(epoll的句柄,本质上也是一个文件描述符)//op:表示动作,用三个宏来表示//    EPOLL_CTL_ADD:注册新的fd到epfd中//    EPOLL_CTL_MOD:修改已经注册的fd的监听事件//    EPOLL_CTL_DEL:从epfd中删除一个事件//fd:需要监听的文件描述符//event:具体需要在该文件描述符上监听的事件int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);//函数调用成功,返回文件描述符就绪的个数,也就是就绪队列中文件描述符的个数,//返回0表示超时,小于0表示出错//epoll_event结构体struct epoll_event{uint32_t events;  /* Epoll events */epoll_data_t data;/* User data variable */}__EPOLL_PACKED;//events可以是一堆宏的集合,这里介绍几个常用的//  EPOLLIN:表示对应的文件描述符可以读(包括对端socket正常关闭)//  EPOLLOUT:表示对应的文件描述符可以写//  EPOLLET:将EPOLL设为边缘触发(Edge Triggered)模式,//          默认情况下epoll为水平触发(Level Triggered)模式typedef union epoll_data{void *ptr;int fd;uint32_t u32;uint64_t u64;}epoll_data_t;//联合体里通常只需要填充fd就OK了,其他参数暂时可以不予理会
      • 工作原理
          1、创建一个epoll 对象,向epoll 对象中添加文件描述符以及我们所关心的在在该文件描述符上发生的事件2、通过epoll_ctl 向我们需要关心的文件描述符中注册事件(读,写,异常等),操作系统将该事件和对象的文件描述符作为一个节点插入到底层建立的红黑树中3、添加到文件描述符上的实际都会与网卡建立回调机制,也就是实际发生时会自主调用一个回调方法,将事件所在的文件描述符插入到就绪队列中4、引用程序调用epoll_wait 就可以直接从就绪队列中将所有就绪的文件描述符拿到,可以说时间复杂度O(1)
      • 水平触发工作方式(LT)
          处理socket时,即使一次没将数据读完,下次调用epoll_wait时该文件描述符也会就绪,可以继续读取数据
      • 边沿触发工作方式(ET)
          处理socket时没有一次将数据读完,那么下次再调用epoll_wait该文件描述符将不再显示就绪,除非有新数据写入在该工作方式,当一个文件描述符就绪是,我们要一次性的将数据读完
      • 隐患问题
          当我们调用read读取缓冲去数据时,如果已经读取完了,对端没有关系蟹段,read就会堵塞,影响后续逻辑解决方式就是讲文件描述符,设置成非堵塞的,当没有数据的时候,read也不会被堵塞,可以处理后续逻辑(读取其他的fd或者继续wait)ET 的性能要好与LT,因为epoll_wait返回的次数比较少,ninx中默认采用ET模式使用epoll
      • 特点
          1、采用了回调机制,与轮询区别看待2、底层采用红黑树结构管理已经注册的文件描述符3、采用就绪队列保存已经就绪的文件描述符
      • 优点
          1、文件描述符数目无上限:通过epoll_ctl 注册一个文件描述符后,底层采用红黑树结构管理所有需要监控的文件描述符2、基于实际的就绪通知方式:每当有文件描述符就绪时,该响应事件会调用回调方法将该文件描述符插入到就绪队列中,不需要内核每次去轮询式的查看每个被关心的文件描述符3、维护就绪队列:当文件描述符就绪的时候,就会被放到内核中的一个就绪队列中,调用epoll_wait可以直接从就绪队列中获取就绪的文件描述符,时间复杂度是O(1)

四、总结

nginx 通过 多进程 + io多路复用(epoll) 实现了高并发采用多个worker 进程实现对 多cpu 的利用通过eopll 对 多个文件描述符 事件回调机制和就绪描述符的处理 实现单线程io复用从而实现高并发 

转载于:https://www.cnblogs.com/xiaobaiskill/p/10969180.html

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

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

相关文章

转载:程序员从初级到中级10个秘诀

Justin James曾发表过一篇博文《10 tips for advancing from a beginner to an intermediate developer》&#xff0c;为我们分享如何才能完成程序员从初级到中级的蜕变&#xff0c;现将中文译文转载于此&#xff0c;供大家借鉴。 在一封与TechRepublic会员交流的邮件当中&…

ux设计工具_UX设计中的工具和实用主义

ux设计工具There’s a zillion tools for User Experience and User Interface Design. Don’t take my word for it: a simple Google search for “what are the best tools for wireframing” (to take just one aspect of UX) leads you to endless pages of “The 20 best…

幕后常驻嘉宾配音小姐姐的2021年度总结

大家好&#xff0c;我是若川。这是公众号幕后常驻嘉宾配音小姐姐&#xff0c;看完了上一个阿源小姐姐的年度总结《一张图看程序媛阿源的2021个人年度流水账》&#xff0c;写的年度总结投稿。点击以下音频可以查看收听往期更多音频。以下是正文~Hi&#xff0c;大家好呀~我是若川…

java spring cloud版b2b2c社交电商spring cloud分布式微服务:服务注册与发现(Eureka、Consul)...

Spring Cloud简介电子商务社交平台源码请加企鹅求求&#xff1a;一零三八七七四六二六。Spring Cloud是一个基于Spring Boot实现的云应用开发工具&#xff0c;它为基于JVM的云应用开发中涉及的配置管理、服务发现、断路器、智能路由、微代理、控制总线、全局锁、决策竞选、分布…

js 全选

<form name"frm" method"post" action"/add" onsubmit"return check()"><table class"titem" ><tr><td class"field"> * 所属批次</td><td class"value"><sele…

结果规格化_结果

结果规格化If you’ve seen an Instagram story involving a question and people tilting their heads, you probably were looking at the “Who Is More” Instagram filter. In this article, I will share the creative process and decision making behind this filter.如…

2021 年 JavaScript 大事记

大家好&#xff0c;我是 ConardLi&#xff0c;不知不觉中&#xff0c;2021 年已经接近尾声了&#xff0c;不知道在 2021 这一年&#xff0c;你收获了什么&#xff1f;又失去了什么呢&#xff1f;又到了开始做年终总结的时候了&#xff0c;今天&#xff0c;我来给 JavaScript 做…

java版spring cloud+spring boot+redis多租户社交电子商务平台 (十三)springboot集成spring cache...

电子商务社交平台源码请加企鹅求求&#xff1a;三五三六二四七二五九本文介绍如何在springboot中使用默认的spring cache&#xff0c;声明式缓存Spring 定义 CacheManager 和 Cache 接口用来统一不同的缓存技术。例如 JCache、 EhCache、 Hazelcast、 Guava、 Redis 等。在使用…

windows符号服务器地址

当调试windows程序的时候&#xff0c;有时候会需要一些符号文件。系统的公有符号文件微软都是提供的&#xff0c;只需在调试器中设置即可&#xff0c;在下次调试时&#xff0c;调试器会自动从网上下载需要的符号文件。可以使用符号文件的调试器有windbg等等。 符号服务器地址&a…

如何融入到更积极的环境,促进技术提升

众所周知&#xff0c;关注公众号可以了解学习掌握技术方向&#xff0c;学习优质好文&#xff0c;落实到自己项目中。还可以结交圈内好友&#xff0c;让自己融入到积极上进的技术氛围&#xff0c;促进自己的技术提升。话不多说&#xff0c;推荐这些优质前端公众号前端之神100w阅…

动画 制作_您希望制作的10个醒目的徽标动画

动画 制作重点 (Top highlight)标志设计 (Logo Design) Have you ever watched paint dry? No? I didn’t think so. How about watched a turtle crossing the road? Probably not. Maybe spent an hour standing in line at the post office? Well that’s pretty likely…

NOIP训练营集训笔记—信息学基础算法(倍增与分治算法

本文摘自清北OI学堂内部笔记&#xff0c;作者潘恺璠&#xff0c;来自柳铁一中曾参加过清北训练营提高组精英班&#xff0c;主要记录的是信息学基础算法。笔记非常详细&#xff0c;特分享给大家&#xff01; NOIP2019年夏令营正在报名中&#xff0c;6大校区10种班型&#xff0c;…

使用 CSS 用户选择控制选择

IE10 平台预览 4 包括一个新的 CSS 属性的支持-ms-user-select&#xff0c;这使得 Web 开发者控制完全可以选择什么的文本&#xff0c;在其网站上更容易。如果你是看我一整天都在我的工作站&#xff0c;您会注意到我读计算机上时&#xff0c;我选择的文本。我不是只有一个人读起…

一个在校的普通前端小姐姐的2021

大家好&#xff0c;我是若川。这是我的源码共读群里一个大三的前端小姐姐&#xff08;小曹同学&#xff09;的年度总结。她写了5篇源码笔记。同时做了很多项目&#xff0c;获得了很多奖。而且策划和建立了学校工作室的前端训练营&#xff0c;40人报名参加。总之就是现在的大学生…

按钮 交互_SwiftUI中的微交互—菜单按钮动画

按钮 交互Microinteractions have become increasingly important in a world with a dizzying number of digital platforms and an ocean of content. While microinteractions used to be considered an interesting resource in the early days of digital design, in toda…

JavaScript逻辑运算符的使用技巧

前言 !, &&, || 三个运算符是JavaScript中重要的逻辑运算符&#xff0c;本文将介绍这三个运算符在JavaScript实际编程中的有趣使用技巧。 取反运算符&#xff08;!&#xff09; 如果对一个值连续做两次取反运算&#xff0c;等于将其转为对应的布尔值&#xff0c;与Bool…

如何接触到最新的前端动态、最前沿的前端技术

众所周知&#xff0c;关注公众号可以了解学习掌握技术方向&#xff0c;学习优质好文&#xff0c;落实到自己项目中。还可以结交圈内好友&#xff0c;让自己融入到积极上进的技术氛围&#xff0c;促进自己的技术提升。话不多说&#xff0c;推荐这些优质前端公众号前端有道社区活…

选择控件— UI组件系列

重点 (Top highlight)The word “toggle” is a reference to a switch with a short handle that alternates between two states each time it is activated. You encounter it every time you “switch” on the lights.单词“ toggle”是指带有短手柄的开关&#xff0c;该开…

linux -- Linux diff与patch的深入分析

diff的输出格式分为传统格式和统一格式 1)diff的传统格式输出. ############################################ cat before.txt 输出: This is a line to be deleted This is a line that will be changed This is a line that will be unchanged cat after.txt 输出: This is …

shell命令之---sed

1. sed编辑器基础 1.1 替换标记 命令格式&#xff1a;s/pattern/replacement/flags $ cat data4.txt    This is a test of the test script.    This is the second test of the test script.    有4种可用的替换标记&#xff1a; 数字&#xff0c;表明新文本将替…