New:Microsoft Teams Notifications Are Now Available in Socket.Learn more
Get Started

@toa.io/amqplib

Package Overview
Dependencies
Maintainers
1
Versions
2
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@toa.io/amqplib

An AMQP 0-9-1 client for RabbitMQ: the amqplib interface, built for throughput and a small memory footprint

latest
Source
npmnpm
Version
0.1.0
Version published
Maintainers
1
Created
Source

amqplib

An AMQP 0-9-1 client for RabbitMQ, for Node.js: the interface of amqplib, built for throughput and a small memory footprint.

per message, against a brokeramqplibthis
publish a 1 KB message, CPU5.4 µs1.2 µs4× less
serve a 1 KB request, CPU16.2 µs9.7 µs40% less
serve a 1 KB request, p50 latency1.93 ms0.50 ms4× lower
take a 448 KB message in, CPU490 µs274 µs44% less
publish 448 KB messages, peak memory144 MB87 MB40% less
publish 448 KB messages, collections per 10002045× fewer

Against amqplib@2.0.1 on the same machine and the same broker, and reproduced in a second round. What was measured, and how.

This library is a from-scratch implementation of the interface designed by Michael Bridgen and the contributors to amqp-node/amqplib. It is a drop-in replacement and passes the original project's test suite.

npm install @toa.io/amqplib
import amqp from '@toa.io/amqplib'

const connection = await amqp.connect('amqp://localhost')
const channel = await connection.createChannel()

await channel.assertQueue('tasks')

await channel.consume('tasks', message => {
  if (message === null) return // the server cancelled the consumer

  console.log(message.content.toString())
  channel.ack(message)
})

channel.sendToQueue('tasks', Buffer.from('something to do'))

The callback interface is @toa.io/amqplib/callback_api. Both are documented by the original: amqp-node.github.io/amqplib.

In place of amqplib

What is the same is checked rather than claimed. compat/test is the original's test suite at v2.0.1 as it is, and it runs against this library on every change, as do the original's type tests, which compile against the types declared here under the names the original declares them by: Options.Publish, Replies.AssertQueue, ConsumeMessage and the rest. Two things of that suite are not run, and compat/exemptions.mts says why: the tests of the original's stream multiplexer, an internal module that has no counterpart here, and one test that waits for the original to write to a stream it has already ended.

What differs:

  • It is an ES module, and needs Node.js 24. require() loads it as it loads any ES module.
  • Only the two entry points can be imported. The original's lib/ modules were never part of its interface, and there is nothing at those paths.
  • publish also returns false once 4 MB are waiting to be written, whatever the channel's highWaterMark, and drain follows as it does otherwise.

What it does differently

A message owns its bytes. message.content is a buffer of exactly the content's size, and nothing else is kept alive by it. The socket reads into one buffer per connection, for as long as the connection lives, instead of allocating one per read; content is copied out of it once, straight into the message's buffer, however its frames were split across reads. Nothing is concatenated, and no frame becomes an object on the way.

What is sent during a turn of the event loop goes out in one write. Frames are encoded one after another into memory that is written to again once the socket is done with it, and handed to the socket when the turn ends — or, for what is sent while handling what just arrived, before the read that brought it returns. A reply to a message leaves in the same turn the message came in. Content is copied when it is published, as the original copies it: the buffer given to publish is the caller's again as soon as publish returns.

Strings are found, not made. A consumer tag comes with every delivery, and exchanges, routing keys, content types and header names are few. A string seen before is found by its bytes rather than decoded again.

Measured

Against amqplib@2.0.1, on the same machine, one after the other; benchmarks/readme.md says how, and what each column is.

With RabbitMQ 4.2 on the same host, per message, in the process that takes the messages in (deliver), answers and acknowledges them (turn), or sends them (publish):

scenariolibraryCPU µscopied KBsocket writesGC/1kRSS MBpeak MBp50 ms
deliver.100amqplib3.30.20.000.89293
deliver.100@toa.io/amqplib2.20.10.000.38989
deliver.1kamqplib5.81.10.001.29192
deliver.1k@toa.io/amqplib4.81.00.000.39090
deliver.32kamqplib25.633.10.002.6107111
deliver.32k@toa.io/amqplib22.732.00.001.6123142
deliver.64kamqplib57.2127.80.003.7132144
deliver.64k@toa.io/amqplib52.264.00.002.9111136
deliver.96kamqplib74.9160.40.004.5136145
deliver.96k@toa.io/amqplib71.696.00.005.0130151
deliver.448kamqplib490.51534.80.0056.0148151
deliver.448k@toa.io/amqplib273.9448.00.0020.0121147
turn.1kamqplib16.23.22.003.691951.93
turn.1k@toa.io/amqplib9.72.00.410.692920.50
turn.448kamqplib724.91982.86.0072.01461461.07
turn.448k@toa.io/amqplib575.8896.01.0036.01011381.06
publish.100amqplib4.60.41.000.69393
publish.100@toa.io/amqplib0.80.10.020.19090
publish.1kamqplib5.42.21.000.89597
publish.1k@toa.io/amqplib1.21.00.020.18989
publish.64kamqplib62.364.22.003.1101105
publish.64k@toa.io/amqplib39.164.00.671.48788
publish.448kamqplib360.6448.25.0020.0122144
publish.448k@toa.io/amqplib185.4448.01.004.08687

With no broker and no network, which leaves the library alone. From the bytes a socket reads to the consumer's callback:

content, byteslibraryCPU µsGC/1kRSS MB
100amqplib0.810.36122
100@toa.io/amqplib0.290.12107
1024amqplib0.920.44133
1024@toa.io/amqplib0.340.14114
16384amqplib6.891.41183
16384@toa.io/amqplib2.380.47169
65536amqplib24.545.50173
65536@toa.io/amqplib8.441.89166
458752amqplib227.4949.53184
458752@toa.io/amqplib67.1412.44152

And from publish to the bytes handed to the socket, in bursts of 64:

content, byteslibraryCPU µssocket writesRSS MB
100amqplib2.900.022166
100@toa.io/amqplib0.220.016102
1024amqplib3.280.133187
1024@toa.io/amqplib0.260.016100
16384amqplib5.891.991205
16384@toa.io/amqplib1.330.016167
65536amqplib16.001.981249
65536@toa.io/amqplib11.430.016163
458752amqplib102.235.000401
458752@toa.io/amqplib74.380.016204

With every message acknowledged, the original takes 2.28 µs and 832 MB to receive 100-byte messages where this library takes 0.35 µs and 108 MB.

Node.js 24.21 on a Ryzen 7 7800X3D, Linux 7.0.

Large messages and the allocator. A message's content is one allocation of its size, and how glibc serves allocations of hundreds of kilobytes decides what receiving them costs: by default each one is mapped from the system and handed back, page fault by page fault. Telling it to keep the memory, which is a decision for the application rather than for a library, takes deliver.448k from 274 µs to 211 (the original: 490 to 445) and turn.448k from 576 µs to 415 (the original: 725 to 718):

MALLOC_MMAP_THRESHOLD_=33554432 MALLOC_TRIM_THRESHOLD_=67108864 node service.js

Contributing

See CONTRIBUTING.md.

Keywords

AMQP

FAQs

Package last updated on 20 Sep 2026

Related posts