node.js - 在 Nodejs 上使用 DNS 进行 Consul 服务发现

标签 node.js dns consul service-discovery

长话短说

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

我正在使用 nodejs dns api为唯一 Node 应用程序设置 dns,以便对本地 Consul DNS 接口(interface)进行后续 dns.resolve() 调用。

目标

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

问题

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

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

更新_1

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

设置

  • 1 个 FE Node ,带有 nodejs 进程,运行带有 Expressjs 的简单网络服务器。在 app.get('/') 路由中,它通过 consul 和 axios(如请求)对名为 be01 的后端服务进行 REST POST 调用。
  • 2 是一个带有 nodejs 进程的 Node ,运行一个简单的网络服务器,带有 Expressjs 公开 REST api。
  • 1 个 Consul Node ,Consul 作为服务器运行。
  • 每个 Node 都有自己的 consul agent 运行并加入集群。
  • TCP 端口正确打开

这是服务器: enter image description here

这是来自 Consul UI: enter image description here

在我得到的FE Node 上运行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 Node 上运行 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)
})

使用其 ip:port 和 curl 从 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 Node 上的 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 接口(interface)提供的 8080 或 8081。我错过了什么?

最佳答案

问题是 axios 使用的 dns.lookup 没有使用 dns.setServers 设置的服务器。 dns.lookupdns.resolve 不使用相同的 mechanism对于 resolving .

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

可能更好的解决方案不是在 Node 中执行此操作,而是使用 consul agent 的绑定(bind)选项在本地运行以进行 DNS 解析。

关于node.js - 在 Nodejs 上使用 DNS 进行 Consul 服务发现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47591751/

相关文章:

node.js - 中间件Yeoman功能实现的目的是什么?

C++软件传递参数方法

javascript - 如何使用加密创建随机盐哈希

linux - 我的系统如何知道根名称服务器?

asp.net-mvc - Windows Azure 网站中通配符子域的协议(protocol)绑定(bind)错误

node.js - Azure Node.js Web 应用程序 (ES2015/babel) 的自定义启动命令?

apache - 设置Apache Web服务器

consul - 部署 consul 集群的最佳实践是什么?

consul - 如何在领导人选举中使用 Consul?

docker - 为 dockerized Consul 找出服务的 IP 地址