0%

ngx_listening_t相关

关于ngx_listening_t的一些记录

顺序随机

1. ngx_init_cycle() :

处理继承,申请ls存储数组的空间,

1.从旧nginx继承过来ls个数,设置新的nginx监听端口的最大个数,默认为最多10
2.申请ls的存储空间,并设置ls数组的一些性质:nelts, size, nalloc, pool
3.判断ls的一些属性是从旧nginx继承还是重新设置

如果继承,受影响的属性有:

  • remain = 0:设置不保留旧的ls,等继承过来后如果原fd关掉后就全部销毁
  • ignore: 根据旧的nginx_ls判断是否继承ls_fd,就是不再重新打开新的,直接拿过来用。
  • 如果新旧相应ls的sockaddr一致,就继承这些属性:fd,nls.previous,nls.listen=1(该fd已监听),deferred_accep

如果重新设置:

  • open = 1, accept_filter(?), deferred_accept(?)

4.打开listening_socket, ngx_open_listening_sockets()
5.配置listening_socket, ngx_configure_listening_sockets()
6.关闭不用的ls_fd

使用remain和ls.fd判断是否需要关闭

2.ngx_open_listening_sockets(ngx_cycle_t *cycle):

打开监听文件描述符,设置重用和非阻塞等基础属性

  1. 如果ignore==1或者fd != -1或者inherited==1就跳过,说明不需要再打开该fd(可能是需要关闭或者已经打开了的)。
  2. 调用ngx_socket()打开ls_fd
  3. 设置端口重用: setsocketopt(s, SOL_SOCKET, SO_REUSEADDR, (const void *)&reuseaddr, sizeof(int))
  4. 不适用AIO_EVENT时设置fd为非阻塞:ngx_nonblocking(s)
  5. bind(s, ls[i].sockaddr, ls[i].socklen)
  6. listen(s, ls[i].backlog), 后来还会重新调整backlog
  7. 设置ls[i].listen = 1;
  8. 会重试5次,每次间隔500ms

3.ngx_configure_listening_sockets(ngx_cycle_t *cycle)

配置监听文件描述符的一些附加属性

  1. 配置日志文件: ls[i].log = *ls[i].logp;
  2. set rcvbuf
  3. set sndbuf
  4. set keepalive
  5. 调整backlog,
  6. 调整deferred_accept,根据SO_ACCEPTFILTER和TCP_DEFER_ACCEPT来判断不同系统的处理方式

4.ngx_close_listening_sockets(ngx_cycle_t *cycle)

关闭ls.fd连接上的事件,关闭文件描述符

  1. set ngx_accept_mutex_held = 0; ngx_use_accept_mutex = 0;
  2. 如果ls上的connection上的读事件还活跃,就先删除事件,再删除该连接,并置c->fd = -1
  3. ngx_close_socket(ls[i].fd)
  4. ls[i].fd = -1;
  5. 最后,set cycle->listening.nelts = 0

5.ngx_http_optimize_servers()

组织监听端口信息,初始化监听

  1. 调用ngx_http_init_listening()

6.ngx_http_init_listening()

初始化listening

  1. 循环添加ls,ngx_http_add_listening()
  2. 申请hport存储空间

7.ngx_http_add_listening()

设置ls的初始操作句柄

  1. 创建一个ls, ngx_create_listening()
  2. set ls->addr_ntop = 1;
  3. ***set ls->handler = ngx_http_init_connection, 非常重要 ***
  4. 设置poll_size, post_accept_timeout, logp, log.data, log.handler, backlog, rcvbuf, sndbuf, keepalive, accept_filter,deferred_accept,

8.ngx_create_listening(ngx_conf_t *cf, void *sockaddr, socklen_t socklen)

创建ls的存储空间

  1. 向cf->cycle->listening数组中添加一个ls
  2. 申请ls的存储空间
  3. 配置该ls的地址的相关信息:

sockaddr, socklen, addr_text.len, addr_text.data, fd=-1, type = SOCK_STREAM, backlog

9.ngx_http_init_connection()

http处理框架的第一个事件返回句柄,初始化http连接

  1. 申请ngx_http_connection_t的存储空间(hc,代表一个address:port组合)
  2. set c->data = hc, 该c是建立tcp连接后的ngx_connection_t
  3. 根据address:port的个数不同做不同的处理
  4. 配置hc的相应属性:addr_conf
  5. set hc->conf_ctx默认配置
  6. set ngx_http_log_ctx_t的相关属性,http的日志记录属性:connection = c;request=NULL; current_request = NULL;
  7. set c->log的相关性质:connection = c->number; handler = ngx_http_log_error; data = ctx; action = "waiting for request";
  8. set log_error = NGX_ERROR_INFO;
  9. 配置读事件为ngx_http_wait_request_handler
  10. 配置写事件为ngx_http_empty_handler
  11. 检查是否支持SPDY或HTTP_SSL协议,需要设置对应的读事件
  12. 检查事件是否已经准备好了,为什么在这里就可以检查?因为是event_accept调用的http_init_connection,这时候如果是deferred accept()或者,rtsig, aio, iocp事件模型,就会暂缓调用rev->handler,也就是ngx_http_wait_request_handler,等待真正发送过来数据之后才会调用该handler,而不是发现syn过来就开始创建。
  13. 该事件还没准备好的时候,就把它添加到定时器的红黑树中,
  14. 设置该连接可重用,也就是可以再次被使用???
  15. 将该读事件添加到epoll框架中

10.ngx_event_process_init(ngx_cycle_t *cycle)

