amqp-node / amqplib

AMQP 0-9-1 library and client for Node.JS

Home Page:https://amqp-node.github.io/amqplib/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Amqp protocol localhost connect request is unsuccessful even when rabbitmq is up

87sam12 opened this issue · comments

commented

In my findings: (File: node_modules/amqplib/lib/connect.js)

if (protocol === 'amqp:') {
    sock = require('net').connect(sockopts, onConnect);
  }

control never enters the onConnect callback function. This is from source code of amqplib. "net" is nodejs's internal socket connectivity library.

Also the following is my sockopts object:

{
host: 'localhost', 
servername: 'localhost', 
port: 5672
}

Im using node v 16 (But it happens in 18.0.0 as well). I also find the net package is fine in v 16 as well (https://nodejs.org/docs/latest-v16.x/api/net.html#socketconnect)
image
image
image

Any ideas?

Most likely amqplib is unable to connect because of a configuration or network issue. Try setting a socket timeout so that amqplib doesn't hang indefinitely

const amqplib = require('amqplib/callback_api');

const socketOptions = {
  timeout: 10000
}
amqplib.connect('amqp://localhost', socketOptions, (err, connection) => {
  if (err) throw err;
  console.log('Connected');
  connection.close((err) => {
    if (err) throw err;        
  })
})
commented

Now its returning: But I get error:

Error: write ECONNRESET
at afterWriteDispatched (node:internal/stream_base_commons:164:15)
at writeGeneric (node:internal/stream_base_commons:155:3)
at Socket._writeGeneric (node:net:795:11)
at Socket._write (node:net:807:8)
at writeOrBuffer (node:internal/streams/writable:389:12)
at _write (node:internal/streams/writable:330:10)
at Socket.Writable.write (node:internal/streams/writable:334:10)
at Connection.C.sendBytes (/node_modules/amqplib/lib/connection.js:527:15)
at Connection.C.sendProtocolHeader (/node_modules/amqplib/lib/connection.js:118:8)
at Connection.C.open (/node_modules/amqplib/lib/connection.js:287:8)
at Socket.onConnect (/node_modules/amqplib/lib/connect.js:149:7)
at Object.onceWrapper (node:events:509:28)
at Socket.emit (node:events:390:28)
at Socket.emit (node:domain:475:12)
at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1147:10)
at TCPConnectWrap.callbackTrampoline (node:internal/async_hooks:130:17)

Sometimes I do get following error as well:
'Expected ConnectionOpenOk; got <ConnectionClose channel:0>'

Im really not sure of the exact connection string for queues. Lets assume my queue name is q1
then will my conn str be,
amqp://guest:guest@localhost:5672/api/queues/q1
or
amqp://guest:guest@localhost:5672/api/queues/%2F/q1
Could you pls tell me where can I find the crisp doc that shares clear cut syntax for conn strs? As most of the doc that I found were giving unneeded info.

If at all my app has several queues to connect with. Now the best practice is to keep the number of connections to the minimum I suppose. But if thats the case, could you pls let me know how must my code look like?

When my URI scheme is : 'amqp://localhost:5672' , Im able to connect.
Im facing problems when I have to connect to queues like:
amqp://guest:guest@localhost:5672/api/queues/%2F/q1
What are the best practices in case of connections and establishing links with queues?
Lets assume my app uses 2 queues and needs to keep connection with those. Will my approach look like the following ? :

async initClient() {
    this.connection = await amqp.connect("amqp://localhost:5672", { timeout: 10000 });
    this.channelQ1 = await this.connection.createChannel();
    this.channelQ1.assertQueue((queue = "q1"));
    this.channelQ1.bindQueue((queue = "q1"), (source = "amq.direct"));
    this.channelQ2 = await this.connection.createChannel();
    this.channelQ2.assertQueue((queue = "q2"));
    this.channelQ2.bindQueue((queue = "q2"), (source = "amq.direct"));
  }

Lets imagine I have a js class like the following. Whats the best practice?

class CloudAMQPClient {
  constructor(cloudAmqpUrl, queueName, consumerTag) {
    this.cloudAmqpUrl = cloudAmqpUrl;
    this.queueName = queueName;
    this.consumerTag = consumerTag;
  }
  async initClient() {
    this.connection = await amqp.connect(this.cloudAmqpUrl, { timeout: 10000 });
    this.channel = await this.connection.createChannel();
    this.channel.assertQueue((queue = "q1"));
    this.channel.bindQueue((queue = "q1"), (source = "amq.direct"));
  }
}

Now for maintaining a single connection to the rabbitmq server and connecting to multiple queues with that same client or maintaining separate connections to each of those queues? Because I found sample code here on github which was structured in this format. But if the constructor is designed in a manner to accept queueName as well, then it was written in an assumption to use separate clients for multiple queues. But somewhere else I found its not the best approach, as obviously it adds on to the network overhead.
Also could you pls let me know the difference between channel.bindQueue and channel.publish(,routingKey='q1'). If at all a channel is already bounded to a queue, will the publish require any queuename as routingKey, if so why?

Hi @12Sam12,

I think you're confusing the RabbitMQ HTTP RESTful API and the amqp protocol. This library only uses amqp. With amqp you do not connect to queues by specifying the queue name in the connection string, you connect to the broker only then use the channel api.

Often client applications have only one connection per Virtual Host, but you may decide to have one connection for consuming messages, and user a second one for publishing.