0%

nginx杂谈

nginx通过epoll等事件机制来驱动,下面分析下nginx是如何实现与后端upstream的异步连接。

首先分析下:ngx_event_connect_peer

看下这个函数在哪些地方被调用

1
2
3
4
5
6
$grep -rn 'ngx_event_connect_peer' src/
src/http/ngx_http_upstream.c:1104: rc = ngx_event_connect_peer(&u->peer);
src/event/ngx_event_connect.h:72:ngx_int_t ngx_event_connect_peer(ngx_peer_connection_t *pc);
src/event/ngx_event_connect.c:15:ngx_event_connect_peer(ngx_peer_connection_t *pc)
src/mail/ngx_mail_proxy_module.c:151: rc = ngx_event_connect_peer(&p->upstream);
src/mail/ngx_mail_auth_http_module.c:195: rc = ngx_event_connect_peer(&ctx->peer);

暂时只关注http,只在upstream模块中被调用,看下其使用方法:
调用链: ngx_http_upstream_connect(ngx_http_request_t *r, ngx_http_upstream_t *u)
-> rc = ngx_event_connect_peer(&u->peer);

下面分析ngx_event_connect_peer(ngx_peer_connection_t *pc)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
struct ngx_peer_connection_s {
ngx_connection_t *connection;

struct sockaddr *sockaddr;
socklen_t socklen;
ngx_str_t *name;

ngx_uint_t tries;

ngx_event_get_peer_pt get;
ngx_event_free_peer_pt free;
void *data;

#if (NGX_SSL)
ngx_event_set_peer_session_pt set_session;
ngx_event_save_peer_session_pt save_session;
#endif

#if (NGX_THREADS)
ngx_atomic_t *lock;
#endif

ngx_addr_t *local;

int rcvbuf;

ngx_log_t *log;

unsigned cached:1;

/* ngx_connection_log_error_e */
unsigned log_error:2;
};

这是专门用来给后端用的connection结构体,

======================================================

在看多了代码后,有这样一个体会:即搞清楚ngx_http_request, ngx_connection_t,ngx_event_t ngx_http_upstream_t等结构体的相互的联动以及为何要这样设计联动,就可以大体上掌握nginx的http框架了、事件框架了

(由于http框架是建立在事件框架基础上的,在对比下mail等七层的框架,就可以很容易融会贯通了。)

r、c、ev间的关系:

知道r如何找其他两者
c = r->connection
rev = c->read;
wev = c->write;

知道c如何找其他两者
r = c->data;(在空闲的c中,c->data指向下一个空闲的c)
rev = c->read;
wev = c->write;

知道到ev如何找其他两者
c = ev->data;
r = c->data;

分析下,为何ev->data指向的是c:因为事件是和连接层面相关的,和七层应用层面无关,事件的data将指向该事件是发生在哪个连接上的。
为何c->data指向r,其实c的data不一定指向r,只是在http层面, c是用于http传输的,那么c的data指向r,如果是在mail传输,c的data指向的是s(ngx_mail_session_t),

为何r->connection会指向c,显而易见从名字就可以看出,且这个c是和客户端的c,不是和上有的c(如果有的化)

和上有的的c是这样:u=r->upstream, c = u->peer.connection(这个c是和上游的c)

对事件的处理的一点心得:
r->read_event_handler/r->write_event_handler为何要有这样的钩子,通过源码可以查看到是在
ngx_http_request_handler(ev)中被调用的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
static void
ngx_http_request_handler(ngx_event_t *ev)
{
ngx_connection_t *c;
ngx_http_request_t *r;
ngx_http_log_ctx_t *ctx;

c = ev->data;
r = c->data;

ctx = c->log->data;
ctx->current_request = r;

ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http run request: \"%V?%V\"", &r->uri, &r->args);

if (ev->write) {
r->write_event_handler(r);

} else {
r->read_event_handler(r);
}

ngx_http_run_posted_requests(c);
}

而ngx_http_request_handler又被赋为
c->read->handler = ngx_http_request_handler;
c->write->handler = ngx_http_request_handler;
连接的读写事件的handler

从这里可以看出:事件触发 -> 调用ngx_http_request_handler -> 继续调用r->read_event_handler or r->write_event_handler。

这样nginx提供了这样一种机制:它将ev的回掉设置固定,但是将真正执行的handler留给模块自己去决定,比如
你只需关注在模块中将r->read_event_handler/write_event_handler设置好就OK,不用去设置ev的回调,这算是提供的一种对外接口。