配置解析阶段:



Syntax: resolver address ... [valid=time];



ngx_http_core_resolver()



clcf->resolver = ngx_resolver_create()



• 设置cleanup的handler (ngx_resolver_cleanup)



• 初始化保存域名节点信息的红黑树 (r->name_rbtree)



• 初始化重传和过期队列 (r->name_resend_queue r->name_expire_queue)



• 设置超时事件的handler (ngx_resolver_resend_handler)



• 解析dns server的ip并设置到地址数组 (r->udp_connections)



• 解析参数 (valid, ipv6 等)



 



请求构造阶段:



proxy_pass http://$host;



ngx_resolver_ctx_t ctx 每次域名解析都会生成这个结构体, 直接malloc,未使用r->pool.



ctx = ngx_resolve_start()



• 如果$host是ip地址, 直接设置ctx->quick = 1, 表示后续逻辑不需要走dns解析逻辑.



• 如果r->udp_connections 不存在, 返回NGX_NO_RESOLVER, 最终请求返回502.



初始化ctx参数



• ctx->type = NGX_RESOLVE_A;



• ctx->handler = ngx_http_upstream_resolve_handler;



• ctx->timeout = clcf->resolver_timeout;



• ngx_resolve_name(ctx)



• 如果 ctx->quick == 1, 直接调用 ctx->handler, 跳过dns解析.



• 否者调用 ngx_resolve_name_locked, 执行dns解析.



• ngx_resolve_name_locked(r, ctx)



1 调用ngx_resolver_lookup_name查找域名节点rn是否在r->name_rbtree缓存节点中, 存在进入(2), 否者进入 (5)



2 判断rn->valid是否过期,没有过期进入(3), 否者进入(4).



3 如果存在 rn->naddrs, 是A记录节点, 循环调用rn->waiting链表上的 ctx->handler, 然后函数返回OK; 如果不存在 rn->naddrs, 表示是CNAME记录节点, 那么递归调用ngx_resolve_name_locked,进入步骤 (1).



4 rn->valid已经过期, 如果存在rn->waiting, 表示已经触发了新的dns请求, 只需要把ctx挂在到链表上, 函数返回NGX_AGAIN. 如果不存在rn->waiting,表示这是域名失效之后的第一个请求, 需要清空上一次dns请求申请的内



存, 进入 (6)



5 不存在rn, 表示第一次域名请求, 初始化rn节点, 并加入 r->name_rbtree红黑树.



6 创建域名查询请求 ngx_resolver_create_name_query



7 发送域名查询请求 ngx_resolver_send_query, 并设置dns查询的读事件 uc->connection->read->handler = ngx_resolver_read_response



8 挂载超时事件 ngx_add_timer(ctx->event, ctx->timeout) ctx->event->handler->ngx_resolver_timeout_handler



9 函数结束, 返回NGX_AGAIN.



dns域名查询的发送请求阶段完成, 接下来请求将等待事件触发.



 



事件触发阶段:



DNS查询可读事件 ( ngx_resolver_read_response )



1 读事件触发, 调用ngx_resolver_read_response



2 最终调用到 ngx_resolver_process_a.



3 如果是A记录, 设置rn的ip地址, 并循环调用 ctx->waiting的handler, 把堆积的请求都处理完, 函数返回.



4 如果是CNAME记录, 将rn->waiting链表中的ctx循环调用ngx_resolve_name_locked, 继续进行dns解析流程.



 



 



超时事件 ( ngx_resolver_timeout_handler )



1 如果在ctx->timeout (resolver_timeout) 时间内没有可读事件, 将触发 ngx_resolver_timeout_handler.



2 该函数将 ctx->state = NGX_RESOLVE_TIMEDOUT, 并调用 ctx->handler,handler会判断ctx->state,返回502.



 



 



请求结束阶段:



ngx_resolve_name_done(ctx) 每个请求在结束的时候会调用



• 如果关闭了请求的时候还没有域名解析超时, 删除定时器. ngx_del_timer(ctx->event)



• 如果ctx存在于rn->waiting链表中, 从链表中删除ctx.



• 释放ctx分配的内存.



• 调用ngx_resolver_expire, 遍历r->name_expire_queue队列删除r->name_rbtree中的过期rn.



 



 



DNS异步重传阶段: (跟请求无关)



重传函数 ngx_resolver_resend_handler



• 遍历r->name_resend_queue链表, 如果存在rn->waiting, 表示还有请求在等待域名解析结果, 需要重新发送域名请求(ngx_resolver_send_query); 否则就从r->name_rbtree中删除rn节点.



• 重新挂载超时定时器, 等待下次重传事件.



 



 



重传队列和过期队列:



• r->name_expire_queue 过期队列 每次域名查询之后都会更新时间 rn->expire = ngx_time() + r->expire (30s)



• r->name_resend_queue 重传队列 每次域名重传之后都会更新时间 rn->expire = ngx_time() + r->resend_timeout (5s)



rn必在其中一个队列: 初始化时构造DNS请求, 在resend队列, 在DNS解析成功之后, 放入expire队列. 如果ttl过期了, 将从expire队列删除, 放入resend队列, 构造新的DNS请求....



rn->waiting 在nginx启动或者域名过期, 等待dns查询结果返回的过程中, 请求这个域名的请求(ctx)都将挂载在这个队列上.等查询结果返回之后, 循环调用链表中每个请求的handler.如果在查询结果返回之前请求就因为其他原因终止了, 请求会通过调用ngx_resolve_name_done把自己从链表中删除.