Nginx域名解析流程,源码分析

Stella981
• 阅读 659

Nginx域名解析流程,源码分析

nginx在做正向代理、反向代理的时候,或upstream使用域名的时候,要做频繁的域名解析,为了更快的响应,nginx有一套自己的域名解析过程

Nginx域名解析流程,源码分析

今天详细分析一下nginx的域名解析过程

在nginx中,只有两个配置指令关于域名解析,就是resolver,和resolver_timeout,resolver_timeout不多说,就是域名解析超时时间,这里具体就说resolver指令

简单配置了个nginx反向代理,如图,然后为了便于调试,只起了一个工作进程

Nginx域名解析流程,源码分析

先用strace看了下系统调用,在connect调用中已经解析到了baidu.com的IP地址

Nginx域名解析流程,源码分析

这和预想的不一样,原本以为是每次调用都会去查一次系统DNS,但是这里却看到没有查系统DNS,难道没有调用系统dns吗?自有一套?下面验证猜想

首先我把/etc/resolv.conf的nameserver改成个不可访问的,然后启动nginx

Nginx域名解析流程,源码分析

发现无法正常启动,报错解析不到域名的地址,那应该还是调用系统dns了,接着用strace看一下启动过程

Nginx域名解析流程,源码分析

前面部分就不截图了,基本就是调用各种系统组件,初始化的过程,到这里开始读取default.conf配置文件,然后开始解析proxy_pass后面的域名地址

可以看到过程如下:

首先查询nscd

接着查询/etc/host.conf

然后查询/etc/resolve.conf

接着通过nsswith进行解析,利用resolve文件中提供的nameserver地址

Nginx域名解析流程,源码分析

尝试发送解析多次后,解析失败

最后调用wirte输出错误

Nginx域名解析流程,源码分析

通过以上strace追踪发现,nginx是在启动的时候就调用系统dns进行域名解析操作,下面结合源码看下nginx启动的时候如何初始化域名解析

从上面分析,是在解析配置文件的时候才去做域名解析操作的,所以根据nginx初始化流程判断,直接查看nginx的http_core_module中可以看到对resolver的声明Nginx域名解析流程,源码分析

然后在core/ngx_resolver.c中查看ngx_resolver_t的结构体

首先是typedef定义了别名

Nginx域名解析流程,源码分析

找到ngx_resolver_s查看结构体变量声明如下:

Nginx域名解析流程,源码分析

可以看到声明了dns查询,以及红黑树缓存dns数据,以及IPv6的处理

nginx在初始化的时候,通过core/ngx_resolver.c中的ngx_resolver_create来初始化上面的结构体,如果在配置文件中有设置resolver指令,则在启动的时候通过http/ngx_http_core_resolver进行调用

Nginx域名解析流程,源码分析

接着看下ngx_resolver_create做了什么

Nginx域名解析流程,源码分析

太长了,不贴代码了,这里解释下过程,有兴趣可以去看源码

这里主要就是配置解析阶段:

  • 设置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->connections)

  • 解析参数(valid,ipv6)等

Nginx域名解析流程,源码分析

请求构造阶段:

Nginx域名解析流程,源码分析

通过ngx_resolver_start开始做解析,判断如果是IP地址,则temp->quick=1,直接返回IP地址

我们知道,通常只有在proxy_pass和upstream中进行域名配置,所以接着看下proxy_pass指令源码和upstream指令源码

Nginx域名解析流程,源码分析

代码比较长,在http/ngx_http_proxy_module.c中,在ngx_http_proxy_passs中,首先判断upstream中的域名配置和proxy_length,接着判断proxy_pass后面的url中最后是否是"/",如果是,自动跳转,接着判断url中变量数量,根据数量判断是http还是https协议,接着还是通过调用ngx_http_upstream_add,将域名添加到upstream解析队列中,所以所有的调用解析,还是从upstream中调用,接着看upstream

Nginx域名解析流程,源码分析

接着刚才ngx_http_upstream_add,proxy_pass中的url传入之后,开始通过ngx_http_upstream_create创建upstream,接着在upstream初始化中声明resolver并调用ngx_resolve_start解析域名Nginx域名解析流程,源码分析

Nginx域名解析流程,源码分析

整个过程总结如下:

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.

过程比较复杂,总的来说,当proxy_pass后面是连接的时候,即使不定义upstream,nginx也会隐式的,将proxy_pass后面的url创建一个upstream,由upstream模块进行调用resolver来做域名的解析

解析是在初始化的时候就进行的,首先会根据服务器DNS配置或host配置进行一个缓存队列,队列中缓存的IP及域名对是有过期时间的,过期后清理,重新进行解析

Nginx域名解析流程,源码分析

我通过正常的配置,curl请求,反向代理到百度正常,接着我修改我的hosts文件,将百度代理到一个随意的内网地址,再次请求,仍然可以请求到,所以可以证明上面的缓存时间,所以当你更新DNS后,为了让nginx更快更新,需要重启nginx

resolver对于IPv6的配置,默认是开启的,也就是当域名解析到既有ipv4又有ipv6时,都会解析到,官方提供ipv6=on|off,来控制ipv6解析

Nginx域名解析流程,源码分析

Nginx调试必备

你的nginx登录认证安全吗?

Nginx常见异常整理,帮你快速定位

Nginx平滑升级要停服务?姿势不对吧

Nginx通过Cookie做灰度就这么简单

Nginx处理图片,就是这么简单

nginx中利用resolver实现动态upstream

Nginx域名解析流程,源码分析

扫描二维码

Nginx域名解析流程,源码分析

Nginx域名解析流程,源码分析

关注我们吧

本文分享自微信公众号 - 运维研习社()。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Wesley13 Wesley13
2年前
Java获得今日零时零分零秒的时间(Date型)
publicDatezeroTime()throwsParseException{    DatetimenewDate();    SimpleDateFormatsimpnewSimpleDateFormat("yyyyMMdd00:00:00");    SimpleDateFormatsimp2newS
Stella981 Stella981
2年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Easter79 Easter79
2年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这