nginx的域名解析器使用已連接udp(收發前先調用ngx_udp_connect)發送dns查詢、接收dns響應,如上篇
tcp異步連接所講,iocp需要先投遞udp的接收操作,才能引發接收完成的事件,因此要對域名解析器和udp異步接收作些改進。
發送后投遞
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
}
當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中。
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
}
先以非阻塞方式接收,若發生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,便于在回調內統一處理。
posted on 2015-06-25 17:01
春秋十二月 閱讀(6250)
評論(0) 編輯 收藏 引用 所屬分類:
Opensrc