搭错车的小火柴

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

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进程间内存共享时,经常使用红黑树进行对象管理。

红黑树是一个查找二叉树,左子节点都小于右子节点,高度差不会太大,

 

红黑树优点:

 

部分使用红黑树的模块:

 

 

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

 

posted on 2020-08-03 14:36  搭错车的小火柴  阅读(219)  评论(0编辑  收藏  举报