初始化事件驱动框架,在这里会把ls的read->handler都设置为ngx_event_accept,并添加到epoll框架中等待事情发生,发生后就会调用ls->handler,也就是在http框架初始化阶段设置的ngx_http_init_connection()函数。cycle中的一些connection被拿来用做ls的专属连接来用,而不是分配给每个worker进程全部用作http连接(????)

  1. 使用ls.fd新建connection,利用此connection的一些位置存储信息(在ngx_event_accept中会使用到)
  2. set c->log = &ls[i].log;
  3. set c->listening = &ls[i];
  4. set ls[i].connection = c;
  5. set c->read->rev->log = c->log;
  6. set c->read->rev->accept = 1;表示该连接用于accept
  7. set c->read->rev->deferred_accept为ls[i]的deferred_accept
  8. set c->read->rev->handler = ngx_event_accept; 非常重要
  9. 将c->read->rev事件添加到epoll中去处理

11.ngx_event_accept(ngx_event_t *ev)

  1. 判断该事件是否超时,如果超时,就重新启用该事件ngx_enable_accept_events(),并设置ev->timeout = 0
  2. 获取配置信息
  3. 根据使用的事件处理框架来判断ev->available,可以用来连续建立连接而不返回,以此加快建立连接的数量,尽可能多的让客户端连上来。
  4. 获取附在ls上的连接c的信息(在ngx_event_process_init中设置的):listening结构体和ls_fd
  5. set ev->ready = 0;
  6. s = accept()
  7. 处理accept()各种错误
  8. set ngx_accept_disabled,用于负载均衡
  9. 用s去get新连接c
  10. 设置该新连接c的各种属性:申请c->pool存储空间, c->sockaddr, 申请c->log存储空间
  11. 设置该s为非阻塞
  12. 设置c->log为&ls->log
  13. set 该连接的读写方法c->recv = ngx_recv;....
  14. set c->pool->log = &ls->log
  15. set c->socklen = socklen; c->listening = ls; c->local_sockaddr = ls->sockaddr; c->unexpected_eof = 1;最后表示不希望没有数据,这是刚开始建立连接,当然需要读取数据
  16. set c->write->ready = 1
  17. 如果该连接事件是延迟连接的,就把rev->ready设为1,以通知http_init_connection,该连接准备好了。因为如果设置了延迟连接,则在accept的时候就已经有数据发过来了,那样http_init_connection就可以直接进行处理了
  18. set c->read->log = log; c->write->log = log;
  19. ngx_connection_counter+1
  20. 处理该连接的文本地址信息
  21. 如果没用epoll,可以调用ngx_add_conn把读写事件都添加到事件处理机制中
  22. 调用 ls->handler(c);也就是http_init_connection来处理

12.ngx_enable_accept_events(ngx_cycle_t *cycle)

重新激活该连接事件

  1. 循环判断该连接事件的读事件是否活跃,if(c->read->active)
  2. 不活跃的就把c->read再添加到epoll中

13.ngx_master_process_cycle(ngx_cycle_t *cycle)

14.ngx_add_inkherited_sockets(ngx_cycle_t *cycle)

从环境变量中恢复旧nginx程序的监听端口,获取后会调用ngx_set_inherited_sockets()进行相应配置,比如说

从fd获取缓冲区大小等,配置ls相应的值

15.ngx_set_inherited_sockets(ngx_cycle_t *cycle)

见14

关于listening从建立监听到处理http请求的调用链

main():nginx.c–>
ngx_init_cycle(ngx_cycle_t *old_cycle):ngx_cycle.c–>
ngx_conf_parse(ngx_conf_t *cf, ngx_str_t *filename):ngx_conf_file.c–>
ngx_conf_handler(ngx_conf_t *cf, ngx_int_t last):ngx_conf_file.c–>
cmd->set():ngx_conf_file.c___ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf):ngx_http.c–>
ngx_http_optimize_servers(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf, ngx_array_t *ports):ngx_http.c–>
ngx_http_init_listening(ngx_conf_t *cf, ngx_http_conf_port_t *port):ngx_http.c–>
ngx_http_add_listening(ngx_conf_t *cf, ngx_http_conf_addr_t *addr):ngx_http.c–>
ngx_create_listening(ngx_conf_t *cf, void *sockaddr, socklen_t socklen):ngx_connection.c–>
main():nginx.c–>
ngx_master_process_cycle(ngx_cycle_t *cycle):ngx_process_cycle.c–>
ngx_start_worker_processes(ngx_cycle_t *cycle, ngx_int_t n, ngx_int_t type):ngx_process_cycle.c–>
ngx_spawn_process(ngx_cycle_t *cycle, ngx_spawn_proc_pt proc, void *data, char *name, ngx_int_t respawn):ngx_process.c–>
ngx_worker_process_cycle(ngx_cycle_t *cycle, void *data):ngx_process_cycle.c–>
ngx_worker_process_init(ngx_cycle_t *cycle, ngx_int_t worker):ngx_process_cycle.c–>
ngx_modules[i]->init_process(cycle):ngx_process_cycle.c___ngx_event_process_init(ngx_cycle_t *cycle):ngx_event.c–>
ls->c->read->handler==event_accept()___ngx_event_accept(ngx_connection_t *c, ngx_cycle_t *cycle):ngx_event_accept.c–>
ls->handler(c)==ngx_http_init_connection(ngx_connection_t *c):ngx_http_request.c–>
ngx_http_wait_request_handler(ngx_event_t *rev):ngx_http_request.c

在nginx中,所有的事件处理入口都是ev,ev带有不同的连接信息,可以针对不同的对象进行处理