nginx是为了解决C10K问题。 同时处理10000个连接数的web服务器
nginx的优点
rps 每秒请求数 可以达到百万级别
热部署
client请求先打到master,然后分发给多个work,如何分发,work争抢。
每个work是一个独立的进程,当reload时,有请求的work先不退出,先退出重启那些没有请求连接的work,老的work仍然处理着请求。
发送一个请求,占用work的几个连接数?
2个(静态资源)或者4个(反向代理后端服务器)

https 中的TLS (SSL)保证了http的明文加密

nginx为什么多进程而不是多线程?
多线程的时候,线程之间是共享同一个地址空间的,可能会引起进程退出,为了高可靠性。
master进程监听新连接的到来,并让其中一个worker进程accept。这里需要处理惊群效应问题,详见nginx的accept_mutex设计
worker进程accept到fd之后,把fd注册到到本进程的epoll句柄里面,由本进程处理这个fd的后续读写事件
worker进程根据自身负载情况,选择性地不去accept新fd,从而实现负载均衡
优点:
进程挂掉不会影响这个服务,是由worker主动实现负载均衡的,这种负载均衡方式比由master来处理更简单
缺点:
多进程模型编程比较复杂,进程间同步没有线程那么简单,进程的开销比线程更多
kill -SIGHUP 9170 向master进程发送HUP信号 ,等价于nginx -s reload ,重启work进程
kill -SIGTERM work进程, work进程退出, 会重启该work进程
linux中,当子进程终止的时候,会向父进程发送CHLD信号,如果work进程由于某些模块bug
意外的终止掉,master进程可通过CHLD发现,然后重启该work进程。
QUIT:优雅的停止nginx进程,不会像tcp直接立刻结束连接,tcp reset 这种报文。
HUP : 重载配置文件
USR1: 重新打开日志文件,做日志分隔


work_shutdown_timeout =10s 最长等10s 老的work进程还没处理完,就会自动退出

热升级,老的master进程 和新的master进程并存,方便回滚,随时唤醒老的master进程。
优雅的关闭主要针对 http请求。对于tcp/udp请求,nginx也没办法获取报文信息,来知道是否处理完
如果直接关闭,那客户端会收到请求错误,我们要让用户无感知。


建立关闭连接都是读事件,当请求数据,nginx要封装数据到报文,进行网络传输,为写事件。


nginx 是不允许模块长时间使用cpu,这样会导致事件队列中的事件不能及时处理,超时报错,例如gzip,分段使用cpu,而不是一直使用。
nginx如何从kernel中快速获取到 等待处理的事件?
当有100W并发连接数时,其实每次处理的活跃连接数是有限的,poll select模型会让内核把100W个请求都遍历一遍,让操作系统判断哪些连接上面有事件进来的。然后取出活跃连接数事件来处理。
epoll是维护了一个链表结构,里面存活跃连接。获取连接,从链表获取,从内核态读取到用户态
新建,修改,删除事件,往红黑树里,log(n)复杂度
长、短连接
1.长连接是指在一个TCP连接上,可以重用多次发送数据包,在TCP连接保持期间,如果没有数据包发送,需要双方发检测包以维持此连接。
2.半开连接的处理:当客户端与服务器建立起正常的TCP连接后,如果客户主机掉线(网线断开)、电源掉电、或系统崩溃,服务器将永远不会知道。长连接中间件,需要处理这个细节。linux默认配置2小时,可以配置修改。
3.短连接是指通信双方有数据交互时,就建立一个TCP连接,数据发送完成后,则断开此TCP连接。但是每次建立连接需要三次握手、断开连接需要四次挥手。
关闭连接最好由客户端主动发起,TIME_WAIT这个状态最好不要在服务器端,减少占用资源。
并发连接数:=活跃连接数+非活跃连接数。所有建立的TCP连接数量。网络服务器能并行管理的连接数。
活跃连接数:所有ESTABLISHED状态的TCP连接。

IO模型是用来管理 fd (文件描述符)。
IO复用(select poll epoll )
select 模型:把要管理的fd放到一个数据里,循环这个数据。数组大小1024,可管理连接有限。poll 与select类似,只是把数组类型改为链表,没有1024大小限制。
而epoll 为 event poll,只会管理有事件发生的 fd,也就是只会处理活跃的连接。epoll通过内核和用户空间共享一块mmap()文件映射内存来实现的消息传递。
长连接超时
MaxKeepAliveRequests 100 (一个连接最大的请求数到100的时候,断开连接)
KeepAliveTimeout 15 (一个连接到了15秒之后,断开连接)
一个线程处理多个请求,在用户态完成多个请求的切换,而不是操作系统传统的 一个线程处理一个请求,进行线程间请求切换。减少CPU上下文切换时间。
阻塞:操作系统、底层的C库,提供的方法,或者是系统调用,这个方法,可能会导致进程进入sleep 状态,操作系统把cpu切换给另一个进程用了。
阻塞调用会引起 进程间的主动切换,而nginx处理的并发连接太多了,是不容忍进程间切换的

nginx一般采用 同步非阻塞 模式
nginx如何通过连接池处理网络请求呢?
work_connections 设置的这个数组的大小,和下面的连个数组大小一样,每一个连接对应事件数组
每一个connection是一个结构体,也就是说每一个连接都会使用这个结构体。对应两个事件。
连接内存池:每一个tcp连接都要往连接内存池申请资源,对于长连接即keep alive tcp的连接,连接内存池用后不释放,除非关闭连接才会释放。默认是256K字节512字节。默认为每个请求分配的内存,不是代表只能用这么多,当不够了,会自己分配。只是默认值,减少了分配次数。
请求内存池:一般是4K。给header url 里的东西申请的内存


nginx进程间通讯,开辟一块共享内存,多个work都能使用它,但是多个work进程同时操作一块内存,就产会竞争关系,引入锁。早起nginx锁 一般会引起进程休眠状态,产生主动切换,work1给这块内存加锁,work2过来会休眠等待。现在 nginx采用了自选锁,即work2进程不会休眠,会一直请求这块内存。
所以nginx要求所有模块必须快速的使用共享内存,快速的取得锁,快速的释放锁。
做集群的流控,就要在共享内存中,而不是在单独的某个work上。

nginx_lua_shared 中同时使用了红黑树和单链表。红黑树 插入,key value 快。

哈希表中存不变的静态的数据。
共享内存上经常使用红黑树管理多个对象,实际在nginx内存中也会大量使用红黑树,






经过relip模块,nginx会把 x-real-ip 的内容 赋予给remote_addr.这样就拿到了最原始的IP
这样为后续的连接限制(limit_conn)模块,才有意义。








日志缓存,当内存中的日志达到buffer_size,在往磁盘里写入,避免了频繁IO。
buffer大小默认为64K

