{"id":13527103,"url":"https://github.com/sfast/zeronode","last_synced_at":"2025-04-01T09:31:05.964Z","repository":{"id":32955723,"uuid":"96012772","full_name":"sfast/zeronode","owner":"sfast","description":"Zeronode - minimal building block for NodeJS microservices","archived":false,"fork":false,"pushed_at":"2023-01-03T15:17:06.000Z","size":2998,"stargazers_count":126,"open_issues_count":26,"forks_count":11,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-03-04T02:07:18.631Z","etag":null,"topics":["distributed-actors","distributed-systems","loadbalancing","microservice","microservices","microservices-architecture","networking","pattern-matching","request-reply","scalable"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/zeronode","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sfast.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"docs/CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"docs/CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-07-02T09:06:05.000Z","updated_at":"2024-12-02T06:00:23.000Z","dependencies_parsed_at":"2023-01-14T22:49:27.930Z","dependency_job_id":null,"html_url":"https://github.com/sfast/zeronode","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sfast%2Fzeronode","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sfast%2Fzeronode/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sfast%2Fzeronode/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sfast%2Fzeronode/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sfast","download_url":"https://codeload.github.com/sfast/zeronode/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246615956,"owners_count":20806034,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["distributed-actors","distributed-systems","loadbalancing","microservice","microservices","microservices-architecture","networking","pattern-matching","request-reply","scalable"],"created_at":"2024-08-01T06:01:41.167Z","updated_at":"2025-04-01T09:31:05.518Z","avatar_url":"https://github.com/sfast.png","language":"JavaScript","readme":"![Zeronode](https://i.imgur.com/NZVXZPo.png)\n\u003cbr/\u003e\n\n[![JavaScript Style Guide](https://cdn.rawgit.com/standard/standard/master/badge.svg)](https://github.com/standard/standard)\u003cbr/\u003e\u003cbr/\u003e\n[![NPM](https://nodei.co/npm/zeronode.png)](https://nodei.co/npm/zeronode/)\u003cbr/\u003e\u003cbr/\u003e\n\n[\u003cimg src=\"https://img.shields.io/gitter/room/nwjs/nw.js.svg\"\u003e](https://gitter.im/npm-zeronode/Lobby) \n[![Known Vulnerabilities](https://snyk.io/test/github/sfast/zeronode/badge.svg)](https://snyk.io/test/github/sfast/zeronode)\n[![GitHub license](https://img.shields.io/github/license/sfast/zeronode.svg)](https://github.com/sfast/zeronode/blob/master/LICENSE)\n[![GitHub issues](https://img.shields.io/github/issues/sfast/zeronode.svg)](https://github.com/sfast/zeronode/issues)\n\n[![Tweet](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](https://twitter.com/intent/tweet?text=Zeronode%20-%20rock%20solid%20transport%20and%20smarts%20for%20building%20NodeJS%20microservices.%E2%9C%8C%E2%9C%8C%E2%9C%8C\u0026url=https://github.com/sfast/zeronode\u0026hashtags=microservices,scaling,loadbalancing,zeromq,awsomenodejs,nodejs)\n[![GitHub stars](https://img.shields.io/github/stars/sfast/zeronode.svg?style=social\u0026label=Stars)](https://github.com/sfast/zeronode)\n\n\n## Zeronode - minimal building block for NodeJS microservices\n* [Why Zeronode?](#whyZeronode)\n* [Installation](#installation)\n* [Basics](#basics)\n* [Benchmark](#benchmark)\n* [API](#api)\n* [Examples](#examples)\n    * [Basic Examples](#basicExamples)\n    * [Basic Examples](#basicExamples)\n* [Advanced] (#advanced)\n    * [Basic Examples](#basicExamples)\n    * [Basic Examples](#basicExamples)\n* [Contributing](#contributing)\n* [Have a question ?](#askzeronode)\n* [License](#license)\n\n\u003ca name=\"whyZeronode\"\u003e\u003c/a\u003e\n### Why you need ZeroNode ? \nApplication backends are becoming complex these days and there are lots of moving parts talking to each other through network.\nThere is a great difference between sending a few bytes from A to B, and doing messaging in reliable way.\n- How to handle dynamic components ? (i.e., pieces that come and/or go away temporarily, scaling a microservice instances )\n- How to handle messages that we can't deliver immediately ? (i.e waiting for a component to come back online)\n- How to route messages in complex microservice architecture ? (i.e. one to one, one to many, custom grouping)\n- How we handle network errors ? (i.e., reconnecting of various pieces)\n\nWe created Zeronode on top of \u003ca href=\"http://zeromq.org\" target=\"_blank\"\u003ezeromq\u003c/a\u003e as to address \u003ca href=\"http://zguide.zeromq.org/page:all#Why-We-Needed-ZeroMQ\" target=\"_blank\"\u003ethese\u003c/a\u003e\nand some more common problems that developers will face once building solid systems.\n\u003cbr/\u003e\nWith zeronode its just super simple to create complex server-to-server communications (i.e. build network topologies).\n\n\u003ca name=\"installation\"\u003e\u003c/a\u003e\n### Installation \u0026 Important notes \nZeronode depends on \u003ca href=\"http://zeromq.org\" target=\"_blank\"\u003ezeromq\u003c/a\u003e\n\u003cbr/\u003e For Debian, Ubuntu, MacOS you can just run\n```bash\n$ npm install zeronode --save\n```\nand it'll also install [zeromq](http://zeromq.org) for you. \n\u003cbr/\u003eKudos to \u003ca href=\"https://github.com/davidharutyunyan\" target=\"_blank\"\u003eDave\u003c/a\u003e for adding install scripts.\nFor other platforms please open an issue or feel free to contribute.\n\n\u003ca name=\"basics\"\u003e\u003c/a\u003e\n### Basics\nZeronode allows to create complex network topologies (i.e. line, ring, partial or full mesh, star, three, hybrid ...) \nEach participant/actor in your network topology we call __znode__, which can act as a sever, as a client or hybrid.\n\n```javascript\nimport Node from 'zeronode';\n\nlet znode = new Node({\n    id: 'steadfast',\n    options: {}, \n    config: {}\n});\n\n// ** If znode is binded to some interface then other znodes can connect to it\n// ** In this case znode acts as a server, but it's not limiting znode to connect also to other znodes (hybrid)\n(async () =\u003e {\n    await znode.bind('tcp://127.0.0.1:6000');\n})();\n\n// ** znode can connect to multiple znodes\nznode.connect({address: 'tcp://127.0.0.1:6001'})\nznode.connect({address: 'tcp://127.0.0.1:6002'})\n\n// ** If 2 znodes are connected together then we have a channel between them \n// ** and both znodes can talk to each other via various messeging patterns - i.e. request/reply, tick (fire and forgot) etc ...\n\n```\n\nMuch more interesting patterns and features you can discover by reading the [API](#api) document.\nIn case you have a question or suggestion you can talk to authors on [Zeronode Gitter chat](#askzeronode)\n\n\n\n\u003ca name=\"benchmark\"\u003e\u003c/a\u003e\n### Benchmark\nAll Benchmark tests are completed on Intel(R) Core(TM) i5-6200U CPU @ 2.30GHz.\n\n\u003ctable\u003e\u003ctbody\u003e\n\u003ctr\u003e\u003ctd\u003e\u003c/td\u003e\u003ctd\u003eZeronode\u003c/td\u003e\u003ctd\u003eSeneca (tcp)\u003c/td\u003e\u003ctd\u003ePigato\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003e1000 msg, 1kb data\u003c/td\u003e\u003ctd\u003e394ms\u003c/td\u003e\u003ctd\u003e2054ms\u003c/td\u003e\u003ctd\u003e342ms\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003e50000 msg, 1kb data\u003c/td\u003e\u003ctd\u003e11821ms\u003ctd\u003e140934ms\u003c/td\u003e\u003ctd\u003eFAIL(100s timeout)\u003c/td\u003e\u003c/tr\u003e\n\u003c/tbody\u003e\u003c/table\u003e\n\u003cbr/\u003e\n\n\u003ca name=\"api\"\u003e\u003c/a\u003e\n### API \n\n#### Basic methods\n* [\u003ccode\u003e**new Node()**\u003c/code\u003e](#node)\n* [\u003ccode\u003eznode.**bind()**\u003c/code\u003e](#bind)\n* [\u003ccode\u003eznode.**connect()**\u003c/code\u003e](#connect)\n* [\u003ccode\u003eznode.**unbind()**\u003c/code\u003e](#unbind)\n* [\u003ccode\u003eznode.**disconnect()**\u003c/code\u003e](#disconnect)\n* [\u003ccode\u003eznode.**stop()**\u003c/code\u003e](#stop)\n\n#### Simple messaging methods\n* [\u003ccode\u003eznode.**request()\u003c/code\u003e**](#request)\n* [\u003ccode\u003eznode.**tick()\u003c/code\u003e**](#tick)\n\n#### Attaching/Detaching handlers to tick and request \n\n* [\u003ccode\u003eznode.**onRequest()**\u003c/code\u003e](#onRequest)\n* [\u003ccode\u003eznode.**onTick()**\u003c/code\u003e](#onTick)\n* [\u003ccode\u003eznode.**offRequest()**\u003c/code\u003e](#offRequest)\n* [\u003ccode\u003eznode.**offTick()**\u003c/code\u003e](#offTick)\n\n#### Load balancing methods\n\n* [\u003ccode\u003eznode.**requestAny()**\u003c/code\u003e](#requestAny)\n* [\u003ccode\u003eznode.**requestDownAny()**\u003c/code\u003e](#requestDownAny)\n* [\u003ccode\u003eznode.**requestUpAny()**\u003c/code\u003e](#requestUpAny)\n* [\u003ccode\u003eznode.**tickAny()**\u003c/code\u003e](#tickAny)\n* [\u003ccode\u003eznode.**tickDownAny()**\u003c/code\u003e](#tickDownAny)\n* [\u003ccode\u003eznode.**tickUpAny()**\u003c/code\u003e](#tickUpAny)\n* [\u003ccode\u003eznode.**tickAll()**\u003c/code\u003e](#tickAll)\n* [\u003ccode\u003eznode.**tickDownAll()**\u003c/code\u003e](#tickDownAll)\n* [\u003ccode\u003eznode.**tickUpAll()**\u003c/code\u003e](#tickUpAll)\n\n#### Debugging and troubleshooting\n\n* [\u003ccode\u003e**znode.enableMetrics()**\u003c/code\u003e](#enableMetrics)\n* [\u003ccode\u003e**znode.disableMetrics()**\u003c/code\u003e](#disableMetrics)\n\n\u003ca name=\"node\"\u003e\u003c/a\u003e\n#### let znode = new Node({ id: String, bind: Url, options: Object, config: Object })\nNode class wraps many client instances and one server instance.\nNode automatically handles:\n* Client/Server ping/pong\n* Reconnections\n\n```javascript\nimport { Node } from 'zeronode';\n\nlet znode = new Node({\n    id: 'node',\n    bind: 'tcp://127.0.0.1:6000',\n    options: {}\n    config: {}\n});\n```\n\nAll four arguments are optional.\n* `id` is unique string which identifies znode.\n* `options` is information about znode which is shared with other connected znoded. It could be used for advanced use cases of load balancing and messege routing.\n* `config` is an object for configuring znode\n    * `logger` - logger instance, default is Winston.\n    * `REQUEST_TIMEOUT` - duration after which request()-s promise will be rejected, default is 10,000 ms.\n    * `RECONNECTION_TIMEOUT` (for client znodes) - zeronode's default is -1 , which means zeronode is always trying to reconnect to failed znode server. Once `RECONNECTION_TIMEOUT` is passed and recconenction doesn't happen zeronode will fire `SERVER_RECONNECT_FAILURE`. \n    * `CONNECTION_TIMEOUT` (for client znodes) - duration for trying to connect to server after which connect()-s promise will be rejected.\n\nThere are some events that triggered on znode instances:\n* `NodeEvents.`**`CLIENT_FAILURE`** - triggered on server znode when client connected to it fails.\n* `NodeEvents.`**`CLIENT_CONNECTED`** - triggered on server znode when new client connects to it.\n* `NodeEvents.`**`CLIENT_STOP`** - triggered on server znode when client successfully disconnects from it.\n\n* `NodeEvents.`**`SERVER_FAILURE`** - triggered on client znode when server znode fails.\n* `NodeEvents.`**`SERVER_STOP`** - triggered on client znode when server successfully stops.\n* `NodeEvents.`**`SERVER_RECONNECT`** - triggered on client znode when server comes back and client znode successfuly reconnects.\n* `NodeEvents.`**`SERVER_RECONNECT_FAILURE`** - triggered on client znode when server doesn't come back in `reconnectionTimeout` time provided during connect(). If `reconnectionTimeout` is not provided it uses `config.RECONNECTION_TIMEOUT` which defaults to -1 (means client znode will try to reconnect to server znode for ages).\n* `NodeEvents.`**`CONNECT_TO_SERVER`** - triggered on client znode when it successfully connects to new server.\n* `NodeEvents.`**`METRICS`** - triggered when [metrics enabled](#enableMetrics).\n\n\n\u003ca name=\"bind\"\u003e\u003c/a\u003e\n#### znode.bind(address: Url)\nBinds the znode to the specified interface and port and returns promise. \nYou can bind only to one address.\nAddress can be of the following protocols: `tcp`, `inproc`(in-process/inter-thread), `ipc`(inter-process).\n\n\u003ca name=\"connect\"\u003e\u003c/a\u003e\n#### znode.connect({ address: Url, timeout: Number, reconnectionTimeout: Number })\nConnects the znode to server znode with specified address and returns promise. \nznode can connect to multiple znodes.\nIf timeout is provided (in milliseconds) then the _connect()-s_ promise will be rejected if connection is taking longer.\u003cbr/\u003e\nIf timeout is not provided it will wait for ages till it connects.\nIf server znode fails then client znode will try to reconnect in given `reconnectionTimeout` (defaults to `RECONNECTION_TIMEOUT`) after which the `SERVER_RECONNECT_FAILURE` event will be triggered.\n\n\u003ca name=\"unbind\"\u003e\u003c/a\u003e\n#### znode.unbind()\nUnbinds the server znode and returns promise.\nUnbinding doesn't stop znode, it can still be connected to other nodes if there are any, it just stops the server behaviour of znode, and on all the client znodes (connected to this server znode) `SERVER_STOP` event will be triggered.\n\n\u003ca name=\"disconnect\"\u003e\u003c/a\u003e\n#### znode.disconnect(address: Url)\nDisconnects znode from specified address and returns promise.\n\n\u003ca name=\"stop\"\u003e\u003c/a\u003e\n#### znode.stop()\nUnbinds znode, disconnects from all connected addresses (znodes) and returns promise.\n\n\u003ca name=\"request\"\u003e\u003c/a\u003e\n#### znode.request({ to: Id, event: String, data: Object, timeout: Number })\nMakes request to znode with id(__to__) and returns promise. \u003cbr/\u003e\nPromise resolves with data that the requested znode replies. \u003cbr/\u003e\nIf timeout is not provided it'll be `config.REQUEST_TIMEOUT` (defaults to 10000 ms). \u003cbr/\u003e\nIf there is no znode with given id, than promise will be rejected with error code `ErrorCodes.NODE_NOT_FOUND`.\n\n\u003ca name=\"tick\"\u003e\u003c/a\u003e\n#### znode.tick({ to: Id, event: String, data: Object })\nTicks(emits) event to given znode(__to__).\u003c/br\u003e\nIf there is no znode with given id, than throws error with code `ErrorCodes.NODE_NOT_FOUND`.\n\n\u003ca name=\"onRequest\"\u003e\u003c/a\u003e\n#### znode.onRequest(requestEvent: String/Regex, handler: Function)\nAdds request handler for given event on znode.\n```javascript\n/**\n* @param head: { id: String, event: String }\n* @param body: {} - requestedData\n* @param reply(replyData: Object): Function\n* @param next(error): Function \n*/\n// ** listening for 'foo' event\nznode.onRequest('foo', ({ head, body, reply, next }) =\u003e {\n  // ** request handling logic \n  // ** move forward to next handler or stop the handlers chain with 'next(err)'\n  next() \n})\n\n// ** listening for any events matching Regexp\nznode.onRequest(/^fo/, ({ head, body, reply, next }) =\u003e {\n  // ** request handling logic \n  // ** send back reply to the requester znode\n  reply(/* Object data */) \n})\n```\n\n\u003ca name=\"onTick\"\u003e\u003c/a\u003e\n#### znode.onTick(event: String/Regex, handler: Function)\nAdds tick(event) handler for given event.\n```javascript\nznode.onTick('foo', (data) =\u003e {\n   // ** tick handling logic \n})\n```\n\n\u003ca name=\"offRequest\"\u003e\u003c/a\u003e\n#### znode.offRequest(requestEvent: String/Regex, handler: Function)\nRemoves request handler for given event.\u003cbr/\u003e\nIf handler is not provided then removes all of the listeners.\n\n\u003ca name=\"offTick\"\u003e\u003c/a\u003e\n#### znode.offTick(event: String/Regex, handler: Function)\nRemoves given tick(event) handler from event listeners' list. \u003cbr/\u003e\nIf handler is not provided then removes all of the listeners.\n\n\u003ca name=\"requestAny\"\u003e\u003c/a\u003e\n#### znode.requestAny({ event: String, data: Object, timeout: Number, filter: Object/Function, down: Bool, up: Bool })\nGeneral method to send request to __only one__ znode satisfying the filter.\u003cbr/\u003e\nFilter can be an object or a predicate function. Each filter key can be object itself, with this keys.\n- **$eq** - strict equal to provided value.\n- **$ne** - not equal to provided value.\n- **$aeq** - loose equal to provided value.\n- **$gt** - greater than provided value.\n- **$gte** - greater than or equal to provided value.\n- **$lt** - less than provided value.\n- **$lte** - less than or equal to provided value.\n- **$between** - between provided values (value must be tuple. eg [10, 20]).\n- **$regex** - match to provided regex.\n- **$in** - matching any of the provided values.\n- **$nin** - not matching any of the provided values.\n- **$contains** - contains provided value.\n- **$containsAny** - contains any of the provided values.\n- **$containsNone** - contains none of the provided values.\n\n```javascript\n    // ** send request to one of znodes that have version 1.*.*\n    znode.requestAny({\n        event: 'foo',\n        data: { foo: 'bar' },\n        filter: { version: /^1.(\\d+\\.)?(\\d+)$/ }\n    })\n    \n    // ** send request to one of znodes whose version is greater than 1.0.0\n    znode.requestAny({\n        event: 'foo',\n        data: { foo: 'bar' },\n        filter: { version: { $gt: '1.0.0' } }\n    })\n    \n    // ** send request to one of znodes whose version is between 1.0.0 and 2.0.0\n    znode.requestAny({\n        event: 'foo',\n        data: { foo: 'bar' },\n        filter: { version: { $between: ['1.0.0', '2.0.0.'] } }\n    })\n\n    // ** send request to one of znodes that have even length of name.\n    znode.requestAny({\n        event: 'foo',\n        data: { foo: 'bar' },\n        filter: (options) =\u003e !(options.name.length % 2)\n    })\n\n    // ** send request to one of znodes that connected to your znode (downstream client znodes)\n    znode.requestAny({\n        event: 'foo',\n        data: { foo: 'bar' },\n        up: false\n    })\n\n    // ** send request to one of znodes that your znode is connected to (upstream znodes).\n    znode.requestAny({\n        event: 'foo',\n        data: { foo: 'bar' },\n        down: false\n    })\n```\n\n\u003ca name=\"requestDownAny\"\u003e\u003c/a\u003e\n#### znode.requestDownAny({ event: String, data: Object, timeout: Number, filter: Object/Function })\nSend request to one of downstream znodes (znodes which has been connected to your znode via _connect()_ ).\n\n\n\u003ca name=\"requestUpAny\"\u003e\u003c/a\u003e\n#### znode.requestUpAny({ event: String, data: Object, timeout: Number, filter: Object/Function })\nSend request to one of upstream znodes (znodes to which your znode has been connected via _connect()_ ).\n\n\u003ca name=\"tickAny\"\u003e\u003c/a\u003e\n#### znode.tickAny({ event: String, data: Object, filter: Object/Function, down: Bool, up: Bool })\nGeneral method to send tick-s to __only one__ znode satisfying the filter.\u003cbr/\u003e\nFilter can be an object or a predicate function.\nUsage is same as [`node.requestAny`](#requestAny)\n\n\u003ca name=\"tickDownAny\"\u003e\u003c/a\u003e\n#### znode.tickDownAny({ event: String, data: Object, filter: Object/Function })\nSend tick-s to one of downstream znodes (znodes which has been connected to your znode via _connect()_ ).\n\n\u003ca name=\"tickUpAny\"\u003e\u003c/a\u003e\n#### znode.tickUpAny({ event: String, data: Object, filter: Object/Function })\nSend tick-s to one of upstream znodes (znodes to which your znode has been connected via _connect()_ ).\n\n\u003ca name=\"tickAll\"\u003e\u003c/a\u003e\n#### znode.tickAll({ event: String, data: Object, filter: Object/Function, down: Bool, up: Bool })\nTick to **ALL** znodes satisfying the filter (object or predicate function), up ( _upstream_ ) and down ( _downstream_ ).\n\n\u003ca name=\"tickDownAll\"\u003e\u003c/a\u003e\n#### znode.tickDownAll({ event: String, data: Object, filter: Object/Function })\nTick to **ALL** downstream znodes.\n\n\u003ca name=\"tickUpAll\"\u003e\u003c/a\u003e\n#### znode.tickUpAll({ event: String, data: Object, filter: Object/Function })\nTick to **ALL** upstream znodes.\n\n\u003ca name=\"enableMetrics\"\u003e\u003c/a\u003e\n#### znode.enableMetrics(interval)\nEnables metrics, events will be triggered by the given interval. Default interval is 1000 ms. \u003cbr/\u003e\n\n\u003ca name=\"disableMetrics\"\u003e\u003c/a\u003e\n#### znode.disableMetrics()\nStops triggering events, and removes all collected data.\n\n\u003ca name=\"examples\"\u003e\u003c/a\u003e\n### Examples\n\u003ca name=\"basicExamples\"\u003e\u003c/a\u003e\n#### Simple client server example\nNodeServer is listening for events, NodeClient connects to NodeServer and sends events: \u003cbr/\u003e\n(myServiceClient) ----\u003e (myServiceServer)\n\nLets create server first\n\nmyServiceServer.js\n```javascript\nimport Node from 'zeronode';\n\n(async function() {\n    let myServiceServer = new Node({ id: 'myServiceServer',  bind: 'tcp://127.0.0.1:6000', options: { layer: 'LayerA' } });\n\n    // ** attach event listener to myServiceServer\n    myServiceServer.onTick('welcome', (data) =\u003e {\n        console.log('onTick - welcome', data);\n    });\n\n    // ** attach request listener to myServiceServer\n    myServiceServer.onRequest('welcome', ({ head, body, reply, next }) =\u003e {\n        console.log('onRequest - welcome', body);\n        reply(\"Hello client\");\n        next();\n    });\n\n    // second handler for same channel\n    myServiceServer.onRequest('welcome', ({ head, body, reply, next }) =\u003e {\n        console.log('onRequest second - welcome', body);\n    });\n\n    // ** bind znode to given address provided during construction\n    await myServiceServer.bind();\n}());\n\n```\nNow lets create a client\n\nmyServiceClient.js\n```javascript\nimport Node from 'zeronode'\n\n(async function() {\n    let myServiceClient = new Node({ options: { layer: 'LayerA' } });\n\n    //** connect one node to another node with address\n    await myServiceClient.connect({ address: 'tcp://127.0.0.1:6000' });\n\n    let serverNodeId = 'myServiceServer';\n\n    // ** tick() is like firing an event to another node\n    myServiceClient.tick({ to: serverNodeId, event: 'welcome', data:'Hi server!!!' });\n\n    // ** you request to another node and getting a promise\n    // ** which will be resolve after reply.\n    let responseFromServer = await myServiceClient.request({ to: serverNodeId, event: 'welcome', data: 'Hi server, I am client !!!' });\n\n    console.log(`response from server is \"${responseFromServer}\"`);\n    // ** response from server is \"Hello client.\"\n}());\n\n```\n\n\u003ca name=\"advancedExamples\"\u003e\u003c/a\u003e\n#### Example of filtering the znodes via options.\n\nLet's say we want to group our znodes logicaly in some layers and send messages considering that layering.\n- __znode__-s can be grouped in layers (and other options) and then send messages to only filtered nodes by layers or other options.\n- the filtering is done on senders side which keeps all the information about the nodes (both connected to sender node and the ones that\nsender node is connected to)\n\nIn this example, we will create one server znode that will bind in some address, and three client znodes will connect to our server znode.\n2 of client znodes will be in layer `A`, 1 in `B`.\n\nserverNode.js\n```javascript\nimport Node from 'zeronode'\n\n(async function() {\n    let server = new Node({ bind: 'tcp://127.0.0.1:6000' });\n    await server.bind();\n}());\n```\n\nclientA1.js\n```javascript\nimport Node from 'zeronode'\n\n(async function() {\n    let clientA1 = new Node({ options: { layer: 'A' } });\n   \n    clientA1.onTick('foobar', (msg) =\u003e {\n        console.log(`go message in clientA1 ${msg}`);\n    });\n    \n    // ** connect to server address and set connection timeout to 20 seconds\n    await clientA1.connect({ address: 'tcp:://127.0.0.1:6000', 20000 });\n}());\n```\n\nclientA2.js\n```javascript\nimport Node from 'zeronode'\n\n(async function() {\n    let clientA2 = new Node({ options: { layer: 'A' } });\n   \n    clientA2.onTick('foobar', (msg) =\u003e {\n        console.log(`go message in clientA2 ${msg}`);\n    });\n    // ** connect to server address and set connection timeout infinite\n    await clientA2.connect({ address: 'tcp:://127.0.0.1:6000') };\n}());\n```\n\nclientB1.js\n```javascript\nimport Node from 'zeronode'\n\n(async function() {\n    let clientB1 = new Node({ options: { layer: 'B' } });\n   \n    clientB1.onTick('foobar', (msg) =\u003e {\n        console.log(`go message in clientB1 ${msg}`);\n    });\n    \n    // ** connect to server address and set connection timeout infinite\n    await clientB1.connect({ address: 'tcp:://127.0.0.1:6000' });\n}());\n```\n\nNow that all connections are set, we can send events.\n```javascript\n// ** this will tick only one node of the layer A nodes;\nserver.tickAny({ event: 'foobar', data: { foo: 'bar' }, filter: { layer: 'A' } });\n\n// ** this will tick to all layer A nodes;\nserver.tickAll({ event: 'foobar', data: { foo: 'bar' }, filter: { layer: 'A' } });\n\n// ** this will tick to all nodes that server connected to, or connected to server.\nserver.tickAll({ event: 'foobar', data: { foo: 'bar' } });\n\n\n// ** you even can use regexp to filer znodes to which the tick will be sent\n// ** also you can pass a predicate function as a filter which will get znode-s options as an argument\nserver.tickAll({ event: 'foobar', data: { foo: 'bar' }, filter: {layer: /[A-Z]/} })\n```\n\n\u003ca name=\"askzeronode\"\u003e\u003c/a\u003e\n### Still have a question ?\nWe'll be happy to answer your questions. Try to reach out us on zeronode gitter chat [\u003cimg src=\"https://img.shields.io/gitter/room/nwjs/nw.js.svg\"\u003e](https://gitter.im/npm-zeronode/Lobby) \u003cbr/\u003e\n\n\u003ca name=\"contributing\"\u003e\u003c/a\u003e\n### Contributing\nContributions are always welcome! \u003cbr/\u003e\nPlease read the [contribution guidelines](https://github.com/sfast/zeronode/blob/master/docs/CONTRIBUTING.md) first.\n\n### Contributors\n* [Artak Vardanyan](https://github.com/artakvg)\n* [David Harutyunyan](https://github.com/davidharutyunyan)\n\n### More about zeronode internals\nUnder the hood we are using \u003ca href=\"http://zeromq.org\" target=\"_blank\"\u003ezeromq\u003c/a\u003e-s Dealer and Router sockets.\n\n\u003ca name=\"license\"\u003e\u003c/a\u003e\n### License\n[MIT](https://github.com/sfast/zeronode/blob/master/LICENSE)\n","funding_links":[],"categories":["Repository","Packages","包","JavaScript","Web frameworks","目录","scalable"],"sub_categories":["Web Frameworks","Web frameworks","Web 框架","Web框架"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsfast%2Fzeronode","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsfast%2Fzeronode","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsfast%2Fzeronode/lists"}