Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

moleculer

Package Overview
Dependencies
Maintainers
1
Versions
117
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

moleculer

Fast & powerful microservices framework for Node.JS

  • 0.6.0
  • Source
  • npm
  • Socket score

Version published
Weekly downloads
47K
increased by19.01%
Maintainers
1
Weekly downloads
 
Created
Source

Moleculer logo

Build Status Coverage Status Codacy Badge Code Climate David Known Vulnerabilities

Moleculer NPM version

Moleculer is a fast & powerful microservices framework for NodeJS (>= v6.x).

Please do not use this in production since it's still under heavy development!

What's included

  • Promise-based functions
  • request-reply concept
  • event bus system
  • supports middlewares
  • multiple services on a node/server
  • built-in caching solution (memory, Redis)
  • multiple transporters (NATS, MQTT, Redis)
  • load balanced requests (round-robin, random)
  • every nodes are equal, no master/leader node
  • auto discovery services
  • parameter validation
  • request timeout handling with fallback response
  • health monitoring & statistics
  • supports versioned services (run different versions of the service)

Table of content

Installation

$ npm install moleculer --save

or

$ yarn add moleculer

Quick start

Simple service & call actions locally

const { ServiceBroker } = require("moleculer");

// Create broker
let broker = new ServiceBroker({ 
    logger: console 
});

// Create a service
broker.createService({
    name: "math",
    actions: {
        // You can call it as broker.call("math.add")
        add(ctx) {
            return Number(ctx.params.a) + Number(ctx.params.b);
        },

        // You can call it as broker.call("math.sub")
        sub(ctx) {
            return Number(ctx.params.a) - Number(ctx.params.b);
        }
    }
});

// Start broker
broker.start();

// Call actions of service
broker.call("math.add", { a: 5, b: 3 })
    .then(res => console.log("5 + 3 =", res));

// Call actions with error handling
broker.call("math.sub", { a: 9, b: 2 })
    .then(res => console.log("9 - 2 =", res))
    .catch(err => console.error(`Error occured! ${err.message}`));

// Chain calls
broker.call("math.add", { a: 3, b: 5})
    .then(res => broker.call("math.sub", { a: res, b: 2 }))
    .then(res => console.log("3 + 5 - 2 =", res));

Try it on Runkit

Main modules

ServiceBroker

The ServiceBroker is the main component of Moleculer. It handles services & events, calls actions and communicates with remote nodes. You need to create an instance of ServiceBroker on every node.

Create broker

Create broker with default settings

let { ServiceBroker } = require("moleculer");
let broker = new ServiceBroker();

Create broker with custom settings

let { ServiceBroker } = require("moleculer");
let broker = new ServiceBroker({
    logger: console,
    logLevel: "info"
});

Create with transporter

let { ServiceBroker, NatsTransporter } = require("moleculer");
let broker = new ServiceBroker({
    nodeID: "node-1",
    transporter: new NatsTransporter(),
    logger: console,
    logLevel: "debug",
    requestTimeout: 5 * 1000,
    requestRetry: 3
});

Create with cacher

let ServiceBroker = require("moleculer").ServiceBroker;
let MemoryCacher = require("moleculer").Cachers.Memory;
let broker = new ServiceBroker({
    cacher: new MemoryCacher(),
    logger: console,
    logLevel: {
        "*": "warn", // global log level for every modules
        "CACHER": "debug" // custom log level for cacher modules
    }
});    

Constructor options

All available options:

{
    nodeID: null,

    logger: null,
    logLevel: "info",

    transporter: null,
    requestTimeout: 5 * 1000,
    requestRetry: 0,
    heartbeatInterval: 10,
    heartbeatTimeout: 30,

    cacher: null,

    metrics: false,
    metricsSendInterval: 5 * 1000,
    statistics: false,
    validation: true,
    internalActions: true
    
    ServiceFactory: null,
    ContextFactory: null
}
NameTypeDefaultDescription
nodeIDStringComputer nameThis is the ID of node. It identifies a node in the cluster when there are many nodes.
loggerObjectnullLogger class. During development you can set to console. In production you can set an external logger e.g. winston or pino
logLevelString or ObjectinfoLevel of logging (debug, info, warn, error)
transporterObjectnullInstance of transporter. Required if you have 2 or more nodes. Internal transporters: NatsTransporter
requestTimeoutNumber5000Timeout of request in milliseconds. If the request is timed out, broker will throw a RequestTimeout error. Disable: 0
requestRetryNumber0Count of retry of request. If the request is timed out, broker will try to call again.
cacherObjectnullInstance of cacher. Built-in cachers: MemoryCacher or RedisCacher
metricsBooleanfalseEnable metrics function.
metricsSendIntervalNumber5000Metrics event sends period in milliseconds
statisticsBooleanfalseEnable broker statistics. Measure the requests count & latencies
validationBooleanfalseEnable action parameters validation.
internalActionsBooleantrueRegister internal actions for metrics & statistics functions
heartbeatIntervalNumber10Interval (seconds) of sending heartbeat
heartbeatTimeoutNumber30Timeout (seconds) of heartbeat
ServiceFactoryClassnullCustom Service class. Broker will use it when creating a service
ContextFactoryClassnullCustom Context class. Broker will use it when creating a context at call

