nginx的请求处理流程
nginx处理的流量是其他应用服务处理流量的数倍。
nginx的三个状态机:传输层状态机、http状态机、mail状态机。
核心部分由非阻塞的事件驱动处理引擎(epoll)实现,利用线程池处理阻塞的磁盘调用。最后通过应用层的协议,比如http、mail、stream、FastCGI等协议代理到响应的应用服务器。
nginx的进程结构
1.单进程结构
不适用于生产环境。
2.多进程结构:https://tengine.taobao.org/book/chapter_02.html#id1
利用服务器多核的特性,实现健壮的nginx。为什么不是多线程呢?因为nginx要保持他的高可用性,高可靠性。进程是资源调度的基本单位,线程之间是共享内存的,容易导致内存错误。
worker处理真正的请求,master主要是监控worker进程,master和worker之间通过signal进行通信。
缓存要被cachemanager、cacheloader、多个worker进程之间共享。
worker进程间通讯都是通过共享内存实现的,每个worker独占一颗cpu内核,减少缓存失效的命中率。
nginx重新启动配置文件: nginx -s reload
查看本地示例:
# 查看当前的nginx进程
➜ nginx ps -ef | grep nginx 501 13775 1 0 8:42下午 ?? 0:00.00 nginx: master process /Users/xiaohuochai/Desktop/nginx/sbin/nginx 501 13776 13775 0 8:42下午 ?? 0:00.00 nginx: worker process 501 13785 885 0 8:42下午 ttys005 0:00.00 grep --color=auto --exclude-dir=.bzr --exclude-dir=CVS --exclude-dir=.git --exclude-dir=.hg --exclude-dir=.svn nginx # 给父进程发送hup信号(其实结果是会reload
➜ nginx kill -SIGHUP 13775
# 查看hup之后的进程结果(worker进程们的pid变了,说明worker们 reload了 子进程无论是新启还是kill 都会给master发送信号 ➜ nginx ps -ef | grep nginx 501 13775 1 0 8:42下午 ?? 0:00.00 nginx: master process /Users/xiaohuochai/Desktop/nginx/sbin/nginx 501 13942 13775 0 8:46下午 ?? 0:00.00 nginx: worker process 501 13948 885 0 8:46下午 ttys005 0:00.00 grep --color=auto --exclude-dir=.bzr --exclude-dir=CVS --exclude-dir=.git --exclude-dir=.hg --exclude-dir=.svn nginx ➜ nginx
nginx使用信号管理父子进程
(进程间交换数据必须通过内核,常见的多进程间通信方式:共享内存、信号、匿名管道、有名管道、高级管道、消息队列、信号量、套接字)
master、worker、nginx命令行都可以发送信号,控制进程。
linux规定,当子进程终止的时候,会向父进程发送‘chld’ 信号。
master从worker接收到的消息,分别表示:
- TERM/INT 表示立刻停止nginx进程
- QUIT 优雅的停止nginx进程
- HUP 重载配置文件
- USR1 重新打开日志文件,做日志文件的切割
- USR2 热部署使用,见下面热部署流程
- WINCH 热部署使用
reload重载配置文件的真相
当配置文件发生改变的时候,都会nginx -s reload,在不影响现有业务不宕机的情况下,如何平滑的更新nginx配置。
老配置worker如果长时间不优雅退出,也不会影响新worker,但是会占用系统资源。nginx提供了一个配置shutdown_timeout,可以在worker进程timeout时候强制关闭。
热升级的完整流程
reload --重新加载,reload会重新加载配置文件,Nginx服务不会中断,master进程不会退出。而且reload时会测试conf语法等,如果出错会rollback用上一次正确配置文件保持正常运行。
restart --重启(先stop后start),会重启Nginx服务,新启master,关闭旧的master,如果配置文件出错会导致服务启动失败,那就是更长时间的服务中断了。
优雅的关闭worker进程
worker关闭连接池的所有连接后,才会退出进程。如果超时,所有链接会被强制关闭。
网络收发与nginx事件间的对应关系
wireshark和charles抓包有什么优缺点?
(感觉wireshark复杂一些??
nginx的事件驱动模型, 事件循环+分发
当有新事件到达时,push进事件队列,但是如果事件队列中的时间长时间得不到处理,引发恶性循环,大量的cpu计算资源都会消耗在不正常的事件。
epoll(网络事件收集器模型)的优劣以及原理
这张图比较了几种常见的事件处理模型,横轴是连接句柄也就是并发数量,纵轴是随着并发增加分发事件所消耗的时间,图中曲线代表着不同的事件分发模型的效率。
epoll的事件处理时间与句柄数增加几乎是无关的,适合做大并发连接的处理,在linux 2.5.44内核中被引入(深入浅出nodejs里面有讲
nginx的请求切换
传统web服务,如tomcat等,会在多进程间切换,切换的损耗很大。nginx的worker,只要在cpu分配的时间片内,就不会在多个process之间切换,在本进程时间片结束前处理多个请求。
同步&异步、阻塞&非阻塞之间的区别
阻塞和非阻塞,其实指的是底层的系统调用,是否会引起sleeping。非阻塞,即使时间片用完,也不会被切换掉。
如何在非阻塞情况下处理同步与异步?(开发效率与运行效率的平衡
nginx的模块究竟是什么?
nginx官方模块说明(modules reference):http://nginx.org/en/docs/
nginx有很多模块,编译的时候,可以选择需要哪些模块,比如压缩(ngx_http_gzip_filter_module)模块,然后这个模块就会被编译进nginx。
有些模块默认会被使用,有些模块需要手动配置才会被使用。
大模块也会有子模块,比如ngx_http_gzip_filter_module,就是http模块的子模块.
怎么知道该模块支持哪些命令呢?
进 /src/http/ngx_http_gzip_filter_module.c文件找到 ngx_command_t 结构体片段,ngx_command_t是一个数组,数组中列出了该模块支持的指令名和参数。
// ngx_http_gzip_filter_module 的 ngx_command_t 说明该模块有那些指令 static ngx_command_t ngx_http_gzip_filter_commands[] = { { ngx_string("gzip"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF |NGX_CONF_FLAG, ngx_conf_set_flag_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_gzip_conf_t, enable), NULL }, { ngx_string("gzip_buffers"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2, ngx_conf_set_bufs_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_gzip_conf_t, bufs), NULL }, { ngx_string("gzip_types"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, ngx_http_types_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_gzip_conf_t, types_keys), &ngx_http_html_default_types[0] }, { ngx_string("gzip_comp_level"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_conf_set_num_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_gzip_conf_t, level), &ngx_http_gzip_comp_level_bounds }, { ngx_string("gzip_window"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_conf_set_size_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_gzip_conf_t, wbits), &ngx_http_gzip_window_p }, { ngx_string("gzip_hash"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_conf_set_size_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_gzip_conf_t, memlevel), &ngx_http_gzip_hash_p }, ngx_null_command }; static ngx_http_module_t ngx_http_gzip_filter_module_ctx = { ngx_http_gzip_add_variables, /* preconfiguration */ ngx_http_gzip_filter_init, /* postconfiguration */ NULL, /* create main configuration */ NULL, /* init main configuration */ NULL, /* create server configuration */ NULL, /* merge server configuration */ ngx_http_gzip_create_conf, /* create location configuration */ ngx_http_gzip_merge_conf /* merge location configuration */ }; // ngx_http_gzip_filter_module 的 ngx_module_t 说明该模块 ngx_module_t ngx_http_gzip_filter_module = { NGX_MODULE_V1, &ngx_http_gzip_filter_module_ctx, /* module context */ ngx_http_gzip_filter_commands, /* module directives */ NGX_HTTP_MODULE, /* module type 属于哪一个类型的模块 */
NULL, /* init master */ NULL, /* init module */ NULL, /* init process */ NULL, /* init thread */ NULL, /* exit thread */ NULL, /* exit process */ NULL, /* exit master */ NGX_MODULE_V1_PADDING };
模块分类,下图中包含6个大模块,每个大模块又包含很多子模块,比如响应过滤模块可以对响应做二次处理压缩等、upstream模块,当nginx代理请求时,将请求传递给相关的上有服务处理时,就需要upstream模块
现在来看看nginx源码里面子模块的位置:
http下子模块,modules里面放的是一些非必须的模块:
nginx如何通过连接池处理网络请求
连接和事件是怎么对应在一起的?
每一个worker进程下面都会有 ngx_cycle_t 这个结构体(源码里面都可以找到),里面定义了预留connection的数量(http://nginx.org/en/docs/ngx_core_module.html#worker_connections),每个connection会自动对应到客户端上下游的连接,比如一个read_event | write_event,所以read_events 和 write_events的数量和connections一样,index也是对应的。
每个connection所占用的内存资源,根据操作系统位数不同而有差异。在64位操作系统中,每个ngx_connection_s大约占用232字节,再加上两个读事件和写事件,一共是232B + 96*2B.
假设一个worker进程连接池预留512个连接,name初始化时候大概需要 512*(232B + 96*2B)=212KB。当然,所有这些配置都是可配的。
内存池对性能的影响
上一节中讲到了 ngx_connection_s 这个结构体,里面有一个变量叫做“ngx_pool_t *pool;”,由connection_pool_size指定,指的就是这个连接所要使用的内存池,默认size是256|512(http://nginx.org/en/docs/http/ngx_http_core_module.html#connection_pool_size)。
请求内存池,默认size是4K(http://nginx.org/en/docs/http/ngx_http_core_module.html#request_pool_size),因为请求需要保存大量的上下文信息,比如url、header等所以预分配内存较大。
所有worker进程协同工作的关键
worker进程之间只能通过共享内存进行数据共享。
用好共享内存的工具slab管理器
nginx容器,下面重点说一下哈希表
哈希表的max_size与bucket_size如何配置
nginx的哈希表一般用来存放静态的不变的内容,一旦确定,就不会有插入和删除操作。上图右边树图列出了部分使用哈希表的模块,
nginx中最常用的容器--红黑树
nginx进程间内存共享时,经常使用红黑树进行对象管理。
红黑树是一个查找二叉树,左子节点都小于右子节点,高度差不会太大,
红黑树优点:
部分使用红黑树的模块:
使用动态模块提升运维效率