工作中使用openresty,使用第三方服务API通过域名访问。但是,域名通过DNS解析出来之后,在openresty是有

配置解析阶段

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

upstream example.com {
server test.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 地址等信息。

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

location / {
set $myupstream www.baidu.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 进程独享解析缓存.

运行时 balancer_by_lua_file

使用 OpenResty 做反向代理的传统模式是在配置文件的 upstream{ } 块里书写多个服务器定义集群。这种方式不够灵活,增加服务器必须手动修改配置后重启 OpenResty,会影响正常服务。

OpenResty 的 “balancer_by_lua” 指令让动态负载均衡称为可能,它替代了原生的 hash/ip_hash/least_conn 等算法,不仅可以让自由定制负载均衡策略,还可以随意调整后端服务器的数量,完全超越了 upstream 系列指令,实现了接近商业版 Nginx Plus 的功能。

使用方式

upstream dyn_backend {                          # 动态上游集群
    server 0.0.0.0;                         # 占位用,无实际意义
    balancer_by_lua_file service/proxy/balancer.lua;         # 执行负载均衡的 Lua 代码
    keepalive 10;                          # 需在 balancer 指令之后
}

“balancer_by_lua” 也是一个比较特殊的执行阶段,在这里不能使用 ngx.sleep、ngx.req.* 或 coocket,同时应当尽量避免大计算量操作或磁盘读写,否则会导致阻塞。

动态负载均衡使用的服务器列表通常存储在外部的 Redis 或 MySQL 里,由于不能直接使用 cosocker,所以在 “balancer_by_lua” 里也就不能操作这些服务器。但这并不是什么大问题,完全可以在其他的阶段(例如 access_by_lua、ngx.timer)里访问服务器获取列表、解析域名,然后放在 ngx.ctx 或全局模块里传递过来。

支持版本

This directive was first introduced in the v0.10.0 release.

功能接口

在 “balancer_by_lua” 里除了基本的 ngx.* 功能接口外,主要使用的是库 ngx.balancer,它必须显式加载后才能使用,即:

local balancer - require "ngx.balancer"                             -- 显式加载 ngx.balancer 库

ngx.balancer 提供四个函数:

  • set_current_peer:设置使用的后端服务器,必须是 IP 地址,不能是域名;
  • set_timeouts:设置后端的连接和读写超时时间,单位是秒;
  • set_more_tries:设置连接失败后的重试次数;
  • get_last_failure:获取上一次连接失败的具体原因。

这几个函数的的用法都很简单,动态负载均衡的重点其实是服务器列表的维护和选择算法,这些工作通常应该在 “balancer_by_lua” 之外完成,ngx.balancer 只是最后的执行者。

下面的代码是 ngx.balancer 的典型用法,使用了固定的服务器列表和随机数来选择后端,实际应用应该替换为动态更新的数据和更有意义的算法:

local servers = {                                                   -- 简单的服务器列表,IP 地址
{"127.0.0.1", }, -- 实际上应该从 Redis
{"127.0.0.1", }, -- 等服务器里动态加载
} balancer.set_timeouts(, 0.5, 0.5) -- 后端的连接和读写超时时间
balancer.set_more_tries() -- 连接失败后最多在重试 次 local n = math.random(#servers) -- 这里使用随机算法作为示例 local ok, err = balancer.set_current_peer( -- 设置使用的后端服务器
servers[n][], servers[n][]) -- 使用 IP 地址和端口号 if not ok then -- 检查是否设置成功
ngx.log(ngx.ERR, "failed to set peer: ", err)
return ngx.exit()
end

另一种实现方式是把负载均衡算法的主要计算工作放在 “access_by_lua” 等阶段里完成,这样更加灵活,计算出的后端服务器地址等数据放在 ngx.var 或 ngx.ctx 里传递,“balancer_by_lua” 阶段只需要少量的代码:

local server = ngx.ctx.server                                       -- 之前计算得到的后端服务器
if balancer.get_last_failure() then -- 后端出错,需要重新选择
server = ... -- 重新计算后端服务器
end local ok, err = balancer.set_current_peer(server[1], server[2]) -- 通常无需在计算,直接设置,IP 地址和端口号

动态域名使用

upstream dynamicBackend {
server 0.0.0.1; # just an invalid address as a place holder
balancer_by_lua_file 'conf/dynamic_domain/balancer_handle.lua';
keepalive ; # connection pool
}

location / {
    proxy_connect_timeout 5s;
    proxy_send_timeout 10s;
    proxy_read_timeout 30s;

    #默认值为0:重试次数不受限制
    proxy_next_upstream_tries 2;

    #默认情况下只有GET请求会重试(基于这样的考虑:只有GET请求才是幂等)
    proxy_next_upstream error timeout non_idempotent;

    access_by_lua_file 'conf/access_handle.lua';

    log_by_lua_file 'conf/log_handle.lua';

    proxy_pass $proxy_scheme://dynamicBackend;

}

  

openresty域名动态解析的更多相关文章

  1. 使用 DNSPOD API 实现域名动态解析

    0. 简单概述在家里放一个 NAS 服务器,但是宽带的 IP 地址经常改变,一般路由器自带的花生壳域名解析可以解决,如果路由器没有类似功能或者想使用自己的域名,可以尝试使用 DNSPOD API 来实 ...

  2. 域名动态解析到动态IP

    一般宽带用户的IP都是动态IP,重连之后IP可能会发生变化. 如果想在其他地方连接家里的设备,或者在家中搭建服务器,就会受到影响. 现在提供一种动态解析域名的方式,只要检测到IP的变化,那么就调用阿里 ...

  3. 通过阿里云域名动态解析 IP 地址

    这两天在家里用树莓派折腾了一个家用服务器,主要用来做 mac 的 Time Machine ,还有就是当做下载机和 nas ,想着平时上班时间家里没人用网络,空着也是空着,就可以利用空闲带宽下个美剧啥 ...

  4. Java动态解析域名

    Java动态解析域名 Java提供InetAddress类,可以对域名-IP进行正向.逆向解析. InetAddress解析的时候一般是调用系统自带的DNS程序. linux 默认的DNS方式是读取/ ...

  5. 基于 OpenResty 的动态服务路由方案

    2019 年 5 月 11 日,OpenResty 社区联合又拍云,举办 OpenResty × Open Talk 全国巡回沙龙武汉站,又拍云首席布道师在活动上做了< 基于 OpenResty ...

  6. WordPress搭建教程---购买域名+购买VPS主机+域名DNS解析+网站环境+上传网站程序

    WordPress搭建教程 购买域名---NameSilo 购买VPS主机---Vultr 域名DNS解析 网站环境 上传网站程序 参考文章: 1. WordPress搭建教程 https://zhu ...

  7. 加速你的网络!软路由构建 去AD+国内域名加速解析+抗污染+速度优选 与PSW无缝集成 综合方案

    本方案利用OpenWrt搭建4级DNS,实现 去AD + 国内域名加速解析 + 抗污染(域名解析按地区分流)+ 访问速度优选. 方案涉及部分软件配置细节可以参照之前博文:https://www.cnb ...

  8. 使用Newtonsoft.Json.dll(JSON.NET)动态解析JSON、.net 的json的序列化与反序列化(一)

    在开发中,我非常喜欢动态语言和匿名对象带来的方便,JSON.NET具有动态序列化和反序列化任意JSON内容的能力,不必将它映射到具体的强类型对象,它可以处理不确定的类型(集合.字典.动态对象和匿名对象 ...

  9. 理解AngularJS生命周期:利用ng-repeat动态解析自定义directive

    ng-repeat是AngularJS中一个非常重要和有意思的directive,常见的用法之一是将某种自定义directive和ng-repeat一起使用,循环地来渲染开发者所需要的组件.比如现在有 ...

随机推荐

  1. struts与springmvc有何区别

    Struts2与SpringMVC有何区别? (1)SpringMVC的核心控制器是基于servlet技术,而Struts2是基于filter. (2)Struts2是类级别的拦截, 一个类对应一个r ...

  2. 基于UDP的socket tcp和udp的区别(小白进击篇)

    目录 16.基于udp协议的socket通信 为什么udp不会有粘包现象 DGRAM datagram#数据报文 发送sento (发送的信息,发送给的地址) 接收revefrom 客户端 服务端 t ...

  3. 教你如何认识人脸识别开发套件中的双目摄像、3D结构光摄像头、单目摄像头的区别及详细讲解

    深圳市宁远电子提供的人脸识别模组可支持双目摄像头和3D结构光摄像头,在客户咨询中经常有被问到双目的为什么会比单目的成本高,区别在哪里,他们的适用于哪些场景呢?在此,深圳市宁远电子技术工程师就为大家详细 ...

  4. Entity Framework 6.0 入门系列 第一篇

    Entity Framework 6.0 入门系列 第一篇 好几年前接触过一些ef感觉不是很好用,废弃.但是 Entity Framework 6.0是经过几个版本优化过的产物,性能和功能不断完善,开 ...

  5. 试试 python-dotenv,避免敏感信息被硬编码到代码中

    我们开发的每个系统都离不开配置信息,例如数据库密码.Redis密码.邮件配置.各种第三方配置信息,这些信息都非常敏感,一旦泄露出去后果非常严重,被泄露的原因一般是程序员将配置信息和代码混在一起导致的. ...

  6. C#/Java 动态生成电子发票

    电子发票是电商时代的产物,PDF发票是最常见的电子发票之一.在这篇文章中,我将给大家分享一个免费的动态生成PDF电子发票的C#方案,并在文章末尾附上Java解决方案. 典型的发票包含客户和供应商的名称 ...

  7. IntelliJ IDEA 从入门到上瘾教程,2019图文版!

    前言:IntelliJ IDEA 如果说IntelliJ IDEA是一款现代化智能开发工具的话,Eclipse则称得上是石器时代的东西了. 其实笔者也是一枚从Eclipse转IDEA的探索者,随着近期 ...

  8. pyhton介绍、发展趋势、安装

    pyhton介绍.发展趋势.安装 一.python起源 ​ pyhton的创始人为吉多·范罗苏姆(Gudio van Rossum) (后文简称龟叔) ​ 1. 1989年的圣诞节期间,龟叔为了在阿姆 ...

  9. Nginx + fastcgi + php 的原理与关系

    CGI:Common Gateway Interface 公共网关接口,web服务器和脚本语言通信的一个标准.接口.协议[协议] FastCGI:CGI协议的升级版[协议] PHP-CGI: 实现了C ...

  10. [Python] Django框架入门4——深入模板

    说明: 本文主要深入了解模板(templates),主要涉及模板编写步骤.定义模板.模板继承.HTML转义.CSRF等. 一.模板 动态生成HTML.表达外观.实现业务逻辑(view)与显示内容(te ...