Call actions

You can call an action by calling the broker.call method. Broker will search the service (and the node) that has the given action and it will call it. The function returns with a Promise.

Syntax

let promise = broker.call(actionName, params, opts);

The actionName is a dot-separated string. The first part of it is service name. The seconds part of it is action name. So if you have a posts service which contains a create action, you need to use posts.create string as first parameter.

The params is an object that will be passed to the action as part of the Context.

The opts is an object. With this, you can set/override some request parameters, e.g.: timeout, retryCount.

Available options:

NameTypeDefaultDescription
timeoutNumberrequestTimeout of brokerTimeout of request in milliseconds. If the request is timed out and you don't define fallbackResponse, broker will throw a RequestTimeout error. Disable: 0
retryCountNumberrequestRetry of brokerCount of retry of request. If the request timed out, broker will try to call again.
fallbackResponseAnynullReturn with it, if the request is timed out. More info

Usage

// Call without params
broker.call("user.list").then(res => console.log("User list: ", res));

// Call with params
broker.call("user.get", { id: 3 }).then(res => console.log("User: ", res));

// Call with options
broker.call("user.recommendation", { limit: 5 }, { timeout: 500, fallbackResponse: defaultRecommendation })
    .then(res => console.log("Result: ", res));

// Call with error handling
broker.call("posts.update", { id: 2, title: "Modified post title" })
    .then(res => console.log("Post updated!"))
    .catch(err => console.error("Unable to update Post!", err));    

Request timeout & fallback response

If you call action with timeout and the request is timed out, broker throws a RequestTimeoutError error. But if you set fallbackResponse in calling options, broker won't throw error, instead returns with this given value. It can be an Object, Array...etc. This can be also a Function, which returns a Promise. In this case the broker will pass the current Context to this function as an argument.

Emit events

Broker has an internal event bus. You can send events locally & globally. The local event will be received only by local services of broker. The global event that will be received by all services on all nodes.

Send event

You can send event with emit and emitLocal functions. First parameter is the name of event. Second parameter is the payload.

// Emit a local event that will be received only by local services
broker.emitLocal("service.started", { service: service, version: 1 });

// Emit a global event that will be received by all nodes. 
// The `user` will be serialized to transportation.
broker.emit("user.created", user);

Subscribe to events

To subscribe for events use the on or once methods. Or in Service use the events property. In event names you can use wildcards too.

// Subscribe to `user.created` event
broker.on("user.created", user => console.log("User created:", user));

// Subscribe to `user` events
broker.on("user.*", user => console.log("User event:", user));

// Subscribe to all events
broker.on("**", payload => console.log("Event:", payload));    

To unsubscribe call the off method.

Middlewares

Broker supports middlewares. You can add your custom middleware, and it'll be called on every local request. The middleware is a Function that returns a wrapped action handler.

Example middleware from validators modules:

return function validatorMiddleware(handler, action) {
    // Wrap a param validator
    if (_.isObject(action.params)) {
        return ctx => {
            this.validate(action.params, ctx.params);
            return handler(ctx);
        };
    }
    return handler;

}.bind(this);

The handler is the request handler of action, what is defined in Service schema. The action is the action object from Service schema. The middleware should return with the handler or a new wrapped handler. In this example above, we check whether the action has a params props. If yes we return a wrapped handler that calls the validator before calling the original handler. If there is no params property we return the original handler (skip wrapping).

If you don't call the original handler it will break the request. You can use it in cachers. If you find the data in cache, don't call the handler, instead return the cached data.

Example code from cacher middleware:

return (handler, action) => {
    return function cacherMiddleware(ctx) {
        const cacheKey = this.getCacheKey(action.name, ctx.params, action.cache.keys);
        const content = this.get(cacheKey);
        if (content != null) {
            // Found in the cache! Don't call handler, return with the context
            ctx.cachedResult = true;
            return Promise.resolve(content);
        }

        // Call the handler
        return handler(ctx).then(result => {
            // Afterwards save the response to the cache
            this.set(cacheKey, result);

            return result;
        });
    }.bind(this);
};

Internal actions

The broker registers some internal actions to check the health of node or get request statistics.

List of local services

This action lists local services.

broker.call("$node.services").then(res => console.log(res));

List of local actions

This action lists local actions

broker.call("$node.actions").then(res => console.log(res));

List of nodes

This actions lists all connected nodes.

broker.call("$node.list").then(res => console.log(res));

Health of node

This action returns the health info of process & OS.

broker.call("$node.health").then(res => console.log(res));

Statistics

This action returns the request statistics if the statistics is enabled in options.

broker.call("$node.stats").then(res => console.log(res));

Service

The Service is the other main module in the Moleculer. With the help of this you can define actions.

Schema

You need to create a schema to define a service. The schema has some main parts (name, version, settings, actions, methods, events).

Simple service schema

{
	name: "math",
	actions: {
		add(ctx) {
			return Number(ctx.params.a) + Number(ctx.params.b);
		},

		sub(ctx) {
			return Number(ctx.params.a) - Number(ctx.params.b);
		}
	}
}

Base properties

The Service has some base properties in the schema.

{
    name: "posts",
    version: 1
}

The name is a mandatory property so it must be defined. It's the first part of actionName when you call it with broker.call.

The version is an optional property. If you are running multiple version of the same service this needs to be set. It will be a prefix in the actionName.

{
    name: "posts",
    version: 2,
    actions: {
        find() {...}
    }
}

You need to call the find action as

broker.call("v2.posts.find");

Settings

You can add custom settings to your service under settings property in schema. You can reach it in the service via this.settings.

{
    name: "mailer",
    settings: {
        transport: "mailgun"
    },

    action: {
        send(ctx) {
            if (this.settings.transport == "mailgun") {
                ...
            }
        }
    }
}

Actions

The actions are the callable/public methods of the service. They can be called with broker.call method. The action could be a function (handler) or an object with some properties and with handler. The actions should be placed under actions key in the service schema.

{
	name: "math",
	actions: {
        // Simple definition, only the handler function
		add(ctx) {
			return Number(ctx.params.a) + Number(ctx.params.b);
		},

        // Complex definition, set other properties. In this case
        // the `handler` function is required!
		mult: {
            cache: false,
			params: {
				a: "required|numeric",
				b: "required|numeric"
			},
			handler(ctx) {
                // You can reach action params with `ctx.action.*`
                if (ctx.action.cache)
				    return Number(ctx.params.a) * Number(ctx.params.b);
			}
		}
	}
}

You can call these actions as

broker.call("math.add", { a: 5, b: 7 }).then(res => console.log(res));

broker.call("math.mult", { a: 10, b: 31 }).then(res => console.log(res));

Inside the action you can sub-call other actions in other services with ctx.call method. It is an alias to broker.call, just set itself as parent context.

{
    name: "posts",
    actions: {
        get(ctx) => {
            // Find a post by ID
            let post = posts[ctx.params.id];

            // Populate the post.author field through "users" service
            // Call the "users.get" action with author ID
            return ctx.call("users.get", { id: post.author }).then(user => {
                if (user) {
                    // Replace the author ID with the received user object
                    post.author = user;
                }

                return post;
            })
        }
    }
}

Events

You can subscribe to events and can define event handlers in the schema under events key.

{
    name: "users",
    actions: {
        ...
    },

    events: {
        // Subscribe to "user.create" event
        // Same as you subscribe with `broker.on("user.create", ...)` in the `created()` method
        "user.create": function(payload) {
            this.logger.info("Create user...");
            // Do something
        },

        // Subscribe to all "user.*" event
        "user.*": function(payload, eventName) {
            // Do something with payload. The `eventName` contains the original event name. E.g. `user.modified`
        }
    }

}

Methods

You can also create private functions in the Service. They are called as methods. These functions are private, can't be called with broker.call. But you can call it inside service actions.

