Skip to content

Releases: moleculerjs/moleculer

v0.12.7

14 Jun 09:03
Compare
Choose a tag to compare

Changes

  • fix action disabling with mixins #298
  • Fix metrics options and add findNextActionEndpoint to index.d.ts
  • update dependencies
  • set maxReconnectAttempts to -1 in NATS client to try reconnecting continuously

v0.12.6

14 Jun 08:04
Compare
Choose a tag to compare

Changes

  • update dependencies
  • The breakLength is changed to Infinity (single-line printing) for better log processing when logger prints objects and arrays.
  • adds ability to customise console object/array printing #285
    const util = require("util");
    
    const broker = new ServiceBroker({
        logger: true,
        logObjectPrinter: o => util.inspect(o, { depth: 4, colors: false, breakLength: 50 }) // `breakLength: 50` activates multi-line object
    });    

v0.12.5

14 Jun 08:04
Compare
Choose a tag to compare

Changes

  • fix AMQP logs. #270
  • fix transferred retryable error handling
  • broker.createService supports ES6 classes
  • fix broken promise chain if trackContext is enabled

v0.12.4

10 May 07:54
Compare
Choose a tag to compare

New

Graceful shutdown

Thanks for @rmccallum81, ServiceBroker supports graceful shutdown. You can enable it with trackContext broker option. If you enable it, all services wait for all running contexts before shutdowning. You can also define a timeout value with gracefulStopTimeout broker option.

const broker = new ServiceBroker({
    trackContext: true,
    gracefulStopTimeout: 5 * 1000 // waiting max 5 sec
});

This timeout can be overwrite in service settings with $gracefulStopTimeout property.

Changes

  • fix service registry update after reconnecting. #262
  • update index.d.ts
  • update dependencies
  • fix distributed timeout handling

v0.12.3

19 Apr 14:35
Compare
Choose a tag to compare

Changes

  • fix empty service mixins issue (mixins: []).
  • update index.d.ts

v0.12.2

11 Apr 13:16
Compare
Choose a tag to compare

New

Latency strategy

This strategy selects a node which has the lowest latency, measured by periodic PING. Notice that the strategy only ping one of nodes from a single host. Due to the node list can be very long, it gets samples and selects the host with the lowest latency from only samples instead of the whole node list.

Usage

let broker = new ServiceBroker({
    registry: {
        strategy: "Latency"
    }
});

Strategy options

Name Type Default Description
sampleCount Number 5 the number of samples. If you have a lot of hosts/nodes, it's recommended to increase the value.
lowLatency Number 10 the low latency (ms). The node which has lower latency than this value is selected immediately.
collectCount Number 5 the number of measured latency per host to keep in order to calculate the average latency.
pingInterval Number 10 ping interval (s). If you have a lot of host/nodes, it's recommended to increase the value.

Usage with custom options

let broker = new ServiceBroker({
    registry: {
        strategy: "Latency",
        strategyOptions: {
            sampleCount: 15,
            lowLatency: 20,
            collectCount: 10,
            pingInterval: 15
        }
    }
});

Thanks for @zllovesuki!

Filemask for Moleculer Runner

There is a new Moleculer Runner option --mask to define filemask when load all services from folders.

Example

$ moleculer-runner.js -r --mask **/user*.service.js examples

Example to load Typescript services

