在 skynet 的底层,当使用域名而不是 ip 时,由于调用了系统 api getaddrinfo ,有可能阻塞住整个 socket 线程(不仅仅是阻塞当前服务,而是阻塞整个 skynet 节点的网络消息处理)。虽然大多数情况下,我们并不需要向外主动建立连接。但如果你使用了类似 httpc 这样的模块以域名形式向外请求时,一定要关注这个问题。
skynet 暂时不打算在底层实现非阻塞的域名查询。但提供了一个上层模块来辅助你解决 dns 查询时造成的线程阻塞问题。
local dns = require "skynet.dns"
在使用前,必须设置 dns 服务器。
dns.server(ip, port) : port 的默认值为 53 。如果不填写 ip 的话,将从 /etc/resolv.conf 中找到合适的 ip 。
dns.resolve(name, ipv6) : 查询 name 对应的 ip ,如果 ipv6 为 true 则查询 ipv6 地址,默认为 false 。如果查询失败将抛出异常,成功则返回 ip ,以及一张包含有所有 ip 的 table 。
dns.flush() : 默认情况下,模块会根据 TTL 值 cache 查询结果。在查询超时的情况下,也可能返回之前的结果。
dns.flush()
可以用来清空 cache 。注意:cache 保存在调用者的服务中,并非针对整个 skynet 进程。所以,推荐写一个独立的 dns 查询服务统一处理 dns 查询。
11.1 DNS简单使用
示例代码:test_dns.lua
local skynet = require "skynet"
local dns = require "skynet.dns"
skynet.start(function()
skynet.error("nameserver:", dns.server()) --设置DNS服务器地址
-- you can specify the server like dns.server("8.8.4.4", 53)
local ip, ips = dns.resolve "github.com" --调用成功,则把结果缓存到这个服务的内存中,便于下次使用
skynet.error("dns.resolve return:", ip)
for k,v in ipairs(ips) do
skynet.error("github.com",v)
end
dns.flush()
end)
运行结果:
$ ./skynet examples/config test_dns [:01000010] LAUNCH snlua test_dns [:01000010] nameserver: 127.0.1.1 [:01000010] dns.resolve return: 192.30.255.112 #返回查询到的ip地址 [:01000010] github.com 192.30.255.112 [:01000010] github.com 192.30.255.113
11.2 封装一个DNS服务
由于每个服务去调用dns接口查询IP时都会在这个服务上缓存一份,下次查询的时候速度就会快很多,但是如果每个服务都保存一份,显示是浪费了资源空间,下面我们来封装用"lua"消息进行查询的DNS服务。
示例代码:dnsservice.lua
local skynet = require "skynet"
require "skynet.manager"
local dns = require "skynet.dns"
local command = {}
function command.FLUSH()
return dns.flush()
end
function command.GETIP(domain)
return dns.resolve(domain)
end
skynet.start(function()
dns.server()
skynet.dispatch("lua", function(session, address, cmd, ...)
cmd = cmd:upper()
local f = command[cmd]
if f then
skynet.retpack(f(...))
else
skynet.error(string.format("Unknown command %s", tostring(cmd)))
end
end)
skynet.register ".dnsservice"
end)
测试代码:testdnsservice.lua
local skynet = require "skynet"
local cmd,domain = ...
function task()
local r, ips = skynet.call(".dnsservice", "lua", cmd, domain)
skynet.error("dnsservice Test:", domain, r)
skynet.exit()
end
skynet.start(function()
skynet.fork(task)
end)
运行结果:
$ ./skynet examples/config dnsservice [:01000010] LAUNCH snlua dnsservice testdnsservice getip www.baidu.com [:01000012] LAUNCH snlua testdnsservice getip www.baidu.com [:01000012] dnsservice Test 14.215.177.39 [:01000012] KILL self