加载中...

使用动态DNS来完成HTTP请求


使用动态DNS来完成HTTP请求

其实针对大多应用场景,DNS是不会频繁变更的,使用nginx默认的resolver配置方式就能解决。

在奇虎360企业版的应用场景下,需要支持的系统众多:win、centos、ubuntu等,不同的操作系统获取dns的方法都不太一样。再加上我们使用docker,导致我们在容器内部获取dns变得更加难以准确。

如何能够让Nginx使用随时可以变化的DNS源,成为我们急待解决的问题。

当我们需要在某一个请求内部发起这样一个http查询,采用proxy_pass是不行的(依赖resolver的dns,如果dns有变化,必须要重新加载配置),并且由于proxy_pass不能直接设置keepconn,导致每次请求都是短链接,性能损失严重。

使用resty.http,目前这个库只支持ip:port的方式定义url,其内部实现并没有支持domain解析。resty.http是支持set_keepalive完成长连接,这样我们只需要让他支持dns解析就能有完美解决方案了。

  1. local resolver = require "resty.dns.resolver"
  2. local http = require "resty.http"
  3. function get_domain_ip_by_dns( domain )
  4. -- 这里写死了google的域名服务ip,要根据实际情况做调整(例如放到指定配置或数据库中)
  5. local dns = "8.8.8.8"
  6. local r, err = resolver:new{
  7. nameservers = {dns, {dns, 53} },
  8. retrans = 5, -- 5 retransmissions on receive timeout
  9. timeout = 2000, -- 2 sec
  10. }
  11. if not r then
  12. return nil, "failed to instantiate the resolver: " .. err
  13. end
  14. local answers, err = r:query(domain)
  15. if not answers then
  16. return nil, "failed to query the DNS server: " .. err
  17. end
  18. if answers.errcode then
  19. return nil, "server returned error code: " .. answers.errcode .. ": " .. answers.errstr
  20. end
  21. for i, ans in ipairs(answers) do
  22. if ans.address then
  23. return ans.address
  24. end
  25. end
  26. return nil, "not founded"
  27. end
  28. function http_request_with_dns( url, param )
  29. -- get domain
  30. local domain = ngx.re.match(url, [[//([\S]+?)/]])
  31. domain = (domain and 1 == #domain and domain[1]) or nil
  32. if not domain then
  33. ngx.log(ngx.ERR, "get the domain fail from url:", url)
  34. return {status=ngx.HTTP_BAD_REQUEST}
  35. end
  36. -- add param
  37. if not param.headers then
  38. param.headers = {}
  39. end
  40. param.headers.Host = domain
  41. -- get domain's ip
  42. local domain_ip, err = get_domain_ip_by_dns(domain)
  43. if not domain_ip then
  44. ngx.log(ngx.ERR, "get the domain[", domain ,"] ip by dns failed:", err)
  45. return {status=ngx.HTTP_SERVICE_UNAVAILABLE}
  46. end
  47. -- http request
  48. local httpc = http.new()
  49. local temp_url = ngx.re.gsub(url, "//"..domain.."/", string.format("//%s/", domain_ip))
  50. local res, err = httpc:request_uri(temp_url, param)
  51. if err then
  52. return {status=ngx.HTTP_SERVICE_UNAVAILABLE}
  53. end
  54. -- httpc:request_uri 内部已经调用了keepalive,默认支持长连接
  55. -- httpc:set_keepalive(1000, 100)
  56. return res
  57. end

动态DNS,域名访问,长连接,这些都具备了,貌似可以安稳一下。在压力测试中发现这里面有个机制不太好,就是对于指定域名解析,每次都要和DNS服务回话询问IP地址,实际上这是不需要的。普通的浏览器,都会对DNS的结果进行一定的缓存,那么这里也必须要使用了。

对于缓存实现代码,请参考ngx_lua相关章节,肯定会有惊喜等着你挖掘碰撞。


还没有评论.