项目作者: gcba-iris

项目描述 :
A tiny framework for building fast, modular & extensible IoT backends
高级语言: JavaScript
项目地址: git://github.com/gcba-iris/iris.git
创建时间: 2016-08-31T20:53:58Z
项目社区:https://github.com/gcba-iris/iris

开源协议:MIT License

下载



Iris



Build Status


Codacy Status


Coverage Status


A tiny Node.js framework for building fast, modular & extensible IoT backends. Assemble data processing flows in a Gulp-like fashion using third-party plugins, or write your own. Run the app. That’s it.

Iris tries to strike a balance between convention vs configuration. It comes with sane defaults and a plugin system that makes it easy to get started while allowing ample flexibility at the same time.

  1. $ npm install gcba-iris/iris -g

Contents

Flow

Architecture

Iris is built around the flow, which is the path a message follows from the moment it arrives to the moment it gets handled/processed. If a response is generated, the flow will include its way back to the original device.

Data flows are processed in parallel using threads. A message will always be managed by a single thread from start to finish, so if a thread goes down only that thread’s messages will be affected.

  1. --> Dock --> Dispatcher --> Hooks --> Handler
  2. <-- Dock <-- Dispatcher <-- Hooks <-- Handler

Dispatcher

Receives the data object from the dock, calls the registered input hooks and routes the data object to the right handler. Similarly, when there’s a response the dispatcher routes it to the right dock and executes the registered output hooks.

The dispatcher is part of the Iris core and cannot be customized or swapped off—it’s the glue that holds everything together.

Plugins

Dock

Listens to a single port for incoming messages through a specific protocol and parses it into a plain javascript object. Then passes this object along to the dispatcher. If a response comes back, the dock serializes it to match the message format and sends it off to the original device.

Sample message
  1. tag1|subtag1|02,56,58,8|subtag2|sds,sd,wtr,ghd

The flow tag is the first part of the message. It allows Iris to know how to route the message to the right handler. Thus a tag must be unique and belong to a single flow. Subtags are optional.

Handler

Processes the data object received from the dispatcher. A handler can generate a response, which goes back to the dispatcher. The response will be serialized and sent by the respective dock. If there’s no response the data flow ends there.

Hook

A hook is just a callback function that gets executed whenever a piece of data passes by the dispatcher in the course of a data flow. Multiple hooks can be tied to a single flow. Each hook can be set to run when the message object comes in (input hook) or when the response goes out (output hook).

Getting started

1. Create an empty project

  1. $ iris new myApp

Creates a new folder called myApp with the following contents:

  1. myApp/
  2. |___modules/
  3. | |___docks/
  4. | |___handlers/
  5. | |___hooks/
  6. |___irisfile.js
  7. |___package.json

Then runs npm install.

Existing project

  1. $ iris init

Creates an empty Irisfile and runs npm install gcba-iris/iris --save.

2. Add flows

Define data flows in the Irisfile using iris.flow().

Method signature

  1. iris.flow(name, config);
  • name: A string identifier.
  • config: An object containing:
    • tag: A unique string.
    • docks: An array of dock instances.
    • handler: The handler instance.
    • inputHooks (optional): An array of hook instances.
    • outputHooks (optional): An array of hook instances.

Sample irisfile.js

  1. const iris = require('iris');
  2. const dock = require('./docks/http');
  3. const handler = require('./handlers/handler1');
  4. const handler2 = require('./handlers/handler2');
  5. const hook1 = require('./hooks/hook1');
  6. const hook2 = require('./hooks/hook2');
  7. iris.config = {
  8. threads: 4,
  9. logLevel: 'warn'
  10. };
  11. dock.config = {
  12. port: 5000
  13. };
  14. iris.flow('Flow 1', {
  15. tag: 'tag1',
  16. docks: [dock],
  17. handler: handler,
  18. inputHooks: [hook1],
  19. outputHooks: [hook2]
  20. });
  21. iris.flow('Flow 2', {
  22. tag: 'tag2',
  23. docks: [dock],
  24. handler: handler2,
  25. inputHooks: [hook2]
  26. });

The default amount of threads is the number of CPU cores.

3. Run flows

Inside a project directory:

  1. $ iris

Iris screenshot

CLI Commands

  1. Usage: iris [command]
  2. iris
  3. Runs Iris in the current directory.
  4. iris new [project-name]
  5. Creates a new empty Iris project at the directory [project-name].
  6. iris init
  7. Creates an empty Irisfile and installs Iris locally.
  8. iris --version
  9. Displays Iris's version.
  10. iris --help
  11. Displays the list of available commands.

