您的位置:首页 > 运维架构 > Nginx

nginx 代码分析listen 和request请求的流程

2017-04-28 15:05 274 查看
listen 针对于server的端口进行监听,server的配置可以比较复杂如:

server {
listen       192.168.8.81:80;
server_name  vae.test1.com *.test1.com;<!--hosts文件配置的地址-->
index index.html;
location /test1 {
root /usr/local/nginx/html/;
}
}
server {
listen       127.0.0.1:80;
server_name  vae.test1.com *.test1.com;<!--hosts文件配置的地址-->
index index.html;
location /test1 {
root /usr/local/nginx/html/;
}
}

server {
listen       80;
server_name  vae.test1.com;<!--hosts文件配置的地址-->
index index.html;
location /test1 {
root /usr/local/nginx/html/;
}
}
server {
listen       80;
server_name  vae.test2.com;
index index.html;
location /test2 {
root /usr/local/nginx/html/;
}
}
server {
listen       8081;
server_name  vae.test3.com;
index index.html;
location /test3 {
root /usr/local/nginx/html/;
}
}

//完成之后需要重新启动下nginx命令:nginx -s reload,每次修改nginx.conf文件都需要重启


host配置:

#   127.0.0.1       localhost
#   ::1             localhost
192.168.8.81 vae.test1.com
192.168.8.81 vae.test2.com
192.168.8.81 vae.test3.com


ngx_http_add_listen 函数将解析http中的server的地址和端口,以port为粒度,添加addr(即1.1.1.1:80、2.2.2.2:80和80,会放在相同的cmcf->port[i]中)。

时序图和数据结构关系图:



那么在cmcf中的存储例子:



然后,就是在ngx_http_optimize_servers 中优化后加入cf->cycle->listening中(待完善)

接着在ngx_init_cycle –> ngx_open_listening_sockets中打开和监听所有的端口创建socket

在启动过程中的 ngx_worker_process_init中调用ngx_modules[event]->init_process(cycle) 初始化connections 并设置事件的回调 ,以ngx_epoll_moudle为例:

ngx_event_process_init中 listen流程

/* 初始化用来管理所有定时器的红黑树,用于事件的处理轮询 */
if (ngx_event_timer_init(cycle->log) == NGX_ERROR) {
return NGX_ERROR;
}
....
//分配和初始化cycle->connections,设置每个c的读写事件event
cycle->connections = ngx_alloc(sizeof(ngx_connection_t) * cycle->connection_n, cycle->log);
c = cycle->connections;
cycle->read_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n,cycle->log);
cycle->write_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n,cycle->log);
do {
i--;
c[i].data = next;
c[i].read = &cycle->read_events[i]; //关联c[i]和rev wev
c[i].write = &cycle->write_events[i];
c[i].fd = (ngx_socket_t) -1;
next = &c[i];
} while (i);
//针对监听的每个端口,分配connecting,并设置回调函数,加入到epoll池中
//c = ngx_cycle->free_connections;
ls = cycle->listening.elts;
for (i = 0; i < cycle->listening.nelts; i++) {
c = ngx_get_connection(ls[i].fd, cycle->log);
....
rev->handler = ngx_event_accept;
if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {
return NGX_ERROR;
}
}


ngx_event_accept请求的处理流程

-这里涉及到ngx_use_accept_mutex的问题,用于解决多进程之间对请求的均衡处理(后续理解)

//epoll 中接收到请求后的回调,建立socket连接
void ngx_event_accept(ngx_event_t *ev)
{
lc = ev->data;
ls = lc->listening;
s = accept(lc->fd, (struct sockaddr *) sa, &socklen);
if (ngx_use_accept_mutex) {//进程竞争锁
if (ngx_accept_mutex_held) {
ngx_shmtx_unlock(&ngx_accept_mutex);
ngx_accept_mutex_held = 0;
}
ngx_accept_disabled = 1;
} else {
ngx_add_timer(ev, ecf->accept_mutex_delay);
}
c = ngx_get_connection(s, ev->log);
}
//加入到epoll中等待read事件
if (ngx_add_conn && (ngx_event_flags & NGX_USE_EPOLL_EVENT) == 0) {
if (ngx_add_conn(c) == NGX_ERROR) {
ngx_close_accepted_connection(c);
return;
}
}
//在ngx_http_add_listening时ls->handler = ngx_http_init_connection;设置回调
ls->handler(c);//初始化这个对话connection


void ngx_http_init_connection(ngx_connection_t *c)
{//初始化一个request的connection
//设置这个listen的addr_conf和conf_ctx,当数据可读时候用于解析
hc = ngx_pcalloc(c->pool, sizeof(ngx_http_connection_t));
if (hc == NULL) {
ngx_http_close_connection(c);
return;
}
c->data = hc;
....
rev = c->read;//初始化read的epoll毁掉函数
rev->handler = ngx_http_wait_request_handler;
c->write->handler = ngx_http_empty_handler;
//设置这个链接的超时timer
ngx_add_timer(rev, c->listening->post_accept_timeout);
}


具体的时序图:



ngx_http_wait_request_handler

当epoll中此connection可读,调用ngx_http_wait_request_handler(ngx_event_t *rev)

关于rev和connection中的关键数据data的关系

//request 中的main_conf/srv_conf/loc_conf
ngx_http_wait_request_handler(ngx_event_t *rev)
{
n = c->recv(c, b->last, size);
....
c->data = ngx_http_create_request(c); //这里创建request
{
r->main_conf = hc->conf_ctx->main_conf;
r->srv_conf = hc->conf_ctx->srv_conf;
r->loc_conf = hc->conf_ctx->loc_conf;
}

}


ngx_get_connection中 rev->data = c;

ngx_http_init_connection(*c) -> ngx_http_wait_request_handler(rev) -> c->data = ngx_http_create_request(rev)



//设置request 的connect到recv中:
ngx_http_process_request_line(ngx_event_t *rev)
{
ngx_connection_t    *c;
ngx_http_request_t  *r;

c = rev->data; //ngx_get_connection中 rev->data = c;
r = c->data;
//这里的data设置:ngx_http_init_connection(*c) -> ngx_http_wait_request_handler(rev) -> c->data = ngx_http_create_request(rev)
}


最后当请求request过来的时候就能够从rev中获取到request并且里面有相应的ngx_http_core_loc_conf_t

ngx_http_core_find_location 回顾上一篇文章中,根据url和ngx_http_core_loc_conf_t 的三叉排序树或者正则数组 来获取正确模块的loc_conf。

最后就可以调用模块的content_handler函数传入req->locf 完成整请求(待完善)

后续会针对初始化event 和 请求的流程写详细一点,这里只是从listen出发后续展开。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: