淡写流年 發表於 2019-8-23 10:42:00

openresty域名动态解析

<p> 工作中使用openresty,使用第三方服务API通过域名访问。但是,域名通过DNS解析出来之后,在openresty是有</p>
<h2>配置解析阶段</h2>
<p>很多时候我们会在 Nginx 配置文件里配置上一些域名,比如配置我们的上游服务器。</p>
<div class="highlight">
<pre><code class="language-nginx"><span class="k">upstream <span class="s">example.com <span class="p">{
    <span class="kn">server test<span class="s">.example.com<span class="p">;
<span class="p">}</span></span></span></span></span></span></span></code></pre>
</div>
<p>对于这类域名,Nginx 会在配置解析阶段就将其解析出来,接下来(请求处理过程)使用的都是当时解析得到的 IP。Nginx 核心有一个函数&nbsp;<code>ngx_parse_url</code>,负责对 url 格式进行分析,包括解析出主机名,端口号以及 URL path 等。针对 IPv4 的情况,它会调用&nbsp;<code>ngx_parse_inet_url</code>进行具体的解析任务,如果必要,最终它会调用到&nbsp;<code>ngx_inet_resolve_host</code>进行域名解析,<code>ngx_inet_resolve_host</code>&nbsp;大多情况下会使用&nbsp;getaddrinfo&nbsp;进行解析,最终向 /etc/resolv.conf 下所配置的 DNS server 发起解析请求。</p>
<p>归纳来说这个解析过程有两个特点,一是使用了系统配置的 DNS server;二是解析过程是同步且阻塞的,因此这种解析方式仅在&nbsp;Nginx 配置解析阶段会被使用。另外这种解析方式的缺点就是只解析一次,所以如果在 Nginx 运行过程中域名解析发生了改变也是无法感知到的,除非手动重启 Nginx 服务。</p>
<h2>运行时 DNS resolver</h2>
<p>Nginx 核心提供了一套供运行时使用的 DNS 解析机制,它充分契合 Nginx 的事件模型,同样是异步非阻塞的,并且提供了缓存机制。http、stream 和 mail 模块分别提供了配置指令(比如 http 模块提供的&nbsp;resolver),供我们配置相关 DNS server 地址等信息。</p>
<p>下面这个简单的反向代理配置,就会在进行代理前解析 www.baidu.com 这个域名。</p>
<div class="highlight">
<pre><code class="language-nginx"><span class="k">location <span class="s">/ <span class="p">{
    <span class="kn">set <span class="nv">$myupstream www.baidu.com<span class="s"><span class="p">;
    <span class="kn">proxy_http_version <span class="mi">1<span class="s">.1<span class="p">;
    <span class="kn">proxy_set_header <span class="s">Connection <span class="s">""<span class="p">;
    <span class="kn">proxy_pass <span class="s">http://<span class="nv">${myupstream}/index.html<span class="p">;
<span class="p">}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</div>
<p>注意如果直接在&nbsp;<code>proxy_pass</code>&nbsp;指令里写明需要代理的域名(即不使用变量的方式),那么域名解析就会发生在配置解析阶段了,即上面所讲的过程。这其实也是一种实现动态 upstream 的方式。</p>
<p>这套运行时 DNS resolver 其实是一个 DNS client 的角色,由它自己组织查询报文并发送给目标 DNS 服务器,同时支持解析 IPv6 地址(从&nbsp;<code>1.5.8</code>&nbsp;开始),支持反向地址解析和 SRV 解析。它把对每个域名的解析抽象为一棵红黑树的节点,包括任何必要的信息。同时这棵红黑树也充当着缓存,查询时会以域名作为 key,如果对应缓存是新鲜的,即会复用缓存,并且会对解析得到的地址顺序进行一定的回转后再提供给上层使用。如果没有缓存或者缓存过期,新的 DNS 请求会被构建并且发送。</p>
<p>当然,很多时候这套运行时的 DNS resolver 也不能完全满足需求:</p>
<ol>
<li>无法配置主备 DNS 服务器地址,我们在 resolver 指令里配置的地址都会按顺序被轮询到。</li>
<li>无法在 DNS 服务器故障或者网络质量不佳的情况下复用陈旧的缓存,这可能导致上层服务不可用。</li>
<li>每个 Nginx worker 进程独享解析缓存.</li>
</ol>
<h2>运行时 balancer_by_lua_file&nbsp;</h2>
<p>&nbsp; 使用 OpenResty 做反向代理的传统模式是在配置文件的 upstream{ } 块里书写多个服务器定义集群。这种方式不够灵活,增加服务器必须手动修改配置后重启 OpenResty,会影响正常服务。</p>
<p>&nbsp; OpenResty 的 “balancer_by_lua” 指令让动态负载均衡称为可能,它替代了原生的 hash/ip_hash/least_conn 等算法,不仅可以让自由定制负载均衡策略,还可以随意调整后端服务器的数量,完全超越了 upstream 系列指令,实现了接近商业版 Nginx Plus 的功能。</p>
<h4 id="id-动态域名解析-使用方式">&nbsp; &nbsp;使用方式&nbsp; &nbsp;</h4>
<table style="height: 115px; width: 1863px" border="0">
<tbody>
<tr>
<td><code class="bash plain">upstream dyn_backend {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><code class="bash comments">#&nbsp;动态上游集群</code><br><code class="bash spaces">&nbsp;&nbsp;&nbsp;&nbsp;</code><code class="bash plain">server 0.0.0.0;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><code class="bash comments">#&nbsp;占位用,无实际意义</code><br><code class="bash spaces">&nbsp;&nbsp;&nbsp;&nbsp;</code><code class="bash plain">balancer_by_lua_file service</code><code class="bash plain">/proxy/balancer</code><code class="bash plain">.lua;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><code class="bash comments">#&nbsp;执行负载均衡的 Lua 代码</code><br><code class="bash spaces">&nbsp;&nbsp;&nbsp;&nbsp;</code><code class="bash plain">keepalive 10;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><code class="bash comments">#&nbsp;需在 balancer 指令之后</code><br><code class="bash plain">}</code></td>





</tr>





</tbody>





</table>
<p class="auto-cursor-target">&nbsp; &nbsp;“balancer_by_lua” 也是一个比较特殊的执行阶段,在这里不能使用 ngx.sleep、ngx.req.* 或 coocket,同时应当尽量避免大计算量操作或磁盘读写,否则会导致阻塞。</p>
<p class="auto-cursor-target">&nbsp; &nbsp; 动态负载均衡使用的服务器列表通常存储在外部的 Redis 或 MySQL 里,由于不能直接使用 cosocker,所以在 “balancer_by_lua” 里也就不能操作这些服务器。但这并不是什么大问题,完全可以在其他的阶段(例如 access_by_lua、ngx.timer)里访问服务器获取列表、解析域名,然后放在 ngx.ctx 或全局模块里传递过来。</p>
<p class="auto-cursor-target">&nbsp; <strong>支持版本</strong></p>
<p class="auto-cursor-target">&nbsp; &nbsp;&nbsp;This directive was first introduced in the&nbsp;<code>v0.10.0</code>&nbsp;release.</p>
<h4 class="auto-cursor-target">&nbsp;功能接口</h4>
<p>&nbsp;在 “balancer_by_lua” 里除了基本的 ngx.* 功能接口外,主要使用的是库 ngx.balancer,它必须显式加载后才能使用,即:</p>
<div class="cnblogs_code">
<pre>local balancer - require <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">ngx.balancer</span><span style="color: rgba(128, 0, 0, 1)">"</span>                           -- 显式加载 ngx.balancer 库</pre>
</div>
<p>ngx.balancer 提供四个函数:</p>
<ul>
<li>set_current_peer:设置使用的后端服务器,必须是 IP 地址,不能是域名;</li>
<li>set_timeouts:设置后端的连接和读写超时时间,单位是秒;</li>
<li>set_more_tries:设置连接失败后的重试次数;</li>
<li>get_last_failure:获取上一次连接失败的具体原因。</li>
</ul>
<p>&nbsp;这几个函数的的用法都很简单,动态负载均衡的重点其实是服务器列表的维护和选择算法,这些工作通常应该在 “balancer_by_lua” 之外完成,ngx.balancer 只是最后的执行者。</p>
<p>&nbsp;下面的代码是 ngx.balancer 的典型用法,使用了固定的服务器列表和随机数来选择后端,实际应用应该替换为动态更新的数据和更有意义的算法:</p>
<div class="cnblogs_code">
<pre>local servers = {                                                   --<span style="color: rgba(0, 0, 0, 1)"> 简单的服务器列表,IP 地址
    {</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">127.0.0.1</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(128, 0, 128, 1)">80</span>},                                              --<span style="color: rgba(0, 0, 0, 1)"> 实际上应该从 Redis
    {</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">127.0.0.1</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(128, 0, 128, 1)">81</span>},                                              --<span style="color: rgba(0, 0, 0, 1)"> 等服务器里动态加载
}

balancer.set_timeouts(</span><span style="color: rgba(128, 0, 128, 1)">1</span>, <span style="color: rgba(128, 0, 128, 1)">0.5</span>, <span style="color: rgba(128, 0, 128, 1)">0.5</span>)                                  --<span style="color: rgba(0, 0, 0, 1)"> 后端的连接和读写超时时间
balancer.set_more_tries(</span><span style="color: rgba(128, 0, 128, 1)">2</span>)                                          -- 连接失败后最多在重试 <span style="color: rgba(128, 0, 128, 1)">2</span><span style="color: rgba(0, 0, 0, 1)"> 次

local n </span>= math.random(#servers)                                     --<span style="color: rgba(0, 0, 0, 1)"> 这里使用随机算法作为示例

local ok, err </span>= balancer.set_current_peer(                        --<span style="color: rgba(0, 0, 0, 1)"> 设置使用的后端服务器
                servers[</span><span style="color: rgba(128, 0, 128, 1)">1</span>], servers[<span style="color: rgba(128, 0, 128, 1)">2</span>])                     --<span style="color: rgba(0, 0, 0, 1)"> 使用 IP 地址和端口号

</span><span style="color: rgba(0, 0, 255, 1)">if</span> not ok then                                                      --<span style="color: rgba(0, 0, 0, 1)"> 检查是否设置成功
    ngx.log(ngx.ERR, </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">failed to set peer: </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, err)
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> ngx.exit(<span style="color: rgba(128, 0, 128, 1)">500</span><span style="color: rgba(0, 0, 0, 1)">)
end</span></pre>
</div>
<p>&nbsp;</p>
<p class="auto-cursor-target">&nbsp;另一种实现方式是把负载均衡算法的主要计算工作放在 “access_by_lua” 等阶段里完成,这样更加灵活,计算出的后端服务器地址等数据放在 ngx.var 或 ngx.ctx 里传递,“balancer_by_lua” 阶段只需要少量的代码:</p>
<div class="cnblogs_code">
<pre>local server = ngx.ctx.server                                       --<span style="color: rgba(0, 0, 0, 1)"> 之前计算得到的后端服务器
</span><span style="color: rgba(0, 0, 255, 1)">if</span> balancer.get_last_failure() then                                 --<span style="color: rgba(0, 0, 0, 1)"> 后端出错,需要重新选择
    server </span>= ...                                                    --<span style="color: rgba(0, 0, 0, 1)"> 重新计算后端服务器
end


local ok, err </span>= balancer.set_current_peer(server, server)   --<span> 通常无需在计算,直接设置,IP 地址和端口号 </span></pre>
</div>
<p>&nbsp;</p>
<h3 class="auto-cursor-target">&nbsp;动态域名使用</h3>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">upstream dynamicBackend {
    server </span><span style="color: rgba(128, 0, 128, 1)">0.0</span>.<span style="color: rgba(128, 0, 128, 1)">0.1</span>; # just an invalid address <span style="color: rgba(0, 0, 255, 1)">as</span><span style="color: rgba(0, 0, 0, 1)"> a place holder
    balancer_by_lua_file </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">conf/dynamic_domain/balancer_handle.lua</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;
    keepalive </span><span style="color: rgba(128, 0, 128, 1)">100</span><span style="color: rgba(0, 0, 0, 1)">; # connection pool
}</span></pre>
<p> location / {<br>                proxy_connect_timeout 5s;<br>                proxy_send_timeout 10s;<br>                proxy_read_timeout 30s;</p>
<p>                #默认值为0:重试次数不受限制<br>                proxy_next_upstream_tries 2;</p>
<p>                #默认情况下只有GET请求会重试(基于这样的考虑:只有GET请求才是幂等)<br>                proxy_next_upstream error timeout non_idempotent;</p>
<p>                access_by_lua_file 'conf/access_handle.lua';</p>
<p>                log_by_lua_file 'conf/log_handle.lua';</p>
<p>    proxy_pass $proxy_scheme://dynamicBackend;</p>
<p>      }</p>


</div>
<p>&nbsp;</p>
<p class="auto-cursor-target">  </p>
<p class="auto-cursor-target">&nbsp;</p><br><br>
来源:https://www.cnblogs.com/blackEyeProgram/p/11398621.html
頁: [1]
查看完整版本: openresty域名动态解析