简体   繁体   English

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

[英]Consul service discovery with DNS on Nodejs

TL;DR TL; DR

Hi all, I'm trying to call nodejs backend microservices from a nodejs frontend written in Express, through Consul DNS interface but I am having errors. 大家好,我试图从Express编写的nodejs前端调用nodejs后端微服务,通过Consul DNS接口,但我有错误。

I am using the nodejs dns api to set the dns for the sole node application in order to make subsequent dns.resolve() calls to the local Consul DNS interface. 我使用nodejs dns api为唯一的节点应用程序设置dns,以便对本地Consul DNS接口进行后续的dns.resolve()调用。

Target 目标

I would like to be able to make an http request to my backend service without the need to wire its IPs and ports in my client code. 我希望能够向我的后端服务发出http请求,而无需在我的客户端代码中连接其IP和端口。 Neither I want to write a custom code to query the Consul HTTP API to get the ip:port couple for my service any time I need to call it. 我不想编写自定义代码来查询Consul HTTP API,以便在我需要调用它时获取我的服务的ip:port couple。

Problem 问题

Problem is that when I use axios (similar to request ) to make an HTTP call to the backend service I always get an error because it cannot resolve the address. 问题是,当我使用axios (类似于请求 )对后端服务进行HTTP调用时,我总是会收到错误,因为它无法解析地址。 It seems Axios is not using the dns I previously setted with: 似乎Axios没有使用我之前设置的dns:

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

Update_1 Update_1

Setting the dns of the virtual machine (/etc/resolv.conf) to localhost e using the -recursor command line option for consul with the default dns everything works! 使用-recursor命令行选项将虚拟机的dns(/etc/resolv.conf)设置为localhost,使用默认dns的consul一切正常! I still would like to understand what I am doing wrong setting the dns only on my nodejs app. 我仍然想了解我在我的nodejs应用程序上设置dns的错误。

Setup 设定

  • 1 FE node with a nodejs process running a simple webserver with Expressjs. 1个带有nodejs进程的FE节点运行带有Expressjs的简单Web服务器。 In the app.get('/') route it make a REST POST call to a backend service called be01 through consul and axios (like request). 在app.get('/')路由中,它通过consul和axios(如request)对名为be01的后端服务进行REST POST调用。
  • 2 BE node with a nodejs process running a simple webserver with Expressjs exposing REST api. 2带有nodejs进程的BE节点运行一个简单的Web服务器,Expressjs暴露REST api。
  • 1 Consul node with Consul running as a server. 1 Consul节点,Consul作为服务器运行。
  • Every node has it own consul agent running and joined to the cluster. 每个节点都有自己的consul代理运行并加入集群。
  • TCP ports are correctly open TCP端口正确打开

This are the servers: 这是服务器: 在此输入图像描述

This is from the Consul UI: 这来自Consul UI: 在此输入图像描述

Running consul members on the FE node I get: 在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>

If I run dig @localhost -p 8600 be01.service.consul SRV on the FE node I correctly get this result ( as in the Consul docs ): 如果我在FE节点上运行挖掘@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

This is the BE service code: 这是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)
})

Calling it from the FE using its ip:port with curl works without problems: 使用带有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":""}

The web service on the FE node, simplifying, is sort of this: 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");
  });

Running it I get the following result: 运行它我得到以下结果:

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,
  ...
  ...

Conclusion 结论

As you can see the nodejs client is calling the be service but it fails trying to resolve 'be01.service.consul'. 正如您所看到的,nodejs客户端正在调用be服务,但它无法尝试解析'be01.service.consul'。 Moreover it is using the port 80 instead of 8080 or 8081 as provided by the Consul DNS interface. 此外,它使用端口80而不是Consul DNS接口提供的8080或8081。 What am I missing? 我错过了什么?

The problem is that axios is using dns.lookup which does not use the servers set by dns.setServers . 问题是axios正在使用dns.lookup ,它不使用dns.setServers设置的服务器。 dns.lookup and dns.resolve do not use the same mechanism for resolving . dns.lookupdns.resolve不使用相同的机制进行解析

In pure node the possible options are to either resolve the domain name to an IP using dns.resolve* before calling axios or something like this interceptor example (I haven't tried it). 在纯节点中,可能的选项是在调用axios之前使用dns.resolve*将域名解析为IP或类似此拦截器示例 (我还没有尝试过)。

Probably better solution is not to do this in node but use the bind option of the consul agent running locally to do the dns resolving. 可能更好的解决方案是不在节点中执行此操作,而是使用本地运行的consul代理的bind选项来执行dns解析。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM