建议收藏 Nginx的DNS解析详细过程分析( 二 )

  • 查询完成后回调在ngx_resolver_ctx_t中指定的方法
  • 真正的DNS查询完成后 , 不管成功 , 失败或是超时 , nginx会回调相应查询的handler, 如前面设置的:ngx_mail_smtp_resolve_name_handler 。在handler中都需要调用ngx_resolve_addr_done来标识查询结束 。
  • static voidngx_mail_smtp_resolve_name_handler(ngx_resolver_ctx_t *ctx){ in_addr_t addr; ngx_uint_t i; ngx_connection_t *c; struct sockaddr_in *sin; ngx_mail_session_t *s;s = ctx->data; c = s->connection;if (ctx->state) { ngx_log_error(NGX_LOG_ERR, c->log, 0, ""%V" could not be resolved (%i: %s)", &ctx->name, ctx->state, ngx_resolver_strerror(ctx->state)); } else { /* AF_INET only */ sin = (struct sockaddr_in *) c->sockaddr;for (i = 0; i < ctx->naddrs; i++) { addr = ctx->addrs[i];ngx_log_debug4(NGX_LOG_DEBUG_MAIL, c->log, 0, "name was resolved to %ud.%ud.%ud.%ud", (ntohl(addr) >> 24) & 0xff, (ntohl(addr) >> 16) & 0xff, (ntohl(addr) >> 8) & 0xff, ntohl(addr) & 0xff);if (addr == sin->sin_addr.s_addr) { goto found; } }s->host = smtp_unavailable; } found: //不管成功失败都要执行 ngx_resolve_name_done(ctx);}二、域名解析流程分析
    建议收藏 Nginx的DNS解析详细过程分析

    文章插图
     
    通过Nginx进行域名查询的流程图如下 , 颜色越深花费的时间越长 。调用过程分为三种:
    1. 首先判断是不是IPv4地址 , 如果是就直接调用Handler
    2. 再次检查是不是在缓存中 , 如果有 , 就调用Handler
    3. 最后发送远程DNS请求 , 收到回复后调用Handler
    三、查询场景分析及实现介绍
    查询的地址是IP v4地址
    比如74.125.128.100, nginx会在ngx_resolve_start中通过ngx_inet_addr方法进行判断 , 如果是IPv4的地址 , 就设置好标志位 ngx_resolver_ctx_t->quick , 在接下来的ngx_resolve_name中会对这个标志位进行判断 , 如果为1 , 就直接调用ngx_resolver_ctx_t->handler
    ngx_resolver_ctx_t *ngx_resolve_start(ngx_resolver_t *r, ngx_resolver_ctx_t *temp){ in_addr_t addr; ngx_resolver_ctx_t *ctx;if (temp) { addr = ngx_inet_addr(temp->name.data, temp->name.len);if (addr != INADDR_NONE) { temp->resolver = r; temp->state = NGX_OK; temp->naddrs = 1; temp->addrs = &temp->addr; temp->addr = addr; temp->quick = 1;return temp; } } ...}
    1. 超时没有得到查询结果
    2. 调用ngx_resolve_name时设置的回调方法被调用 , 同时ngx_resolver_ctx_t->state被设置为NGX_RESOLVE_TIMEDOUT 。相应的代码为:
    static voidngx_resolver_timeout_handler(ngx_event_t *ev){ ngx_resolver_ctx_t *ctx; ctx = ev->data; ctx->state = NGX_RESOLVE_TIMEDOUT; ctx->handler(ctx);}
    1. 正常查询一个不在缓存中的域名
    2. 如果要查询的域名不在缓存中 , 首先把域名按hash值放在缓存中 , 然后准备查询需要的数据 , 发送DNS查询的UDP请求给DNS服务器 , 
    static ngx_int_tngx_resolve_name_locked(ngx_resolver_t *r, ngx_resolver_ctx_t *ctx){ ngx_resolver_node_t *rn; rn = ngx_resolver_alloc(r, sizeof(ngx_resolver_node_t)); ngx_rbtree_insert(&r->name_rbtree, &rn->node); ngx_resolver_create_name_query(rn, ctx); ngx_resolver_send_query(r, rn);rn->cnlen = 0; rn->naddrs = 0; rn->valid = 0; rn->waiting = ctx;ctx->state = NGX_AGAIN;} //收到DNS查询结果后的回调方法static voidngx_resolver_read_response(ngx_event_t *rev){ ssize_t n; ngx_connection_t *c; u_char buf[NGX_RESOLVER_UDP_SIZE]; c = rev->data;do { n = ngx_udp_recv(c, buf, NGX_RESOLVER_UDP_SIZE); if (n < 0) { return; }ngx_resolver_process_response(c->data, buf, n); } while (rev->ready);} static voidngx_resolver_process_a(ngx_resolver_t *r, u_char *buf, size_t last, ngx_uint_t ident, ngx_uint_t code, ngx_uint_t nan, ngx_uint_t ans){ hash = ngx_crc32_short(name.data, name.len); rn = ngx_resolver_lookup_name(r, &name, hash);//copy addresses to cached node rn->u.addrs = addrs;//回调所有等待本域名解析的请求 next = rn->waiting; rn->waiting = NULL;while (next) { ctx = next; ctx->state = NGX_OK; ctx->naddrs = naddrs; ctx->addrs = (naddrs == 1) ? &ctx->addr : addrs; ctx->addr = addr; next = ctx->next;ctx->handler(ctx); }}
    1. 对同一域名查询多次查询
    2. 如果多次查询时 , 之前的查询结果还在缓存中并且没有失效 , 就直接从缓存中取到查询结果 , 并调用设置的回调方法 。
    static ngx_int_tngx_resolve_name_locked(ngx_resolver_t *r, ngx_resolver_ctx_t *ctx){ uint32_t hash; in_addr_t addr, *addrs; ngx_uint_t naddrs; ngx_resolver_ctx_t *next; ngx_resolver_node_t *rn;hash = ngx_crc32_short(ctx->name.data, ctx->name.len); rn = ngx_resolver_lookup_name(r, &ctx->name, hash);if (rn) { if (rn->valid >= ngx_time()) { naddrs = rn->naddrs;if (naddrs) { ctx->next = rn->waiting; rn->waiting = NULL;do { ctx->state = NGX_OK; ctx->naddrs = naddrs; ctx->addrs = (naddrs == 1) ? &ctx->addr : addrs; ctx->addr = addr; next = ctx->next;ctx->handler(ctx);ctx = next; } while (ctx);return NGX_OK; } } }}


    推荐阅读