{
    name: "mailer",
    actions: {
        send(ctx) {
            // Call the `sendMail` method
            return this.sendMail(ctx.params.recipients, ctx.params.subject, ctx.params.body);
        }
    },

    methods: {
        // Send an email to recipients
        sendMail(recipients, subject, body) {
            return new Promise((resolve, reject) => {
                ...
            });
        }
    }
}

The name of method can't be name, version, settings, schema, broker, actions, logger, because these words are reserved.

Lifecycle events

There are some lifecycle service events, that will be triggered by ServiceBroker.

{
    name: "www",
    actions: {...},
    events: {...},
    methods: {...},

    created() {
        // Fired when the service instance created.
    },

    started() {
        // Fired when `broker.start()` called.
    }

    stopped() {
        // Fired when `broker.stop()` called.
    }
}

Properties of this

In service functions the this is always binded to the instance of service. It has some properties & methods that you can use in service functions.

NameTypeDescription
this.nameStringName of service from schema
this.versionNumberVersion of service from schema
this.settingsObjectSettings of service from schema
this.schemaObjectSchema definition of service
this.brokerServiceBrokerInstance of broker
this.loggerLoggerLogger module
this.actionsObjectActions of service. Service can call its own actions directly.

Create a service

There are several ways to create/load a service.

broker.createService()

Call the broker.createService method with the schema of service as argument. You can use this method when developing or testing.

broker.createService({
    name: "math",
    actions: {
        add(ctx) {
            return Number(ctx.params.a) + Number(ctx.params.b);
        },

        sub(ctx) {
            return Number(ctx.params.a) - Number(ctx.params.b);
        }
    }
});

Load service

You can place your service code to a single file and load this file with broker.

math.service.js

// Export the schema of service
module.exports = {
    name: "math",
    actions: {
        add(ctx) {
            return Number(ctx.params.a) + Number(ctx.params.b);
        },
        sub(ctx) {
            return Number(ctx.params.a) - Number(ctx.params.b);
        }
    }
}

index.js

// Create broker
let broker = new ServiceBroker();

// Load service
broker.loadService("./math.service");

// Start broker
broker.start();

In the service file you can also be create the instance of Service. In this case you need to export a function that returns the instance of Service.

// Export a function, that the `loadService` will be call it with the instance of ServiceBroker
module.exports = function(broker) {
    return new Service(broker, {
        name: "math",
        actions: {
            add(ctx) {
                return Number(ctx.params.a) + Number(ctx.params.b);
            },
            sub(ctx) {
                return Number(ctx.params.a) - Number(ctx.params.b);
            }
        }
    });
}

Or create a function that returns with the schema of service

// Export a function, that the `loadService` will be call with the instance of ServiceBroker
module.exports = function() {
    let users = [....];

    return {
        name: "math",
        actions: {
            create(ctx) {
                users.push(ctx.params);
            }
        }
    };
}

Load multiple services from a folder

You can load multiple services from a folder.

Syntax

broker.loadServices(folder = "./services", fileMask = "*.service.js");

Example

// Load every *.service.js file from the "./services" folder
broker.loadServices();

// Load every *.service.js file from the current folder
broker.loadServices("./");

// Load every user*.service.js file from the "./svc" folder
broker.loadServices("./svc", "user*.service.js");

Local/Private properties

If you would like to create private properties in service, we recommend to declare them in the created handler.

Example for local properties

const http = require("http");

// Simple HTTP server service
module.exports = {
    name: "www",

    settings: {
        port: 3000
    },

    created() {
        // Create HTTP server
        this.server = http.createServer(this.httpHandler);
    },

    started() {
        // Listening...
        this.server.listen(this.settings.port);
    },

    stopped() {
        // Stop server
        this.server.close();
    },

    methods() {
        // HTTP handler
        httpHandler(req, res) {
            res.end("Hello Moleculer!");
        }
    }
}

Context

When you call an action, the broker creates a Context instance which contains all request informations and pass to the action handler as argument.

Available properties & methods of Context:

NameTypeDescription
ctx.idStringContext ID
ctx.requestIDStringRequest ID. If you make sub-calls in a request, it will be the same ID
ctx.parentContextParent context, if it's a sub-call
ctx.brokerServiceBrokerInstance of broker
ctx.actionObjectInstance of action
ctx.paramsAnyParams of request. Second argument of broker.call
ctx.nodeIDStringNode ID
ctx.loggerLoggerLogger module
ctx.levelNumberLevel of request
ctx.call()FunctionYou can make a sub-call. Same arguments like broker.call
ctx.emit()FunctionEmit an event, like broker.emit

Logging

In Services every modules have a custom logger instance. It is inherited from the broker logger instance and you can set in options of broker. Every modules add a prefix to the log messages. Using that prefix you can identify the module.

let { ServiceBroker } = require("moleculer");
let broker = new ServiceBroker({
    logger: console,
    logLevel: "info"
});

broker.createService({
    name: "posts",
    actions: {
        get(ctx) {
            ctx.logger.info("Log message via Context logger");
        }
    },
    created() {
        this.logger.info("Log message via Service logger");
    }
});

broker.call("posts.get").then(() => broker.logger.info("Log message via Broker logger"));

Console messages:

[BROKER] posts service registered!
[POSTS-SVC] Log message via Service logger
[CTX] Log message via Context logger
[BROKER] Log message via Broker logger

Try it on Runkit

Custom log levels

If you want to change log level you need to set logLevel in broker options.

let broker = new ServiceBroker({
    logger: console,
    logLevel: "warn" // only print the 'warn' & 'error' log entries
});

You can set custom log levels to every module by prefix.

let broker = new ServiceBroker({
    logger: console,
    logLevel: {
        "*": "warn", // global settings
        "BROKER": "info",       // Broker logger
        "CTX": "debug",         // Context logger
        "CACHER": "warn",       // Cacher logger
        "TX": "info",           // Transporter logger
        "POSTS-SVC": "error"    // Service logger. Generated from name of service
        "USERS-SVC": false      // No logger
    }
});

Cachers

Moleculer has built-in cache solutions. You have to do two things to enable it:

  1. Set a cacher instance to the broker in constructor options
  2. Set the cache: true in action definition.
let { ServiceBroker } = require("moleculer");
let MemoryCacher = require("moleculer").Cachers.Memory;

let broker = new ServiceBroker({
    logger: console,
    cacher: new MemoryCacher()
});

broker.createService({
    name: "users",
    // cache: true, // If you enable here, all actions will be cached!
    actions: {
        list: {
            cache: true, // Cache this action
            handler(ctx) {
                this.logger.info("Handler called!");
                return [
                    { id: 1, name: "John" },
                    { id: 2, name: "Jane" }
                ]
            }
        }
    }
});

Promise.resolve()
.then(() => {
    // Will be called the handler, because the cache is empty
    return broker.call("users.list").then(res => console.log("Users count: " + res.length));
})
.then(() => {
    // Return from cache, handler won't be called
    return broker.call("users.list").then(res => console.log("Users count from cache: " + res.length));
});

Console messages:

[BROKER] users service registered!
[USERS-SVC] Handler called!
Users count: 2
Users count from cache: 2

Try it on Runkit

Cache keys

The cacher creates keys by service name, action name, and hash of params of context. The key syntax is

    <actionName>:<parameters or hash of parameters>

So if you call the posts.list action with params { limit: 5, offset: 20 }, the cacher calculate a hash from the params. So next time if you call this action with the same params, it will find in the cache by key.

// Hashed cache key for "posts.find" action
posts.find:0d6bcb785d1ae84c8c20203401460341b84eb8b968cffcf919a9904bb1fbc29a

However the hash calculation is an expensive operation. But you can specify which parameters you want to use for caching. In this case you need to set an object for cache property that contains the list of parameters.

{
    name: "posts",
    actions: {
        list: {
            cache: {
                // Only generate cache by from "limit" and "offset" param values
                keys: ["limit", "offset"]
            },
            handler(ctx) {
                return this.getList(ctx.params.limit, ctx.params.offset);
            }
        }
    }
}

// If params is { limit: 10, offset: 30 }, the cache will be:
//   posts.list:10-30

This solution is faster, so we recommend to use it in production environment.

Manual caching

You can also use the cacher manually. Just call the get, set, del methods of broker.cacher.

// Save to cache
broker.cacher.set("mykey", { a: 5 });

// Get from cache (Please note! Some cacher maybe returns with Promise)
let obj = broker.cacher.get("mykey", { a: 5 });

// Remove entry from cache
broker.cacher.del("mykey");

// Clean all entries
broker.cacher.clean();

Clear cache

When you create a new model in your service, sometimes you have to clear the old cache entries. For this purpose there are internal events. When an event like this is fired, the cacher will clean the cache.

