张超:又拍云系统开发高级工程师,负责又拍云 CDN 平台相关组件的更新及维护。Github ID: tokers,活跃于 OpenResty 社区和 Nginx 邮件列表等开源社区,专注于服务端技术的研究;曾为 ngx_lua 贡献源码,在 Nginx、ngx_lua、CDN 性能优化、日志优化方面有较为深入的研究。

DNS 解析在 Nginx/OpenResty 的服务里是不可分割的一个功能,本文主要来介绍下 Nginx 和 OpenResty 服务里的一些不同的 DNS 解析方式以及它们之间的优缺点。

配置解析阶段

很多时候我们会在 Nginx 配置文件里配置上一些域名,比如配置我们的上游服务器。

  • upstream example.com {
  • server foo.example.com;
  • }

对于这类域名,Nginx 会在配置解析阶段就将其解析出来,接下来(请求处理过程)使用的都是当时解析得到的 IP。Nginx 核心有一个函数 ngx_parse_url,负责对 url 格式进行分析,包括解析出主机名,端口号以及 URL path 等。针对 IPv4 的情况,它会调用 ngx_parse_inet_url进行具体的解析任务,如果必要,最终它会调用到 ngx_inet_resolve_host进行域名解析,ngx_inet_resolve_host 大多情况下会使用 getaddrinfo 进行解析,最终向 /etc/resolv.conf 下所配置的 DNS server 发起解析请求。

归纳来说这个解析过程有两个特点,一是使用了系统配置的 DNS server;二是解析过程是同步且阻塞的,因此这种解析方式仅在 Nginx 配置解析阶段会被使用。另外这种解析方式的缺点就是只解析一次,所以如果在 Nginx 运行过程中域名解析发生了改变也是无法感知到的,除非手动重启 Nginx 服务。

运行时 DNS resolver

Nginx 核心提供了一套供运行时使用的 DNS 解析机制,它充分契合 Nginx 的事件模型,同样是异步非阻塞的,并且提供了缓存机制。http、stream 和 mail 模块分别提供了配置指令(比如 http 模块提供的 resolver),供我们配置相关 DNS server 地址等信息。

下面这个简单的反向代理配置,就会在进行代理前解析 http://www.upyun.com 这个域名。

  • location / {
  • set $myupstream www.upyun.com;
  • proxy_http_version 1.1;
  • proxy_set_header Connection "";
  • proxy_pass http://${myupstream}/index.html;
  • }

注意如果直接在 proxy_pass 指令里写明需要代理的域名(即不使用变量的方式),那么域名解析就会发生在配置解析阶段了,即上面所讲的过程。这其实也是一种实现动态 upstream 的方式。

这套运行时 DNS resolver 其实是一个 DNS client 的角色,由它自己组织查询报文并发送给目标 DNS 服务器,同时支持解析 IPv6 地址(从 1.5.8 开始),支持反向地址解析和 SRV 解析。它把对每个域名的解析抽象为一棵红黑树的节点,包括任何必要的信息。同时这棵红黑树也充当着缓存,查询时会以域名作为 key,如果对应缓存是新鲜的,即会复用缓存,并且会对解析得到的地址顺序进行一定的回转后再提供给上层使用。如果没有缓存或者缓存过期,新的 DNS 请求会被构建并且发送。

当然,很多时候这套运行时的 DNS resolver 也不能完全满足需求:

  1. 无法配置主备 DNS 服务器地址,我们在 resolver 指令里配置的地址都会按顺序被轮询到。
  2. 无法在 DNS 服务器故障或者网络质量不佳的情况下复用陈旧的缓存,这可能导致上层服务不可用。
  3. 每个 Nginx worker 进程独享解析缓存。

Cosocket

Cosocket 是 lua-nginx-module 提供的最强有力的接口(个人来看没有之一)。它的 connect方法同样支持传入域名,之后会调用上面介绍的 Nginx 运行时 resolver 来解析对应域名,然后随机挑选一个 IP 作为本次连接的目标 IP 地址。由于是使用的 Nginx 运行时 resolver ,如果 DNS resolver 无法正常进行解析,则利用 Cosocket 构建的服务也都会受到影响。

lua-resty-dns

OpenResty 官方开源的 lua-resty-dns 是利用 Cosocket 实现的一套百分百非阻塞的 DNS resolver,它仅仅充当了一个 DNS 解析器,没有任何其他的附加功能,包括缓存。

前面介绍过 Nginx 运行时 DNS resolver 在很多时候是有诸多的不便的,而 lua-resty-dns 则给我们留了很多的扩展空间:

  1. 提供主备 DNS 服务器地址 —— 结合像 lua-resty-checkups 这样的工具可以很容易的维护主备 DNS 服务器地址,包括进行心跳检测。
  2. 进程间共享缓存 —— 结合使用 lua-nginx-module 提供的共享内存字典,可以非常容易地管理缓存。
  3. 故障时使用陈旧缓存 —— 在 lua-resty-dns 报告异常时再次使用缓存即可(只要缓存拷贝没有被强行淘汰)。

目前 OpenResty 生态圈已经有一些基于lua-resty-dns 实现的加强版实现,比如 Kong 的 lua-resty-dns-client。

总的来说,每一种 DNS 解析方式都有它适用的场景,也有它自己的不足,没有最好的,只有最合适的。在程序设计的时候,需要找到最合适自己业务场景的方式,才能最大程度地保障服务的可用性和可靠性,避免带来一些灾难。

推荐阅读:

