关于CPU
--查看信息: cat /proc/cpuinfo | grep name | sort | uniq
--查看核数: cat /proc/cpuinfo | grep "physical id"
--直接查看:lscpu
CPU(s): 2 2个逻辑CPU
On-line CPU(s) list: 0,1
Thread(s) per core: 2
Core(s) per socket: 1 1核
Socket(s): 1 1个CPU
为什么Nginx
采用Master
和worker
的架构模型?
为什么味worker
进程的数量要和CPU
核数匹配?
Nginx
请求处理流程
--流量来自:web, email, tcp
--处理引擎:非阻塞,事件驱动,需要状态机
传输层状态机
HTTP状态机
MAIL状态机
--线程池:处理磁盘阻塞调用
静态资源,磁盘缓存
Access日志
Error错误日志
--通过HTTP, Mail等协议与上游服务器交互
进程结构。单进程结构适合开发环境,多进程结构适合生产环境,默认是多进程结构。如果用多线程,由于线程之间共享同一个地址空间,一旦一个模块的某个线程出现问题,就会影响其它线程,这样达不到高可靠高可用。Nginx
的Master
进程用来管理Worker
进程,监控每隔Worker
进程是否在正常工作。而Worker
进程用来处理真正的请求。另外Cache Loader
进程用来处理缓存的载入,Cache Manager
进程用来缓存管理。进程间的通讯通过共享内存来解决。Nginx
的设计中,Worker
进程可以多个,但原则是希望一个Worker
进程从头到尾占据一个CPU
。
进程结构演示。本质上当调用nginx -s reload
类似的语句,本质上是向Master
进程发送信号。
--查看Nginx进程:ps -ef | grep nginx
当前进程id, 父进程id
--重载:nginx -s reload
内部会重新启动worker进程和缓存进程
--再次查看进程: ps -ef | grep nginx
worker process, cache manageer process父进程的子进程id发生改变
root 9170 1 master process
nobody 16958 9170 worker process
nobody 16959 9170 worker process
nobody 16960 9170 cache manager process
--删除master进程下的当前进程,重新生成其下的子进程: kill -SIGHUP 9170
--删除某个worker进程,重新生成一个worker进程:kill -SIGTERM 16958
Master
进程信号
--监控worker进程的CHLD信号:当Worker进程挂掉,会向Master进程发送CHLD信号,Master进程就会重新启动该Worker进程
--接受信号
TEM, INT: 立刻停止Worker进程
QUIT:优雅退出Worker进程,不会向客户发出停止提醒
HUP:重新打开配置文件
USR1:重新打开日志文件
USR2:只能配合KILL使用
WINCH:只能配合KILL使用
Worker
进程信号。通常不对Worker
进程直接操作,由Master
进程来操作。
TERM, INT:
QUIT:
USR1:
WINCH:
Nginx
命令行。当启动Nginx
之后,会把Master
进程的pid
记录到一个文件中,默认记录到/etc/nginx/logs
目录下的nginx_pid
文件。执行命令行的效果和执行KILL
的效果是等效的。
reload:HUP
reopen:USR1
stop:TEM
quit:QUIT
使用nginx -s reload
的背后
--向master进程发送HUP信号
--master进程校配置语法是否正确
--master进程打开新的监听端口
--master进程用新配置启动新的worker子进程
--master进程向老的worker子进程发送QUIT信号,优雅关闭保证平滑
--老worker进程关闭监听句柄,处理完当前连接后结束进程
热升级的流程。在进行热升级的时候,老的进程可能会出现一直推不掉,新的Worker
进程同时存在问题,这时候需要考虑回滚。
--备份旧的Nginx文件:
--新的Nginx文件替换旧的Nginx文件:只替换binary文件,新的配置文件目录,日志文件目录和老的保持一致。在新版本的Linux中,如果一个文件正在使用,需要用-f进行强制替换。
--向master进程发送USR2信号:只能通过KILL USR2 pid, Nginx暂未支持命令行
--master进程修改pid文件名,加后缀.oldbin
--master进程用新的Nginx文件启动新的master进程:这里会同时出现两个master进程,新的master进程起新的worker进程
--向老的master进程发送QUIT信号,关闭老master进程:老的master进程会优雅关闭老的worker进程
--如果回滚:因为老的master进程还存在,就向老的master进程发送HUP信号,向新的master发送QUIT信号
优雅地关闭worker
进程。worker
进程处理请求,识别当前的连接没有处理请求,再将连接关闭。优雅地关闭主要针对的是HTTP
请求,而对于TCP
、长连接等是做不到优雅关闭的。
--设置定时器:worker_shutdown_timeout:在Nginx.conf中配置。当nginx -s reload内部会有一个有关优雅关闭的表示位
--关闭监听句柄:worker进程不会再处理新的连接
--关闭空闲连接:大多数时候,这些空闲连接在线程池中
--在循环中等待全部连接关闭:Nginx不是主动关闭,这部分时间可能比较长。判断标志位,如果一个连接没有请求,就关闭
--退出进程
网络收发与Nginx
事件关系。事件主要指的是网络事件。Nginx
的每个连接对应两个事件,一个读事件,一个写事件。客户端向Nginx
发送请求的过程如下:
--客户端
-应用层:发送GET请求
-传输层:记录浏览器和Nginx的端口
-网络层:记录浏览器和Nginx的IP
-链路层:以太网
--客户端路由器:运营商的IP
--Nginx端路由器:
--Nginx端
-链路层:
-网络层:IP
-传输层:端口
-应用层:Nginx开始处理
TCP/IP
协议层级。每次Nginx
收到一个报文,就会分类成各种事件(报文对应Nginx
的各种网络事件),这些事件可以看作是事件发布者,另外在Nginx
中有事件的订阅者。也就是,所有的读和写都有对应的事件发布者和订阅者,Nginx
内部机制中对这些事件进行集中或分发,也可以把这套机制看成是中介者模式。
--数据链路层:HEADER里的源MAC地址,目的MAC地址。FOOTER里的Ethernet, ADSL, Wifi, PPP
--网络层:Nginx的IP, 浏览器的IP
--传输层:包含TCP, UDP协议,目的端口,源端口
--应用层:
网络事件实例。
--浏览器访问:ip:8080
--Wireshark
--对Nginx所在IP进行抓包,对8080端口抓包
-TCP层:解决进程通讯问题。Source Port, Destination Port
-HTTP层:解决机器和机器通讯问题。Source, Destination
-浏览器发送SYN
-服务器发送SYN,进行确认,此时Nginx还没有参与进来
-浏览器再次发送ACK,Nginx开始工作,Nginx把报文看作是读事件,建立连接的事件,Nginx调用accept方法
Nginx
如何处理事件?Nginx
打开80或443端口,等待新的事件进来,新的客户端连上Nginx
,客户端发起连接,对应epoll
中的wait
方法,Nginx
处于sleep
进程状态。当操作系统接受到一个建立TCP
连接的报文,处理完三次握手以后,操作系统通知epoll
的wait
方法,唤醒Nginx
的worker
进程。操作系统内核会把事件放在事件队列中,从事件中获取到需要处理的事件(比如建立TCP连接事件,比如收到TCP请求报文),同时还会产生新的事件放到队列中。如果有些第三方模块消耗很长的CPU计算任务,会导致事件队列到了时间也得不到处理。
Nginx
有事件分发机制,会从操作系统的kernel
中获取到等待处理的事件。Nginx
使用epoll
网络事件搜集器模型。epoll
在处理高并发连接中,每次处理活跃连接数占比很小。epoll
每次取活跃连接的时候,遍历链表,这个链表里仅仅只有活跃的连接。当Nginx
收到80端口建立连接的请求,添加一个读事件用来读取报文消息,与此同时,添加一个写事件,放到红黑树中,保证插入效率是logn
.如果不想处理事件,只需要从红黑树中移除一个结点就可以。当读取一个事件,链表中的事件就没了。当操作系统接收到网卡中的报文,链表增加一个新的元素。
请求切换时事件处理框架如何处理?传统web
服务器在处理高并发,需要进行进程切换,这样成本很高。Nginx
要么一线程处理一连接,要么一线程同时处理多个连接,在用户态代码完成连接切换,减少OS进程切换。
阻塞和非阻塞,主要是指操作系统,或者底层的C库提供的方法或系统调用,如果方法导致进程进入sleep
状态(当前条件不满足),操作系统主动切换为另外一个进程,这样是一个阻塞方法;非阻塞方法,永远不会当时间片未用完之时把进程主动切换掉。
同步和异步,主要是从调用方式而言,Nginx
官方使用javascript
同步的方式实现非阻塞效果,OpenResty
基于lua
语言同同步代码方式实现非阻塞高并发效果。
阻塞调用,以accept
方法为例。调用的时候,如果监听端口所对应的队列,操作系统已经建立了三次握手成功之后的socket
,阻塞方法可能会立刻得到返回。但是,如果队列是空的,操作系统会等待新的三次握手连接到达内核中。Nginx
使用非阻塞调用。
Nginx
的模块。需要具备的特点:
Nginx
如何查看第三方模块的指令呢?如果通过二进制安装,进入objs/ngx_modules.c
文件,可以查看所有被编译进Nginx
中的模块。然后找到ngx_command_t
,可以找出所有的模块指令。
模块分类。
NGX_CORE_MODULE
events
NGX_EVENT_MODULE
event_core
epoll
http
NGX_HTTP_MODULE
ngx_http_core_module
请求处理模块
响应过滤模块
upstream模块
mail
NGX_MAIL_MODULE
stream
NGX_STREAM_MODULE
config
NGX_CONF_MODULE
连接池增加对资源的利用率。当处理高并发,需要把connection
的数目配置足够大。
内存池对性能的影响。对第三方模块开发有意义。
进程间会共享内存。进程间通讯的第一种方式是通过信号。第二种方式涉及数据共享,用共享内存。既然是共享,需要考虑锁。还有一种使用slab
进行内存管理。
Nginx
容器是实现高级功能的基础。
--数组:多块连续内存。
--链表
--队列
--哈希表
--红黑树
--基数树
使用动态模块提升运维效率。