鼎鼎知识库
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

15架构.md 10KB

关于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采用Masterworker的架构模型?

为什么味worker进程的数量要和CPU核数匹配?

Nginx请求处理流程

--流量来自:web, email, tcp
--处理引擎:非阻塞,事件驱动,需要状态机
	传输层状态机
	HTTP状态机
	MAIL状态机
--线程池:处理磁盘阻塞调用
	静态资源,磁盘缓存
	Access日志
	Error错误日志
--通过HTTP, Mail等协议与上游服务器交互

进程结构。单进程结构适合开发环境,多进程结构适合生产环境,默认是多进程结构。如果用多线程,由于线程之间共享同一个地址空间,一旦一个模块的某个线程出现问题,就会影响其它线程,这样达不到高可靠高可用。NginxMaster进程用来管理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连接的报文,处理完三次握手以后,操作系统通知epollwait方法,唤醒Nginxworker进程。操作系统内核会把事件放在事件队列中,从事件中获取到需要处理的事件(比如建立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容器是实现高级功能的基础。

--数组:多块连续内存。
--链表
--队列
--哈希表
--红黑树
--基数树

使用动态模块提升运维效率。