{
    name: "users",
    actions: {
        create(ctx) {
            // Create new user
            let user = new User(ctx.params);

            // Clear all cache entries
            ctx.emit("cache.clean");

            // Clear all cache entries which keys start with `users.`
            ctx.emit("cache.clean", "users.*");

            // Clear multiple cache entries
            ctx.emit("cache.clean", [ "users.*", "posts.*" ]);

            // Delete only one entry
            ctx.emit("cache.del", "users.list");

            // Delete multiple entries
            ctx.emit("cache.del", [ "users.model:5", "users.model:8" ]);
        }
    }
}

Memory cacher

MemoryCacher is a built-in memory cache module.

let MemoryCacher = require("moleculer").Cachers.Memory;

let broker = new ServiceBroker({
    cacher: new MemoryCacher({
        ttl: 30 // Time-to-live is 30sec. Disabled: 0 or null
    })
});

Redis cacher

RedisCacher is a built-in Redis based cache module. It uses ioredis client.

let RedisCacher = require("moleculer").Cachers.Redis;

let broker = new ServiceBroker({
    cacher: new RedisCacher({
        ttl: 30, // Time-to-live is 30sec. Disabled: 0 or null
        prefix: "SERVICER" // Prefix for cache keys
        monitor: false // Turn on/off Redis client monitoring. Will be logged (on debug level) every client operations.

        // Redis settings, pass to the `new Redis()` constructor
        redis: { 
            host: "redis",
            port: 6379,
            password: "1234",
            db: 0
        }
    })
});

Custom cacher

You can also create your custom cache module. We recommend you to copy the source of MemoryCacher or RedisCacher and implement the get, set, del and clean methods.

Transporters

Transporter is an important module if you are running services on more nodes. Transporter communicates with every nodes. Send events, call requests...etc.

NATS Transporter

Moleculer has a built-in transporter for NATS.

NATS Server is a simple, high performance open source messaging system for cloud native applications, IoT messaging, and microservices architectures.

let { ServiceBroker } = require("moleculer");
let NatsTransporter = require("moleculer").Transporters.NATS;

let broker = new ServiceBroker({
	nodeID: "server-1",
	transporter: new NatsTransporter(),
	requestTimeout: 5 * 1000
});

Transporter options

You can pass options to nats.connect() method.

// Connect to 'nats://localhost:4222'
new NatsTransporter(); 

// Connect to remote server
new NatsTransporter("nats://nats.server:4222"); 

// Connect to remote server and change the prefix
new NatsTransporter({
    nats: {
        url: "nats://nats-server:4222",
    },
    prefix: "MY-PREFIX" // Use for channel names at subscribe & publish. Default: "MOL"
});

// Connect to remote server with user & pass
new NatsTransporter({
    nats: {
        url: "nats://nats-server:4222",
        user: "admin",
        pass: "1234"
    }
});

Redis Transporter

Moleculer has a built-in transporter for Redis.

let { ServiceBroker } = require("moleculer");
let RedisTransporter = require("moleculer").Transporters.Redis;

let broker = new ServiceBroker({
	nodeID: "server-1",
	transporter: new RedisTransporter(),
	requestTimeout: 5 * 1000
});

Transporter options

You can pass options to new Redis() method.

// Connect to 'redis://localhost:4222'
new RedisTransporter(); 

// Connect to remote server
new RedisTransporter("redis://redis.server:4222"); 

// Connect to remote server and change the prefix
new RedisTransporter({
    redis: {
        url: "redis://redis-server:4222",
    },
    prefix: "MY-PREFIX" // Use for channel names at subscribe & publish. Default: "MOL"
});

// Connect to remote server with user & pass
new RedisTransporter({
    redis: {
        url: "redis://redis-server:4222",
        user: "admin",
        pass: "1234"
    }
});

MQTT Transporter

Moleculer has a built-in transporter for MQTT protocol (e.g.: Mosquitto).

let { ServiceBroker } = require("moleculer");
let MqttTransporter = require("moleculer").Transporters.MQTT;

let broker = new ServiceBroker({
	nodeID: "server-1",
	transporter: new MqttTransporter(),
	requestTimeout: 5 * 1000
});

Transporter options

You can pass options to mqtt.connect() method.

// Connect to 'mqtt://localhost:4222'
new MqttTransporter(); 

// Connect to remote server
new MqttTransporter("mqtt://mqtt.server:4222"); 