$ node -r ts-node/register node_modules/moleculer/bin/moleculer-runner --hot --repl --mask **/*.service.ts services

Changes

  • fix d.ts issues
  • fix event group handling in mixins (#217)
  • move mergeSchemas from utils to Service static method. It can be overwritten in a custom ServiceFactory
  • improve d.ts
  • fix prefix option in Redis Cacher (223)
  • remove nanomatch dependency, use own implementation
  • fix ContextFactory issue (235)
  • expose utility functions as require("moleculer").Utils
  • overwritable mergeSchemas static method in Service class.
  • Moleculer Runner precedence order is changed. The SERVICES & SERVICEDIR env vars overwrites the paths in CLI arguments.

v0.12.0

03 Mar 15:48
Compare
Choose a tag to compare

This version contains the most changes in the history of Moleculer! More than 200 commits with 17k additions and a lot of new features.

Breaking changes

Github organization is renamed

The Github organization name (Ice Services) has been renamed to MoleculerJS. Please update your bookmarks.

Mixin merging logic is changed

To support #188, mixin merging logic is changed at actions. Now it uses defaultsDeep for merging. It means you can extend the actions definition of mixins, no need to redeclare the handler.

Add extra action properties but handler is untouched

    // mixin.service.js
    module.exports = {
        actions: {
            create(ctx) {
                // Action handler without `params`
            }
        }
    };
    // my.service.js
    module.exports = {
        mixins: [MixinService]
        actions: {
            create: {
                // Add only `params` property to the `create` action
                // The handler is merged from mixin
                params: {
                    name: "string"
                }
            }
        }

    };

Wrapper removed from transporter options

If you are using transporter options, you will need to migrate them. The transporter specific wrapper has been removed from options (nats, redis, mqtt, amqp).

Before

// NATS transporter
const broker = new ServiceBroker({
    transporter: {
        type: "NATS",
        options: {
            nats: {
                user: "admin",
                pass: "1234"    
            }
        }
    }
});

// Redis transporter
const broker = new ServiceBroker({
    transporter: {
        type: "Redis",
        options: {
            redis: {
                port: 6379,
                db: 0
            }
        }
    }
});

// MQTT transporter
const broker = new ServiceBroker({
    transporter: {
        type: "MQTT",
        options: {
            mqtt: {
                user: "admin",
                pass: "1234"    
            }
        }
    }
});

// AMQP transporter
const broker = new ServiceBroker({
    transporter: {
        type: "AMQP",
        options: {
            amqp: {
                prefetch: 1 
            }
        }
    }
});

After

// NATS transporter
const broker = new ServiceBroker({
    transporter: {
        type: "NATS",
        options: {
            user: "admin",
            pass: "1234"    
        }
    }
});

// Redis transporter
const broker = new ServiceBroker({
    transporter: {
        type: "Redis",
        options: {
            port: 6379,
            db: 0
        }
    }
});

// MQTT transporter
const broker = new ServiceBroker({
    transporter: {
        type: "MQTT",
        options: {
            user: "admin",
            pass: "1234"    
        }
    }
});

// AMQP transporter
const broker = new ServiceBroker({
    transporter: {
        type: "AMQP",
        options: {
            prefetch: 1 
        }
    }
});

Default nodeID generator changed

When nodeID didn't define in broker options, the broker generated it from hostname (os.hostname()). It could cause problem for new users when they tried to start multiple instances on the same computer. Therefore, the broker generates nodeID from hostname and process PID. The newly generated nodeID looks like server-6874 where server is the hostname and 6874 is the PID.

Protocol changed

The transport protocol is changed. The new version is 3. Check the changes.

It means, the >=0.12.x versions can't communicate with old <=0.11 versions.

Changes:

  • the RESPONSE packet has a new field meta.
  • the EVENT packet has a new field broadcast.
  • the port field is removed from INFO packet.
  • the INFO packet has a new field hostname.

New features

New ServiceBroker options

There are some new properties in ServiceBroker option: middlewares, created, started, stopped.

They can be useful when you use broker config file and start your project with Moleculer Runner.

// moleculer.config.js
module.exports = {
    logger: true,

    // Add middlewares
    middlewares: [myMiddleware()],

    // Fired when the broker created
    created(broker) {
    },

    // Fired when the broker started
    started(broker) {
        // You can return Promise
        return broker.Promise.resolve();
    },

    // Fired when the broker stopped
    stopped(broker) {
        // You can return Promise
        return broker.Promise.resolve();
    }
};

Broadcast events with group filter

The broker.broadcast function has a third groups argument similar to broker.emit.

// Send to all "mail" service instances
broker.broadcast("user.created", { user }, "mail");

// Send to all "user" & "purchase" service instances.
broker.broadcast("user.created", { user }, ["user", "purchase"]);

CPU usage-based strategy

There is a new CpuUsageStrategy strategy. It selects a node which has the lowest CPU usage.
Due to the node list can be very long, it gets samples and selects the node with the lowest CPU usage from only samples instead of the whole node list.

There are 2 options for the strategy:

  • sampleCount: the number of samples. Default: 3
  • lowCpuUsage: the low CPU usage percent. The node which has lower CPU usage than this value is selected immediately. Default: 10

Usage:

const broker = new ServiceBroker({
    registry: {
        strategy: "CpuUsage"
    }
});

Usage with custom options

const broker = new ServiceBroker({
    registry: {
        strategy: "CpuUsage",
        strategyOptions: {
            sampleCount: 3,
            lowCpuUsage: 10
        }
    }
});

Starting logic is changed

The broker & services starting logic has been changed.

Previous logic: the broker starts transporter connecting. When it's done, it starts all services (calls service started handlers). It has a disadvantage because other nodes can send requests to these services, while they are still starting and not ready yet.

New logic: the broker starts transporter connecting but it doesn't publish the local service list to remote nodes. When it's done, it starts all services (calls service started handlers). Once all services start successfully, broker publishes the local service list to remote nodes. Hence other nodes send requests only after all local service started properly.

Please note: you can make dead-locks when two services wait for each other. E.g.: users service has dependencies: [posts] and posts service has dependencies: [users]. To avoid it remove the concerned service from dependencies and use waitForServices method out of started handler instead.

Metadata is sent back to requester

At requests, ctx.meta is sent back to the caller service. You can use it to send extra meta information back to the caller.
E.g.: send response headers back to API gateway or set resolved logged in user to metadata.

Export & download a file with API gateway:

// Export data
export(ctx) {
    const rows = this.adapter.find({});

    // Set response headers to download it as a file
    ctx.meta.headers = {
        "Content-Type": "application/json; charset=utf-8",
        "Content-Disposition": 'attachment; filename=\"book.json\"'
    }

    return rows;
}

Authenticate:

auth(ctx) {
    let user = this.getUserByJWT(ctx.params.token);
    if (ctx.meta.user) {
        ctx.meta.user = user;

        return true;
    }

    throw new Forbidden();
}

Better ES6 class support

If you like better ES6 classes than Moleculer service schema, you can write your services in ES6 classes.

There are two ways to do it:

  1. Native ES6 classes with schema parsing

    Define actions and events handlers as class methods. Call the parseServiceSchema method in constructor with schema definition where the handlers pointed to these class methods.

    const Service = require("moleculer").Service;
    
    class GreeterService extends Service {
    
        constructor(broker) {
            super(broker);
    
            this.parseServiceSchema({
                name: "greeter",
                version: "v2",
                meta: {
                    scalable: true
                },
                dependencies: [
                    "auth",
                    "users"
                ],
    
                settings: {
                    upperCase: true
                },
                actions: {
                    hello: this.hello,
                    welcome: {
                        cache: {
                            keys: ["name"]
                        },
                        params: {
                            name: "string"
                        },
                        handler: this.welcome
                    }
                },
                events: {
                    "user.created": this.userCreated
                },
                created: this.serviceCreated,
                started: this.serviceStarted,
                stopped: this.serviceStopped,
            });
        }
    
        // Action handler
        hello() {
            return "Hello Moleculer";
        }
    
        // Action handler
        welcome(ctx) {
     ...
Read more

v0.11.10

19 Jan 13:47
Compare
Choose a tag to compare

New

Built-in clustering in Moleculer Runner #169

By @tinchoz49 Moleculer Runner has a new built-in clustering function. With it, you can start multiple instances from your broker.

Example to start all services from the services folder in 4 instances.

$ moleculer-runner --instances 4 services

Please note, the nodeID will be suffixed with the worker ID.

Context meta & params in metrics events #166

By @dani8art you can set that the broker put some ctx.meta and ctx.params fields to the metrics events.
You can define it in the action definition:

module.exports = {
    name: "test",
    actions: {
        import: {
            cache: true,
            metrics: {
                // Disable to add `ctx.params` to metrics payload. Default: false
                params: false,
                // Enable to add `ctx.meta` to metrics payload. Default: true
                meta: true
            },
            handler(ctx) {
                // ...
            }
        }
    }
}

If the value is true, it adds all fields. If Array, it adds the specified fields. If Function, it calls with params or metaand you need to return with an Object.

v0.11.9

08 Jan 21:14
Compare
Choose a tag to compare

New

Strategy resolver

ServiceBroker can resolve the strategy from a string.

let broker = new ServiceBroker({
    registry: {
        strategy: "Random"
        // strategy: "RoundRobin"
    }
});

You can set it via env variables as well, if you are using the Moleculer Runner:

$ REGISTRY_STRATEGY=random

Load env files in Moleculer Runner #158

Moleculer runner can load .env file at starting. There are two new cli options to load env file:

  • -e, --env - Load envorinment variables from the '.env' file from the current folder.
  • -E, --envfile <filename> - Load envorinment variables from the specified file.

Example

# Load the default .env file from current directory
$ moleculer-runner --env 

# Load the specified .my-env file
$ moleculer-runner --envfile .my-env

Fixes

  • fixed hot reloading after broken service files by @askuzminov (#155)
  • allow fallbackResponse to be falsy values

v0.11.6

07 Nov 16:50
Compare
Choose a tag to compare

New

New cacher features

In action cache keys you can use meta keys with # prefix.

broker.createService({
    name: "posts",
    actions: {
        list: {
            cache: {
                // Cache key:  "limit" & "offset" from ctx.params, "user.id" from ctx.meta
                keys: ["limit", "offset", "#user.id"],
                ttl: 5
            },
            handler(ctx) {...}
        }
    }
});

You can override the cacher default TTL setting in action definition.

let broker = new ServiceBroker({
    cacher: {
        type: "memory",
        options: {
            ttl: 30 // 30 seconds
        }
    }
});

broker.createService({
    name: "posts",
    actions: {
        list: {
            cache: {
                // This cache entries will be expired after 5 seconds instead of 30.
                ttl: 5
            },
            handler(ctx) {...}
        }
    }
});

You can change the built-in cacher keygen function to your own one.

let broker = new ServiceBroker({
    cacher: {
        type: "memory",
        options: {
            keygen(name, params, meta, keys) {
                // Generate a cache key
                return ...;
            }
        }
    }
});

Others