nginx关于域名解析的源码分析

发布时间:2024-01-31 点击:121
在nginx中,nginx需要频繁进行域名解析的过程做了自己的优化,使用了自己的一套域名解析过程,并做了缓存处理。我们可以设置dns解析服务器的地址,即通过resolver指令来设置dns服务器的地址,由此来启动nginx的域名解析。
本文,我们来看看nginx是如何做的,这里我们只选出重要的代码进行分析,完整代码请参考nginx源代码,本文基于nginx-1.0.6版本进行的分析。
首先,来看看resolver的初始化。
在ngx_http_core_loc_conf_s的声明中,可以看到对reolver:
struct ngx_http_core_loc_conf_s { ngx_resolver_t *resolver; /* resolver */ }
resolver中保存了与域名解析相关的一些数据,它保存了dns的本地缓存,通过红黑树的方式来组织数据,以达到快速查找。
typedef struct { ngx_event_t *event; // 用于连接dns服务器 ngx_udp_connection_t *udp_connection; // 保存了本地缓存的dns数据 ngx_rbtree_t name_rbtree; ngx_rbtree_node_t name_sentinel; } ngx_resolver_t;
在nginx初始化的时候,通过ngx_resolver_create来初始化这一结构体,如果有设置resolver,则在ngx_http_core_resolver中有调用:
static char * ngx_http_core_resolver(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_core_loc_conf_t *clcf = conf; // 初始化,第二个参数是我们设置的域名解析服务器的ip地址 clcf->resolver = ngx_resolver_create(cf, &u.addrs[0]); if (clcf->resolver == null) { return ngx_ok; } return ngx_conf_ok; }
来看看ngx_resolver_create做了些什么:
ngx_resolver_t * ngx_resolver_create(ngx_conf_t *cf, ngx_addr_t *addr) { ngx_resolver_t *r; ngx_udp_connection_t *uc; r = ngx_calloc(sizeof(ngx_resolver_t), cf->log); if (r == null) { return null; } // 省略了其它数据的初始化过程 r->event = ngx_calloc(sizeof(ngx_event_t), cf->log); if (r->event == null) { return null; } // 设置事件的句柄 r->event->handler = ngx_resolver_resend_handler; r->event->data = r; // 设置dns服务器的地址 if (addr) { uc = ngx_calloc(sizeof(ngx_udp_connection_t), cf->log); if (uc == null) { return null; } r->udp_connection = uc; uc->sockaddr = addr->sockaddr; uc->socklen = addr->socklen; uc->server = addr->name; } return r; }
初始化好了之后,就可以调用了。在nginx中,upstream中使用到了此方法的域名解析。我们结合proxy模块与upstream模块来实例讲解吧,注意在proxy中,只有当proxy_pass中包含有变量时,才会用到nginx自己的dns解析。而且这里有一个需要特别注意的,如果proxy_pass中包含变量,那么nginx中就需要配置resolver来指定dns服务器地址了,否则,将直接返回502错误。从下面的代码中我们可以看到。
首先,在ngx_http_proxy_handler函数中,有如下代码:
static ngx_int_t ngx_http_proxy_handler(ngx_http_request_t *r) { // 这里的意思是,如果没有变量,就不进行变量解析 if (plcf->proxy_lengths == null) { ctx->vars = plcf->vars; u->schema = plcf->vars.schema; } else { // 只有当proxy_pass里面包含变量时,才解析变量,在ngx_http_proxy_eval中会添加域名解析的需求,请看ngx_http_proxy_eval的实现 if (ngx_http_proxy_eval(r, ctx, plcf) != ngx_ok) { return ngx_http_internal_server_error; } } }
而在proxy模块的ngx_http_proxy_eval函数中,可以看到如下代码:
static ngx_int_t ngx_http_proxy_eval(ngx_http_request_t *r, ngx_http_proxy_ctx_t *ctx, ngx_http_proxy_loc_conf_t *plcf) { ngx_str_t proxy; ngx_url_t url; // proxy为要转向的url url.url.data = proxy.data add; url.default_port = port; url.uri_part = 1; // 注意这里设置的为不用解析域名 url.no_resolve = 1; // 由于有设置不用解析域名,所以在ngx_parse_url中就不会对域名进行解析 if (ngx_parse_url(r->pool, &url) != ngx_ok) { return ngx_error; } // 保存与需要解析域名相关的信息 u->resolved = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_resolved_t)); if (u->resolved == null) { return ngx_error; } if (url.addrs && url.addrs[0].sockaddr) { // 如果域名已经是ip地址的格式,就保存起来,这样在upstream里面就不会再进行解析 // 在upsteam模块里面会判断u->resolved->sockaddr是否为空 u->resolved->sockaddr = url.addrs[0].sockaddr; u->resolved->socklen = url.addrs[0].socklen; u->resolved->naddrs = 1; u->resolved->host = url.addrs[0].name; } else { u->resolved->host = url.host; u->resolved->port = (in_port_t) (url.no_port ? port : url.port); u->resolved->no_port = url.no_port; } }
所以,可以看出,只在当proxy_pass到包含变量的url时,才有可能进行域名的解析。因为如果是固定的url,则完全可以在初始化的时候解析域名,而不用在请求的时候进行了。关于这部分代码的实

上海vps云服务器租用
录入错误需要修改网站主体
数据库连接异常-云服务器问题
云服务器续费去哪里买
一台云服务器可以挂多少个qq
中文域名 注册
电脑开机出现bootmenu怎么办 电脑开机显示bootmenu的解决教程
怎么从视频里提取音频 如何从视频中提取音频