Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/node-modules/cluster-client
Sharing Connection among Multi-Process Nodejs
https://github.com/node-modules/cluster-client
Last synced: about 2 months ago
JSON representation
Sharing Connection among Multi-Process Nodejs
- Host: GitHub
- URL: https://github.com/node-modules/cluster-client
- Owner: node-modules
- License: mit
- Created: 2016-12-12T03:03:39.000Z (almost 8 years ago)
- Default Branch: master
- Last Pushed: 2024-01-24T07:58:43.000Z (8 months ago)
- Last Synced: 2024-04-25T15:42:00.710Z (5 months ago)
- Language: JavaScript
- Homepage:
- Size: 208 KB
- Stars: 132
- Watchers: 19
- Forks: 15
- Open Issues: 5
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# cluster-client
Sharing Connection among Multi-Process Nodejs
[![NPM version][npm-image]][npm-url]
[![CI](https://github.com/node-modules/cluster-client/actions/workflows/nodejs.yml/badge.svg)](https://github.com/node-modules/cluster-client/actions/workflows/nodejs.yml)
[![Test coverage][codecov-image]][codecov-url]
[![Known Vulnerabilities][snyk-image]][snyk-url]
[![npm download][download-image]][download-url][npm-image]: https://img.shields.io/npm/v/cluster-client.svg?style=flat-square
[npm-url]: https://npmjs.org/package/cluster-client
[codecov-image]: https://codecov.io/gh/node-modules/cluster-client/branch/master/graph/badge.svg
[codecov-url]: https://codecov.io/gh/node-modules/cluster-client
[snyk-image]: https://snyk.io/test/npm/cluster-client/badge.svg?style=flat-square
[snyk-url]: https://snyk.io/test/npm/cluster-client
[download-image]: https://img.shields.io/npm/dm/cluster-client.svg?style=flat-square
[download-url]: https://npmjs.org/package/cluster-clientAs we know, each Node.js process runs in a single thread. Usually, we split a single process into multiple processes to take advantage of multi-core systems. On the other hand, it brings more system overhead, sush as maintaining more TCP connections between servers.
This module is designed to share connections among multi-process Nodejs.
## Theory
- Inspired by [Leader/Follower pattern](http://www.cs.wustl.edu/~schmidt/PDF/lf.pdf).
- Allow ONLY one process "the Leader" to communicate with server. Other processes "the Followers" act as "Proxy" client, and forward all requests to Leader.
- The Leader is selected by "Port Competition". Every process try to listen on a certain port (for example 7777), but ONLY one can occupy the port, then it becomes the Leader, the others become Followers.
- TCP socket connections are maintained between Leader and Followers. And I design a simple communication protocol to exchange data between them.
- If old Leader dies, one of processes will be selected as the new Leader.## Diagram
normal (without using cluster client)
```js
+--------+ +--------+
| Client | | Client | ...
+--------+ +--------+
| \ / |
| \ / |
| / \ |
| / \ |
+--------+ +--------+
| Server | | Server | ...
+--------+ +--------+```
using cluster-client
```js
+-------+
| start |
+---+---+
|
+--------+---------+
__| port competition |__
win / +------------------+ \ lose
/ \
+--------+ tcp conn +----------+
| Leader |<---------------->| Follower |
+--------+ +----------+
|
+--------+
| Client |
+--------+
| \
| \
| \
| \
+--------+ +--------+
| Server | | Server | ...
+--------+ +--------+```
## Protocol
- Packet structure
```js
0 1 2 4 12
+-------+-------+---------------+---------------------------------------------------------------+
|version|req/res| reserved | request id |
+-------------------------------+-------------------------------+-------------------------------+
| timeout | connection object length | application object length |
+-------------------------------+---------------------------------------------------------------+
| conn object (JSON format) ... | app object |
+-----------------------------------------------------------+ |
| ... |
+-----------------------------------------------------------------------------------------------+
```- Protocol Type
- Register Channel
- Subscribe/Publish
- Invoke
- Sequence diagram```js
+----------+ +---------------+ +---------+
| Follower | | local server | | Leader |
+----------+ +---------------+ +---------+
| register channel | assign to |
+ -----------------------> | --------------------> |
| | |
| subscribe |
+ ------------------------------------------------> |
| subscribe result |
| <------------------------------------------------ +
| |
| invoke |
+ ------------------------------------------------> |
| invoke result |
| <------------------------------------------------ +
| |
```## Install
```bash
npm install cluster-client --save
```Node.js >= 6.0.0 required
## Usage
```js
'use strict';const co = require('co');
const Base = require('sdk-base');
const cluster = require('cluster-client');/**
* Client Example
*/
class YourClient extends Base {
constructor(options) {
super(options);this.options = options;
this.ready(true);
}subscribe(reg, listener) {
// subscribe logic
}publish(reg) {
// publish logic
}* getData(id) {
// invoke api
}getDataCallback(id, cb) {
// ...
}getDataPromise(id) {
// ...
}
}// create some client instances, but only one instance will connect to server
const client_1 = cluster(YourClient)
.delegate('getData')
.delegate('getDataCallback')
.delegate('getDataPromise')
.create({ foo: 'bar' });
const client_2 = cluster(YourClient)
.delegate('getData')
.delegate('getDataCallback')
.delegate('getDataPromise')
.create({ foo: 'bar' });
const client_3 = cluster(YourClient)
.delegate('getData')
.delegate('getDataCallback')
.delegate('getDataPromise')
.create({ foo: 'bar' });// subscribe information
client_1.subscribe('some thing', result => console.log(result));
client_2.subscribe('some thing', result => console.log(result));
client_3.subscribe('some thing', result => console.log(result));// publish data
client_2.publish('some data');// invoke method
client_3.getDataCallback('some thing', (err, val) => console.log(val));
client_2.getDataPromise('some thing').then(val => console.log(val));co(function*() {
const ret = yield client_1.getData('some thing');
console.log(ret);
}).catch(err => console.error(err));
```## API
- `delegate(from, to)`:
create delegate method, `from` is the method name your want to create, and `to` have 6 possible values: [ `subscribe`, `unSubscribe`, `publish`, `invoke`, `invokeOneway`, `close` ], and the default value is invoke
- `override(name, value)`:
override one property
- `create(…)`
create the client instance
- `close(client)`
close the client
- `APIClientBase` a base class to help you create your api client## Best Practice
1. DataClient
- Only provider data API, interact with server and maintain persistent connections etc.
- No need to concern `cluster` issue1. APIClient
- Using `cluster-client` to wrap DataClient
- Put your bussiness logic here### DataClient
```js
const Base = require('sdk-base');class DataClient extends Base {
constructor(options) {
super(options);
this.ready(true);
}subscribe(info, listener) {
// subscribe data from server
}publish(info) {
// publish data to server
}* getData(id) {
// asynchronous API
}
}
```### APIClient
```js
const DataClient = require('./your-data-client');
const { APIClientBase } = require('cluster-client');class APIClient extends APIClientBase {
constructor(options) {
super(options);
this._cache = new Map();
}
get DataClient() {
return DataClient;
}
get delegates() {
return {
getData: 'invoke',
};
}
get clusterOptions() {
return {
name: 'MyClient',
};
}
subscribe(...args) {
return this._client.subscribe(...args);
}
publish(...args) {
return this._client.publish(...args);
}
* getData(id) {
// write your business logic & use data client API
if (this._cache.has(id)) {
return this._cache.get(id);
}
const data = yield this._client.getData(id);
this._cache.set(id, data);
return datal
}
}
``````js
|------------------------------------------------|
| APIClient |
| |----------------------------------------|
| | ClusterClient |
| | |---------------------------------|
| | | DataClient |
|-------|------|---------------------------------|
```For more information, you can refer to the [discussion](https://github.com/eggjs/egg/issues/322)
[MIT](LICENSE)
## Contributors
|[
gxcsoccer](https://github.com/gxcsoccer)
|[
fengmk2](https://github.com/fengmk2)
|[
shaoshuai0102](https://github.com/shaoshuai0102)
|[
killagu](https://github.com/killagu)
|[
semantic-release-bot](https://github.com/semantic-release-bot)
|[
atian25](https://github.com/atian25)
|
| :---: | :---: | :---: | :---: | :---: | :---: |
[
leoner](https://github.com/leoner)
|[
mansonchor](https://github.com/mansonchor)
|[
sinkhaha](https://github.com/sinkhaha)
|[
limitMe](https://github.com/limitMe)This project follows the git-contributor [spec](https://github.com/xudafeng/git-contributor), auto updated at `Tue Jun 20 2023 12:29:14 GMT+0800`.