Node Flags

You can set custom values for the following flags via environment variables:

  • --stack_size: use a variable called STACK_SIZE. The default value is 8192.
  • --max_old_space_size: use a variable called MAX_OLD_SPACE_SIZE. The default value is 8192.
  • --max_new_space_size: use a variable called MAX_NEW_SPACE_SIZE. The default value is 2048.

Writing plugins

Plugins are javascript files or npm packages that get required in the Irisfile. All plugins extend some base class.
Take a look at the technical docs for an in-depth explanation of the architecture and API reference.

Docks

You must implement get path(), listen() and stop(). send() is optional.

  • get path(): returns the dock path.
  • listen(): starts listening at the configured port.
  • stop(): stops listening.
  • send(response): sends the response to the device.

Sample HTTP dock

  1. 'use strict';
  2. const Dock = require('../../lib/bases/Dock');
  3. const http = require('http');
  4. const requestIp = require('request-ip');
  5. class HTTPDock extends Dock {
  6. constructor(name, protocol) {
  7. super(name, protocol);
  8. this._server = null;
  9. this._listening = false;
  10. }
  11. get path() {
  12. return __filename;
  13. }
  14. listen() {
  15. this._server = http.createServer(this._handleRequest.bind(this));
  16. if (!this._listening) {
  17. this._server.listen(this.config.port, () => {
  18. this._listening = true;
  19. this.logger.info(`[HTTP Dock] Listening on port ${this.config.port}...`);
  20. });
  21. }
  22. }
  23. stop() {
  24. if (this._listening) {
  25. this._server.close();
  26. this._listening = false;
  27. this.logger.info('[HTTP Dock] Stopped listening');
  28. }
  29. }
  30. _handleRequest(request, response) {
  31. const chunks = [];
  32. const meta = {
  33. ip: requestIp.getClientIp(request)
  34. };
  35. request.socket.setNoDelay();
  36. request.on('data', function (chunk) {
  37. chunks.push(chunk);
  38. });
  39. request.on('end', function () {
  40. const data = Buffer.concat(chunks);
  41. this.process(data, meta, (message) => {
  42. response.statusCode = 200;
  43. if (message) {
  44. response.write(message);
  45. this.logger.verbose('[HTTP Dock] Sent response to client');
  46. }
  47. response.end();
  48. });
  49. }.bind(this));
  50. }
  51. }
  52. module.exports = new HTTPDock('http', 'HTTP');

Handlers

You must implement get path() and handle().

  • get path(): returns the handler path.
  • handle(data): does with the data whatever the handler was meant to do.

Sample handler

  1. 'use strict';
  2. const Handler = require('iris').Handler;
  3. class Handler1 extends Handler {
  4. constructor(name) {
  5. super(name);
  6. }
  7. get path() {
  8. return __filename;
  9. }
  10. handle(data) {
  11. this.logger.info('[Handler1] Handling data...');
  12. return 'A response';
  13. }
  14. }
  15. module.exports = new Handler1('handler1');

Hooks

You must implement get path() and process().

  • get path(): returns the hook path.
  • process(data): does with the data whatever the hook was meant to do.

Sample hook

  1. 'use strict';
  2. const Hook = require('iris').Hook;
  3. class Hook1 extends Hook {
  4. constructor(name) {
  5. super(name);
  6. }
  7. get path() {
  8. return __filename;
  9. }
  10. process(data) {
  11. this.logger.info('[Hook1] Running...');
  12. }
  13. }
  14. module.exports = new Hook1('hook1');

Utilities

Logger

Iris exposes a Winston instance at several points, available for full use and configuration:

  • At each base class: for plugins’s use. Accesible at this.logger.
  • At the Iris instance itself: for Irisfile and other scripts’s use. Accesible at iris.logger.

Requirements

  • Node.js 6.5.0+

Github

Get the code!


  1. MIT License
  2. Copyright (c) 2016+ Buenos Aires City Government
  3. Permission is hereby granted, free of charge, to any person obtaining a copy
  4. of this software and associated documentation files (the "Software"), to deal
  5. in the Software without restriction, including without limitation the rights
  6. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7. copies of the Software, and to permit persons to whom the Software is
  8. furnished to do so, subject to the following conditions:
  9. The above copyright notice and this permission notice shall be included in all
  10. copies or substantial portions of the Software.
  11. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  12. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  13. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  14. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  15. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  16. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  17. SOFTWARE.