OpenResty 為什么我們的域名不能被解析

2021-08-13 10:14 更新

最近經(jīng)常有朋友在使用一個域名地址時發(fā)現(xiàn)無法被正確解析

比如在使用 Mysql 實例時某些云會給一個私有的域名搭配自有的 ?nameserver ?使用

local client = mysql:new()
client:connect({
    host = "rdsmxxxxxx.mysql.rds.xxxx.com",
    port = 3306,
    database = "test",
    user = "test",
    password = "123456"
})

以上代碼在直接使用時往往會報一個無法解析的錯誤。那么怎么在 ?openresty ?中使用域名呢

搭配 resolver 指令

我們可以直接在 ?nginx ?的配置文件中使用 ?resolver ?指令直接設(shè)置使用的 ?nameserver ?地址。

官方文檔中是這么描述的

Syntax:    resolver address ... [valid=time] [ipv6=on|off];
Default:     —
Context:    http, server, location

一個簡單的例子

resolver 8.8.8.8 114.114.114.114 valid=3600s;

不過這樣的問題在于 ?nameserver ?被寫死在配置文件中,如果使用場景比較復(fù)雜或有內(nèi)部 ?dns ?服務(wù)時維護(hù)比較麻煩。

進(jìn)階玩法

我們的代碼常常運行在各種云上,為了減少維護(hù)成本,我采用了動態(tài)讀取本機 ?/etc/resolv.conf? 的方法來做。

廢話不說,讓我們一睹為快。

local pcall = pcall
local io_open = io.open
local ngx_re_gmatch = ngx.re.gmatch

local ok, new_tab = pcall(require, "table.new")

if not ok then
    new_tab = function (narr, nrec) return {} end
end

local _dns_servers = new_tab(5, 0)

local _read_file_data = function(path)
    local f, err = io_open(path, 'r')

    if not f or err then
        return nil, err
    end

    local data = f:read('*all')
    f:close()
    return data, nil
end

local _read_dns_servers_from_resolv_file = function()
    local text = _read_file_data('/etc/resolv.conf')

    local captures, it, err
    it, err = ngx_re_gmatch(text, [[^nameserver\s+(\d+?\.\d+?\.\d+?\.\d+$)]], "jomi")

    for captures, err in it do
        if not err then
            _dns_servers[#_dns_servers + 1] = captures[1]
        end
    end
end

_read_dns_servers_from_resolv_file()

通過上述代碼我們成功動態(tài)拿到了一組 ?nameserver ?的地址,下面就可以通過 ?resty.dns.resolver? 大殺四方了

local require = require
local ngx_re_find = ngx.re.find
local lrucache = require "resty.lrucache"
local resolver = require "resty.dns.resolver"
local cache_storage = lrucache.new(200)

local _is_addr = function(hostname)
    return ngx_re_find(hostname, [[\d+?\.\d+?\.\d+?\.\d+$]], "jo")
end

local _get_addr = function(hostname)
    if _is_addr(hostname) then
        return hostname, hostname
    end

    local addr = cache_storage:get(hostname)

    if addr then
        return addr, hostname
    end

    local r, err = resolver:new({
        nameservers = _dns_servers,
        retrans = 5,  -- 5 retransmissions on receive timeout
        timeout = 2000,  -- 2 sec
    })

    if not r then
        return nil, hostname
    end

    local answers, err = r:query(hostname, {qtype = r.TYPE_A})

    if not answers or answers.errcode then
        return nil, hostname
    end

    for i, ans in ipairs(answers) do
        if ans.address then
            cache_storage:set(hostname, ans.address, 300)
            return ans.address, hostname
        end
    end

    return nil, hostname
end

ngx.say(_get_addr("www.baidu.com"))
ngx.say(_get_addr("192.168.0.1"))

我這邊把解析的結(jié)果放入了 ?lrucache ?緩存了 5 分鐘,你們同樣可以把結(jié)果放入 ?shared ?中來減少 ?worker ?查詢次數(shù)。

高階玩法

現(xiàn)在我們已經(jīng)實現(xiàn)了自緩存體系的 ?dns ?查詢,如果搭配 ?resty.http? 就會達(dá)到更好的效果。

一個簡單的例子是,通過解析 ?uri ?得到 ?hostname?、?port?、?path?,把 ?hostname ?扔給自緩存 ?dns? 獲取結(jié)果

發(fā)起 ?request ?請求,?addr ?+ ?port? connect 之,設(shè)置 ?header ?的 ?host ?為 ?hostname?,?path ?等值來實現(xiàn) ?ip ?直接訪問等高階用法。

這里就不過多的闡述了。

最終的演示例子如下

local client = mysql:new()
client:connect({
    host = _get_addr(conf.mysql_hostname),
    port = 3306,
    database = "test",
    user = "test",
    password = "123456"
})

如何使用 /etc/hosts 自定義域名

還有些同學(xué)可能會在 ?hosts ?文件中自定義域名和 ?ip?,這時候 ?resolve ?是無法正常解析的。

這個時候可以借助 ?dnsmasq ?這個服務(wù)來緩存我們的 ?dns ?結(jié)果,?hosts ?的定義可以被該服務(wù)識別。

需要在 ?nginx ?的配置文件中,設(shè)置 ?resolver? 為 ?dnsmasq? 服務(wù)的監(jiān)聽地址即可。

See: http://hambut.com/2016/09/09/how-to-resolve-the-domain-name-in-openresty/


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號