nginx iocp(2):udp异步接收
nginx的域名解析器使用已连接udp(收发前先调用ngx_udp_connect)发送dns查询、接收dns响应,如上篇tcp异步连接所讲,iocp需要先投递udp的接收操作,才能引发接收完成的事件,因此要对域名解析器和udp异步接收作些改进。
发送后投递
dns查询由ngx_resolver_send_query函数实现,定义在core/ngx_resolver.c中。
当nginx用于代理连接上游服务器前,要先解析域名,首次调用链为:ngx_http_upstream_init_request->ngx_resolver_name->ngx_resolver_name_locked->ngx_resolver_send_query;若5s(单次超时)后还没收到dns响应,则再发送1次查询,调用链为:ngx_resolver_resend_handler->ngx_resolver_resend->ngx_resolver_send_query,如此反复,直到收到响应或30s(默认总超时)后不再发送查询。它调用ngx_send发送dns查询,16行~21行代码为笔者添加,ngx_resolver_read_response函数用于接收并分析dns响应报文,它会调用到下面的ngx_udp_overlapped_wsarecv函数。
异步接收
由ngx_udp_overlapped_wsarecv函数实现,定义在os/win32/ngx_udp_wsarecv.c中。
先以非阻塞方式接收,若发生WSAWOULDBLOCK错误,则使用MSG_PEEK标志投递一个0字节的重叠接收操作,当dns响应返回时发生完成事件,会再次进入ngx_resolver_read_response而调用到该函数,此时rev->complete为1,rev->ovlp.error为ERROR_MORE_DATA(GetQueuedCompletionStatus返回的错误),由于使用了MSG_PEEK,因此数据还在接收缓冲区中,要忽略ERROR_MORE_DATA而继续接收,这时就能成功了。不管WSARecv返回WSA_IO_PENDING错误还是成功,iocp都会得到完成通知,所以这里当重叠操作投递成功时,返回NGX_AGAIN,便于在回调内统一处理。
发送后投递
dns查询由ngx_resolver_send_query函数实现,定义在core/ngx_resolver.c中。
1
static ngx_int_t ngx_resolver_send_query(ngx_resolver_t *r, ngx_resolver_node_t *rn)
2
{
3
![]()
4
if (rn->naddrs == (u_short) -1) {
5
n = ngx_send(uc->connection, rn->query, rn->qlen);
6
![]()
7
}
8![]()
9
#if (NGX_HAVE_INET6)
10
if (rn->query6 && rn->naddrs6 == (u_short) -1) {
11
n = ngx_send(uc->connection, rn->query6, rn->qlen);
12
![]()
13
}
14
#endif
15![]()
16
#if (NGX_WIN32)
17
if (ngx_event_flags & NGX_USE_IOCP_EVENT){
18
uc->connection->read->ready = 1;
19
ngx_resolver_read_response(uc->connection->read);
20
}
21
#endif
22![]()
23
return NGX_OK;
24
}
static ngx_int_t ngx_resolver_send_query(ngx_resolver_t *r, ngx_resolver_node_t *rn)2
{3

4
if (rn->naddrs == (u_short) -1) {5
n = ngx_send(uc->connection, rn->query, rn->qlen);6

7
}8

9
#if (NGX_HAVE_INET6)10
if (rn->query6 && rn->naddrs6 == (u_short) -1) {11
n = ngx_send(uc->connection, rn->query6, rn->qlen);12

13
}14
#endif15

16
#if (NGX_WIN32) 17
if (ngx_event_flags & NGX_USE_IOCP_EVENT){18
uc->connection->read->ready = 1;19
ngx_resolver_read_response(uc->connection->read);20
}21
#endif22

23
return NGX_OK;24
}异步接收
由ngx_udp_overlapped_wsarecv函数实现,定义在os/win32/ngx_udp_wsarecv.c中。
1
ssize_t ngx_udp_overlapped_wsarecv(ngx_connection_t *c, u_char *buf, size_t size)
2
{
3
int flags, rc;
4
WSABUF wsabuf;
5
ngx_err_t err;
6
ngx_event_t *rev;
7
WSAOVERLAPPED *ovlp;
8
u_long bytes;
9
10
rev = c->read;
11
12
if (!rev->ready) {
13
ngx_log_error(NGX_LOG_ALERT, c->log, 0, "ngx_udp_overlapped_wsarecv second wsa post");
14
return NGX_AGAIN;
15
}
16
17
if (rev->complete) {
18
if (ngx_event_flags & NGX_USE_IOCP_EVENT) {
19
if (rev->ovlp.error && rev->ovlp.error != ERROR_MORE_DATA) {
20
ngx_connection_error(c, rev->ovlp.error, "ngx_udp_overlapped_wsarecv() failed");
21
return NGX_ERROR;
22
}
23
}
24
![]()
25
rev->complete = 0;
26
}
27
28
ovlp = NULL;
29
wsabuf.buf = (CHAR *) buf;
30
wsabuf.len = (ULONG) size;
31
flags = 0;
32
33
retry:
34
rc = WSARecv(c->fd, &wsabuf, 1, (DWORD*)&bytes, (LPDWORD)&flags, ovlp, NULL);
35
36
if (rc == -1) {
37
rev->ready = 0;
38
err = ngx_socket_errno;
39
40
if (err == WSA_IO_PENDING) {
41
return NGX_AGAIN;
42
}
43
44
if (err == WSAEWOULDBLOCK) {
45
if (ngx_event_flags & NGX_USE_IOCP_EVENT) {
46
rev->ovlp.type = NGX_IOCP_IO;
47
ovlp = (WSAOVERLAPPED *)&rev->ovlp;
48
ngx_memzero(ovlp, sizeof(WSAOVERLAPPED));
49
50
wsabuf.buf = NULL;
51
wsabuf.len = 0;
52
flags = MSG_PEEK;
53
54
goto retry;
55
}
56
57
return NGX_AGAIN;
58
}
59
60
ngx_connection_error(c, err, "ngx_udp_overlapped_wsarecv() failed");
61
rev->error = 1;
62
63
return NGX_ERROR;
64
}
65
66
if ((ngx_event_flags & NGX_USE_IOCP_EVENT) && ovlp) {
67
rev->ready = 0;
68
return NGX_AGAIN;
69
}
70
71
return bytes;
72
}
ssize_t ngx_udp_overlapped_wsarecv(ngx_connection_t *c, u_char *buf, size_t size)2
{3
int flags, rc;4
WSABUF wsabuf;5
ngx_err_t err;6
ngx_event_t *rev;7
WSAOVERLAPPED *ovlp;8
u_long bytes;9
10
rev = c->read;11
12
if (!rev->ready) {13
ngx_log_error(NGX_LOG_ALERT, c->log, 0, "ngx_udp_overlapped_wsarecv second wsa post");14
return NGX_AGAIN;15
}16
17
if (rev->complete) {18
if (ngx_event_flags & NGX_USE_IOCP_EVENT) {19
if (rev->ovlp.error && rev->ovlp.error != ERROR_MORE_DATA) {20
ngx_connection_error(c, rev->ovlp.error, "ngx_udp_overlapped_wsarecv() failed");21
return NGX_ERROR;22
}23
}24

25
rev->complete = 0;26
}27
28
ovlp = NULL;29
wsabuf.buf = (CHAR *) buf;30
wsabuf.len = (ULONG) size;31
flags = 0;32
33
retry: 34
rc = WSARecv(c->fd, &wsabuf, 1, (DWORD*)&bytes, (LPDWORD)&flags, ovlp, NULL);35
36
if (rc == -1) {37
rev->ready = 0;38
err = ngx_socket_errno;39
40
if (err == WSA_IO_PENDING) {41
return NGX_AGAIN;42
}43
44
if (err == WSAEWOULDBLOCK) {45
if (ngx_event_flags & NGX_USE_IOCP_EVENT) { 46
rev->ovlp.type = NGX_IOCP_IO;47
ovlp = (WSAOVERLAPPED *)&rev->ovlp;48
ngx_memzero(ovlp, sizeof(WSAOVERLAPPED));49
50
wsabuf.buf = NULL;51
wsabuf.len = 0;52
flags = MSG_PEEK;53
54
goto retry; 55
}56
57
return NGX_AGAIN; 58
}59
60
ngx_connection_error(c, err, "ngx_udp_overlapped_wsarecv() failed"); 61
rev->error = 1;62
63
return NGX_ERROR;64
}65
66
if ((ngx_event_flags & NGX_USE_IOCP_EVENT) && ovlp) {67
rev->ready = 0;68
return NGX_AGAIN;69
}70
71
return bytes;72
}


浙公网安备 33010602011771号