// Connect to remote server and change the prefix
new MqttTransporter({
    mqtt: {
        url: "mqtt://mqtt-server:4222",
    },
    prefix: "MY-PREFIX" // Use for channel names at subscribe & publish. Default: "MOL"
});

// Connect to remote server with user & pass
new MqttTransporter({
    mqtt: {
        url: "mqtt://mqtt-server:4222",
        user: "admin",
        pass: "1234"
    }
});

Custom transporter

You can also create your custom transporter module. We recommend you that copy the source of NatsTransporter and implement the connect, disconnect, subscribe and publish methods.

Metrics

Moleculer has a metrics function. You can turn on in broker options with metrics: true property. If enabled, the broker emits metrics events in every metricsSendInterval.

Health info

Broker emits a global event as metrics.node.health with health info of node.

Example health info:

{
    "cpu": {
        "load1": 0,
        "load5": 0,
        "load15": 0,
        "cores": 4,
        "utilization": 0
    },
    "mem": {
        "free": 1217519616,
        "total": 17161699328,
        "percent": 7.094400109979598
    },
    "os": {
        "uptime": 366733.2786046,
        "type": "Windows_NT",
        "release": "6.1.7601",
        "hostname": "Developer-PC",
        "arch": "x64",
        "platform": "win32",
        "user": {
            "uid": -1,
            "gid": -1,
            "username": "Developer",
            "homedir": "C:\\Users\\Developer",
            "shell": null
        }
    },
    "process": {
        "pid": 13096,
        "memory": {
            "rss": 47173632,
            "heapTotal": 31006720,
            "heapUsed": 22112024
        },
        "uptime": 25.447
    },
    "net": {
        "ip": [
            "192.168.2.100",
            "192.168.232.1",
            "192.168.130.1",
            "192.168.56.1",
            "192.168.99.1"
        ]
    },
    "time": {
        "now": 1487338958409,
        "iso": "2017-02-17T13:42:38.409Z",
        "utc": "Fri, 17 Feb 2017 13:42:38 GMT"
    }
}

You can subscribe to it in your custom monitoring service.

Statistics

Moleculer has a statistics module that collects and aggregates the count & latency info of the requests. You can enable it in broker options with statistics: true property.

Broker emits global events as metrics.node.stats. The payload contains the statistics. You need to enable metrics functions too!

Example statistics:

{
  "requests": {
    // Total statistics
    "total": {

      // Count of requests
      "count": 45,

      // Count of error by code
      "errors": {},

      // Req/sec values
      "rps": {
        "current": 0.7999854548099126,
        // Last x values
        "values": [
          0,
          6.59868026394721,
          2.200440088017604
        ]
      },

      // Request latency values
      "latency": {
        "mean": 0.8863636363636364,
        "median": 0,
        "90th": 1,
        "95th": 5,
        "99th": 12,
        "99.5th": 12
      }
    },

    // Action-based statistics
    "actions": {
      "posts.find": {
        "count": 4,
        "errors": {},
        "rps": {
          "current": 0.599970001499925,
          "values": [
            1.7985611510791368,
            0.20004000800160032
          ]
        },
        "latency": {
          "mean": 7.5,
          "median": 5,
          "90th": 12,
          "95th": 12,
          "99th": 12,
          "99.5th": 12
        }
      }
    }
  }
}

Nodes

Moleculer supports several architectures.

Monolith architecture

In this version you are running every services on one node with one broker. In this case every service can call other services locally. So there is no network latency and no transporter. The local call is the fastest.

Monolith architecture

Microservices architecture

This is the well-known microservices architecture when every service running on individual nodes and communicates others via transporter.

Microservices architecture

Mixed architecture

In this case we are running coherent services on the same node. It is combine the advantages of monolith and microservices architectures. For example, if the posts service calls a lot of times the users service, we put them together, that we cut down the network latency between services. If this node is overloaded, we will add replicas.

Mixed architecture

Best practices (TODO)

  • service files
  • configuration
  • benchmark

Benchmarks

Under development we are measuring every important parts of the framework that we can ensure the best performance.

Benchmark results

Test

$ npm test

or in development

$ npm run ci

Contribution

Please send pull requests improving the usage and fixing bugs, improving documentation and providing better examples, or providing some testing, because these things are important.

License

Moleculer is available under the MIT license.

Contact

Copyright (c) 2017 Ice-Services

@icebob @icebob

Keywords

FAQs

Package last updated on 31 Mar 2017

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc