** Chapar Khaneh, is a Persian term for the postal service used during the Achaemenid era. The system was created by Cyrus the Great, the founder of the Persian Empire, as the royal method of communication throughout the empire. Each "Chapar Khaneh" was a station mainly located along the Royal Road, a 2500 km ancient highway, connecting most of the major cities of the empire. https://en.wikipedia.org/wiki/Chapar_Khaneh
Micro-service design can solve lots of development problems, yet a lightweight reliable and robust communication system is needed.
Pros | Cons | |
---|---|---|
HTTP | Main web frameworks such as Express use this protocol. Every one is familiar with express. | HTTP 1.1 has lots of overhead in memory and connection usage. |
Publish/Subscribe | Publish/Subscribe protocols are well-suited for high throughput data pipeline as they provide a lightweight communication layer. | Programmers have to change their code layout to use pub/sub. Specially they cannot use Express framework. |
Chapar helps you to use AMQP as underlying communication protocol with absolutely zero changes while coding Express/Node.js.
Suppose you have two micro-services in your production server, MS1 and MS2. MS1 has very high number of requests per seconds that needs to be processed by MS2. MS2 is a server for MS1.
You use Chapar.RPCServer
middleware in MS2 and Chapar.RPCClient
in MS1. In MS1 you call an Express route of MS2. Chapar.RPCClient
creates a message with shape of actual HTTP request and sends it with AMQP with RabbitMQ broker.
This message pushes Chapar.RPCServer
in MS2. MS2 calls the internal controller (only function call, no TCP handshake, no HTTP overhead), gets the result and sends the response back on a response queue. You just need to await for Chapar.RPCClient
to provide you the response.
npm i -s chapar
Express app.js:
import express from 'express';
import { RPCServer } from 'chapar';
const app = express();
app.use('/', indexRouter);
// Other Express middlewares
const config = {
rabbitMQ: { // required
host: 'localhost', // required
port: 5672, // required
username: 'RABBITMQ_UESRNAME', // optional, (needed if rabbitMQ requires authentication)
password: 'RABBITMQ_PASSWORD' // optional, (needed if rabbitMQ requires authentication)
},
exchange: { // required
name: 'MY_APP_EXCHANGE_NAME', // required
type: 'direct', // optional, default='direct'
options: { // optional
durable: true // optional, default='{ durable: true }'
}
},
requestQueue: { // optional
name: 'HTTP_OVER_AMQP', // optional, default='HTTP_OVER_AMQP'
options: { // optional
exclusive: false // optional, default={ exclusive: false }
},
bindKey: 'HTTP_OVER_AMQP_ROUTING_KEY', // optional default='HTTP_OVER_AMQP_ROUTING_KEY',
prefetch: 0 // optional, default=0 which means unlimited
}
};
RPCServer(app, config);
// Common Express error handler
module.exports = app;
And thats it. no need to other changes to be done in your Express server application.
configure an export an instance of Chapar.RPCClient:
import {RPCClient} from 'chapar';
const ConfiguredRPCClient = await RPCClient({
rabbitMQ: { // required
host: 'localhost', // required
port: 5672, // required
username: 'RABBITMQ_USERNAME', // optional, (needed if rabbitMQ requires authentication)
password: 'RABBITMQ_PASSWORD' // optional, (needed if rabbitMQ requires authentication)
},
exchange: { // required
name: 'MY_APP_EXCHANGE_NAME', // required
type: 'direct', // optional, default='direct'
options: { // optional
durable: true // optional, default='{ durable: true }'
}
},
replyQueue: {
name: '', // optional, default=''
options: { // optional
exclusive: true // optional, default={ exclusive: true }
},
prefetch: 0 // optional, default=0 which means unlimited
},
publish: { // optional
routingKey: 'HTTP_OVER_AMQP_ROUTING_KEY', // optional, default='HTTP_OVER_AMQP_ROUTING_KEY',
options: { // optional
persistent: true // optional, default=true
}
},
// optional
queryStringStringifier: {
extended: true, // default=true, if true chapar uses 'qs' library to stringify query object in requests
// o.w. it uses 'querystring' library
options: {} // options passed to chosen query string stringifier library
}
});
Some piece of code that need some processing in remote micro-server.
Access to remote micro-server with just one line of code:
const { status, body, headers } = await ConfiguredRPCClient(
'GET', // method
'/users', // url
{}, // cookies
{ name: 'John' }, // query
{ Authorization: 'Bearer abcd...' }, // headers
{} // body
);
console.log(`status: ${status}, body: ${body}, headers: ${headers}`);
An Example output may be:
status: 200,
body: { name: 'John', lastname: 'Doe', phone: '+1001001001' },
headers: { 'x-last-login': '1583048837564' }
In some cases, both micro-services may act as client and both as server. i.e. each serves some processing to for the other. You can use both Chapar.RPCServer
and Chapar.RPCClient
in each one the micro-services.
From version 1.1.0
Chapar started to support requests which may contain complex query string objects. You can configure chapar.RPCClient
to stringify your query object with qs
or querystring
npm libraries through config.queryStringStringifier.extended
.
This parameter should be set according to your remote express application. If your server express app can support extended query string parser i.e. app.use(express.urlencoded({ extended: true }));
is set in your express server app
, you can take advantage of complex query string objects in your client by setting config.queryStringStringifier.extended: true
. If your server uses app.use(express.urlencoded({ extended: false }));
client should be configured with config.queryStringStringifier.extended: false
.
For more information about how does express deals with query strings by default, refer to express urlencoded api.
NOTE: Chapar.RPCServer
automatically reads main express application and uses the the main application query parser. No need to set any thing in Chapar.RPCServer
.
- Version
1.1.2
fixed a bug in http headers validation. - Version
1.1.1
export chapar in bothdefault
and explicit way so that bothimport chapar from 'chapar'
andimport {RPCServer} from 'chapar'
works. - Version
1.1.0
support for complex query string objects. - From
1.0.0
to1.0.13
first public release, debug and bug fixes, some changes are backward UNcompatible
run server example:
npm run dev-run-example-rpc-express
run client example:
npm run dev-run-example-rpc-client
run tests:
npm run test