我眼中的 Nginx(五):Nginx - 子请求设计之道​
我眼中的 Nginx(四):是什么让你的 Nginx 服务退出这么慢?​
我眼中的 Nginx(三):Nginx 变量和变量插值
我眼中的 Nginx(二):HTTP/2 dynamic table size update​
我眼中的 Nginx(一):Nginx 和位运算​

我眼中的 Nginx(六):深入 Nginx/Openresty 服务里的 DNS 解析的更多相关文章

  1. Nginx(六):Nginx HTTP负载均衡和反向代理的配置与优化

    一.什么是负载均衡和反向代理 随着网站访问量的快速增长,单台服务器已经无法承担大量用户的并发访问,必须釆用多台服务器协同工作,以提高计算机系统的处理能力和计算强度,满足当前业务量的需求.而如何在完成同 ...

  2. 用lua扩展你的Nginx(整理)-----openresty

    用lua扩展你的Nginx(整理) 首先得声明.这不是我的原创,是在网上搜索到的一篇文章,原著是谁也搞不清楚了.按风格应该是属于章亦春的文章. 整理花了不少时间,所以就暂写成原创吧. 一. 概述 Ng ...

  3. web 部署专题(六):nginx 安装(二) linux

    https://www.cnblogs.com/quzq/p/12131696.html 基础篇 一.环境 服务器版本:CentOS 7.2 为了保证学习阶段不遇到奇怪的事情,请保证以下四点(大神选择 ...

  4. Nginx源码研究六:NGINX的配置文件分析

    上一篇写到nginx的各个模块的配置信息的存储结构,大体描述了对配置信息的配置项生成,定制,初始化过程.这里重点研究实现定制的过程,所谓实现定制,这里指的是,nginx系统提供使用者定义nginx的配 ...

  5. Nginx详解二十六:Nginx架构篇之性能优化

    一.性能优化考虑点 1.当前系统结构瓶颈 通过压力测试观察指标.日志检测.性能分析 2.了解业务模式 接口业务类型.系统层次化结构 3.性能与安全 二.ab接口压力测试工具 1.安装:yum -y i ...

  6. Nginx详解十六:Nginx场景实践篇之缓存服务

    缓存类型: 服务端缓存 代理缓存 客户端缓存 Nginx代理缓存 配置语法 使用之前需要先定义一个proxy_cache_path配置语法:proxy_cache_path path [levels= ...

  7. 第一次项目上Linux服务器(六:Nginx安装及相关命令(转))

    1.下载nginx 方法一 wget http://nginx.org/download/nginx-1.11.6.tar.gz 方法二 http://nginx.org/en/download.ht ...

  8. 第十六篇 nginx主配置文件参数解释

    # 指定拥有运行nginx权限的用户 #user nobody; # 指定开启的进程数,建议设置为CPU核心数 worker_processes ; # 指定全局错误日志级别,包括:debug/inf ...

  9. Nginx(六):配置解析之location解析

    nginx成为非常流行的代理服务软件,最根本的原因也许是在于其强悍性能.但还有一些必要的条件,比如功能的完整,配置的易用,能够解决各种各样的实际需求问题,这些是一个好的软件的必备特性. 那么,今天我们 ...

随机推荐

  1. ImageMagick

    http://blog.csdn.net/lan861698789/article/details/7738383 1.官网 http://www.imagemagick.org/script/ind ...

  2. 0基础一分钟入门Python

    这篇文章面向所有想学python的小伙伴(甚至你从没听过编程),这篇文章将会带你以最快的速度入门python.赶快上车,时间来不及了... 一,下载和安装python 1.下载: 1.1 python ...

  3. Hibernate JPA 动态criteria语句针对null查询条件的特殊处理

    最近原Hibernate项目需要添加一个条件,结构有点类似下面的格式,学生和房间是多对一的关系,现在要查询所有没有房间的学生. Class Student{ @ManyToOne Room room; ...

  4. JDBC连接数据库时候出错

    错误提示如下: Fri May 13 09:06:04 CST 2016 WARN: Establishing SSL connection without server's identity ver ...

  5. linux小实验-考勤模拟程序

    任务: 设计一个考勤模拟程序,实现如下功能选择界面,要求使用函数 1.上班签到 2.下班签出 3.缺勤信息查阅 4.退出 考勤程序运行后,提示用户输入上述功能选择,并验证用户输入的用户名和密码:用户信 ...

  6. css中的行高line-height

    “行高”顾名思意指一行文字的高度.具体来说是指两行文字间基线之间的距离.基线实在英文字母中用到的一个概念,我们刚学英语的时使用的那个英语本子每行有四条线,其中底部第二条线就是基线,是a,c,z,x等字 ...

  7. ASP.NET(C#) Repeater分页的实现

    ASP.NET(C#) Repeater分页的实现 第一种方式: 数据库连接代码: using System; using System.Data; using System.Configuratio ...

  8. 在C# 中 如何限制在文本框(textBox)中输入的类型为正整数

    在文本框的 KeyPress 事件中写下这些代码就可以保证是正整数了 private void textBox1_KeyPress(object sender, KeyPressEventArgs e ...

  9. SpringBoot自定义拦截器实现IP白名单功能

    SpringBoot自定义拦截器实现IP白名单功能 转载请注明源地址:http://www.cnblogs.com/funnyzpc/p/8993331.html 首先,相关功能已经上线了,且先让我先 ...

  10. python new和init知识点

    __new__ 方法是什么?如果将类比喻为工厂,那么__init__()方法则是该工厂的生产工人,__init__()方法接受的初始化参 数则是生产所需原料,__init__()方法会按照方法中的语句 ...