首页 文章

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

提问于
浏览
2

TL; DR

大家好,我正在尝试从Express编写的nodejs前端调用nodejs后端微服务,通过Consul DNS interface但是我有错误 .

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

目标

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

问题

问题是,当我使用axios(类似于request)对后端服务进行HTTP调用时,我总是会收到错误,因为它无法解析地址 . 似乎Axios没有使用我之前设置的dns:

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

Update_1

使用-recursor命令行选项将虚拟机的dns(/etc/resolv.conf)设置为localhost,使用默认dns的consul一切正常!我仍然想了解我在我的nodejs应用程序上设置dns的错误 .

设置

  • 1具有nodejs进程的FE节点运行带有Expressjs的简单Web服务器 . 在app.get('/')路由中,它通过consul和axios(如请求)对名为be01的后端服务进行REST POST调用 .

  • 2 BE节点,nodejs进程运行一个简单的Web服务器,Expressjs暴露REST api .

  • 1 Consul节点,Consul作为服务器运行 .

  • 每个节点都有自己的consul代理运行并加入群集 .

  • TCP端口已正确打开

这是服务器:
enter image description here

这来自Consul UI:
enter image description here

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

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 ,我正确得到了这个结果(as in the Consul docs):

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)
})

使用带有curl的ip:port从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":""}

FE节点上的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服务,但它无法尝试解析'be01.service.consul' . 此外,它使用端口80而不是Consul DNS接口提供的8080或8081 . 我错过了什么?

1 回答

  • 1

    问题是axios正在使用 dns.lookup ,它不使用 dns.setServers 设置的服务器 . dns.lookupdns.resolveresolving不要使用相同的mechanism .

    在纯节点中,可能的选项是在调用axios之前使用 dns.resolve* 将域名解析为IP,或者像这样interceptor example(我还没试过) .

    可能更好的解决方案是不在节点中执行此操作,而是使用本地运行的consul agent的绑定选项来执行dns解析 .

相关问题