在Nodejs上使用DNS进行领事服务发现

TL; DR

大家好,我试图通过Consul DNS接口从一个nodejs前端调用nodejs后端微服务,但我有错误。

我正在使用nodejs dns api为单一节点应用程序设置dns,以便随后对本地Consul DNS接口执行dns.resolve()调用。

目标

我希望能够向我的后端服务发出http请求,而不需要在我的客户端代码中连接其IP和端口。 我不想编写一个自定义代码来查询Consul HTTP API,以便随时调用我的服务来获取我的服务的ip:port对。

问题

问题是,当我使用axios (类似于请求 )对后端服务进行HTTP调用时,我总是得到一个错误,因为它无法parsing地址。 看来Axios并没有使用我之前设置的dns:

dns.setServers(['127.0.0.1:8600']); 

Update_1

将虚拟机的dns(/etc/resolv.conf)设置为localhost,使用默认的dns使用-recursor命令行选项,一切正常! 我仍然想知道我在做什么错误设置只在我的nodejs应用程序的DNS。

build立

  • 1节点与一个nodejs进程运行一个简单的Web服务器与Expressjs。 在app.get('/')路由中,它通过consul和axios(如request)对一个名为be01的后端服务进行REST POST调用。
  • 2 BE节点与一个nodejs进程运行一个简单的Web服务器与Expressjs暴露REST API。
  • 1 Consul节点与Consul作为服务器运行。
  • 每个节点都有自己的领事代理正在运行并join到集群中。
  • TCP端口正确打开

这是服务器: 在这里输入图像说明

这是来自Consul UI: 在这里输入图像说明

在FE节点上运行领事成员我得到:

 agent-server 10.0.1.7:8301 alive server 1.0.1 2 dc1 <all> agent-be-01 10.0.1.5:8301 alive client 1.0.1 2 dc1 <default> agent-be-02 10.0.1.6:8301 alive client 1.0.1 2 dc1 <default> agent-fe 10.0.1.4:8301 alive client 1.0.1 2 dc1 <default> 

如果我在FE节点上运行dig @localhost -p 8600 be01.service.consul SRV ,我正确地得到了这个结果( 如在Consul文档中 ):

 root@NGINXLB:~/node8080$ dig @localhost -p 8600 be01.service.consul SRV ; <<>> DiG 9.9.5-3ubuntu0.16-Ubuntu <<>> @localhost -p 8600 be01.service.consul SRV ; (1 server found) ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 43367 ;; flags: qr aa rd; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 5 ;; WARNING: recursion requested but not available ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 4096 ;; QUESTION SECTION: ;be01.service.consul. IN SRV ;; ANSWER SECTION: be01.service.consul. 0 IN SRV 1 1 8081 agent-be-02.node.dc1.consul. be01.service.consul. 0 IN SRV 1 1 8080 agent-be-01.node.dc1.consul. ;; ADDITIONAL SECTION: agent-be-02.node.dc1.consul. 0 IN A 10.0.1.6 agent-be-02.node.dc1.consul. 0 IN TXT "consul-network-segment=" agent-be-01.node.dc1.consul. 0 IN A 10.0.1.5 agent-be-01.node.dc1.consul. 0 IN TXT "consul-network-segment=" ;; Query time: 2 msec ;; SERVER: 127.0.0.1#8600(127.0.0.1) ;; WHEN: Fri Dec 01 10:09:00 UTC 2017 ;; MSG SIZE rcvd: 246 

这是BE服务代码:

 var express = require('express'); var bodyParser = require('body-parser'); var app = express(); var jsonParser = bodyParser.json() var urlencodedParser = bodyParser.urlencoded({ extended: false }) app.post('/api/getUserTiles', jsonParser, function (req, res) { console.log("> API request for '/api/getUserTiles'"); if (!req.body) { return res.sendStatus(400) } else { var user = req.body.user; console.log("User: " + user); res.json({ "authorizedTiles": [ {"tileID": "profile"} ,{"tileID": "search"} ,{"tileID": "test"} ], "email": "max@google.com", "userName":"Max", "has_error": false, "error_message": "" }); } }); var server = app.listen(8080, function () { var host = server.address().address var port = server.address().port console.log("Example app listening at http://%s:%s", host, port) }) 

使用其卷发的IP:端口从FE调用它可以毫无问题地工作:

 root@NGINXLB:~/node8080$ curl -d="user=Max" -X POST http://10.0.1.5:8080/api/getUserTiles {"authorizedTiles":[{"tileID":"profile"},{"tileID":"search"},{"tileID":"test"}],"email":"max@google.com","userName":"Max","has_error":false,"error_message":""} 

有限元节点上的Web服务简化了,就是这样的:

 var axios = require('axios'); var dns = require('dns'); var consul = require('consul')(); dns.setServers(['127.0.0.1:8600']); //console.log(dns.getServers()); dns.resolveSrv("be01.service.consul", function(err, records){ console.log("\nDNS SRV query"); if(err){ console.log(err); }else{ console.log(records); } }); consul.health.service({ "service": "be01", "dc": "dc1", "passing": true }, function(err, result){ console.log("\nConsul.health.service query:"); if(err){console.log(err); throw err;} else if(result.length > 0){ for(var i=0; i<result.length; i++){ console.log(result[i].Node.Address + ":" + result[i].Service.Port); } } }); axios.post("http://be01.service.consul/api/getUserTiles", {'user':'Max'}) .then(function(response){ if (response.data.has_error) { console.log("\nRESPONSE HAS ERROR!"); }else { console.log("\nSUCCESS!"); } }) .catch(function(err) { console.log("\nERROR CALLING THE BE SERVICE"); }); 

运行它我得到以下结果:

 root@NGINXLB:~/node8080$ node app.js DNS SRV query [ { name: 'agent-be-01.node.dc1.consul', port: 8080, priority: 1, weight: 1 }, { name: 'agent-be-02.node.dc1.consul', port: 8081, priority: 1, weight: 1 } ] Consul.health.service query: 10.0.1.5:8080 10.0.1.6:8081 ERROR CALLING THE BE SERVICE { Error: getaddrinfo ENOTFOUND be01.service.consul be01.service.consul:80 at errnoException (dns.js:50:10) at GetAddrInfoReqWrap.onlookup [as oncomplete] (dns.js:92:26) code: 'ENOTFOUND', errno: 'ENOTFOUND', syscall: 'getaddrinfo', hostname: 'be01.service.consul', host: 'be01.service.consul', port: 80, ... ... 

结论

正如你所看到的,nodejs客户端正在调用be服务,但是它试图parsing'be01.service.consul'失败。 此外,它使用端口80,而不是由领事DNS接口提供的8080或8081。 我错过了什么?

问题是axios正在使用dns.lookup ,它不使用dns.setServers设置的服务器。 dns.lookupdns.resolve不使用相同的机制来parsing 。

在纯节点中,可能的select是在调用axios之前使用dns.resolve*将域名parsing为IP,或者像这个拦截器的例子 (我没有尝试过)。

可能更好的解决scheme是不要在节点中做这个,而是使用本地运行的consul代理的bind选项来执行dnsparsing。

Interesting Posts