Grenache Node.js HTTP
Grenache Node.js HTTP implementation, currently at stable version 1.0.1, is a specialized micro-framework designed for building performant microservices in Node.js environments. It stands out by utilizing Distributed Hash Tables (DHT), similar to those found in Bittorrent, to facilitate peer-to-peer connections rather than traditional centralized brokers. This package provides the HTTP transport layer for Grenache, enabling the creation of RPC servers and clients that announce and discover services over an overlay network managed by external 'Grape' DHT nodes. Its release cadence follows the broader Grenache ecosystem. Key differentiators include its decentralized service discovery, direct peer-to-peer communication, and focus on optimized performance, moving beyond conventional request-response patterns by leveraging a robust, distributed network infrastructure. It specifically abstracts away the complexities of DHT interaction for HTTP-based service communication.
Common errors
-
Error: Timeout
cause The RPC client did not receive a response from the service within the specified timeout, often due to the service not being announced or not responding in time.fixEnsure the RPC server is running and has successfully announced its service to the Grape network. Check Grape connectivity from both client and server, and increase the timeout if the service is known to have long processing times. -
Error: connect ECONNREFUSED 127.0.0.1:30001
cause The Grenache Link instance was unable to connect to the specified Grape (DHT) node. This usually means Grape is not running or is inaccessible.fixVerify that the Grape instance is running on the specified IP address and port, and that no firewall is blocking the connection. Double-check the 'grape' option in the Link constructor. -
ReferenceError: PeerRPCServer is not defined
cause Attempted to use `PeerRPCServer` (or `PeerRPCClient`) without correctly requiring/importing it, or using an incorrect import style for CommonJS.fixFor CommonJS, use `const { PeerRPCServer } = require('grenache-nodejs-http')` or `const PeerRPCServer = require('grenache-nodejs-http').PeerRPCServer`. This package does not support ESM imports in its current major version.
Warnings
- gotcha Grenache requires external 'Grape' DHT instances to be running and accessible for service discovery and communication. This is a runtime dependency, not an npm installation.
- gotcha The PeerRPCServer's 'stream' event is emitted immediately upon request arrival, while the 'request' event buffers the entire payload before emission. If 'disableBuffered' is true, the 'request' event must be manually triggered or handled.
- gotcha Service announcements made via 'link.announce()' are ephemeral in the DHT. To ensure continuous service availability, announcements must be re-issued periodically, typically using a setInterval.
- breaking This package explicitly requires Node.js version 16.0 or higher, as indicated by the `engines` field in `package.json`. Running on older versions may lead to unexpected errors or failure to start.
Install
-
npm install grenache-nodejs-http -
yarn add grenache-nodejs-http -
pnpm add grenache-nodejs-http
Imports
- PeerRPCServer
import { PeerRPCServer } from 'grenache-nodejs-http'const { PeerRPCServer } = require('grenache-nodejs-http') - PeerRPCClient
import PeerRPCClient from 'grenache-nodejs-http'
const { PeerRPCClient } = require('grenache-nodejs-http') - GrenacheHttpModule
const GrenacheHttp = require('grenache-nodejs-http')
Quickstart
/*
Before running this code, you need to install grenache-grape globally:
npm i -g grenache-grape
Then, start two Grape instances:
grape --dp 20001 --aph 30001 --bn '127.0.0.1:20002'
grape --dp 20002 --aph 40001 --bn '127.0.0.1:20001'
*/
const Link = require('grenache-nodejs-link')
const { PeerRPCServer, PeerRPCClient } = require('grenache-nodejs-http')
// RPC Server
const serverLink = new Link({
grape: 'http://127.0.0.1:30001'
})
serverLink.start()
const peerServer = new PeerRPCServer(serverLink, {
timeout: 300000
})
peerServer.init()
const service = peerServer.transport('server')
// Use Math.floor(Math.random()) for port generation, replacing _.random
service.listen(Math.floor(Math.random() * 1000) + 1024)
setInterval(() => {
serverLink.announce('rpc_test', service.port, {})
}, 1000)
service.on('request', (rid, key, payload, handler) => {
console.log(`Server received request for '${key}' with payload: '${payload}'`)
handler.reply(null, 'world')
})
console.log(`RPC Server listening on port ${service.port} and announcing 'rpc_test'`)
// RPC Client
const clientLink = new Link({
grape: 'http://127.0.0.1:30001'
})
clientLink.start()
const peerClient = new PeerRPCClient(clientLink, {})
peerClient.init()
peerClient.request('rpc_test', 'hello', { timeout: 10000 }, (err, data) => {
if (err) {
console.error('Client error:', err)
process.exit(-1)
}
console.log(`Client received data: '${data}'`) // world
// In a real application, you might want to stop links gracefully
// clientLink.stop()
// serverLink.stop()
// process.exit(0)
})
console.log('RPC Client sending request to \'rpc_test\' service...')