1 | --------------------------------------------------
|
2 | <a name="0.13.5"></a>
|
3 | # [0.13.5](https://github.com/moleculerjs/moleculer/compare/v0.13.4...v0.13.5) (2018-12-09)
|
4 |
|
5 | # New
|
6 |
|
7 | ## Conditional caching
|
8 | It's a common issue that you enable caching for an action but sometimes you don't want to get data from cache. To solve it you may set `ctx.meta.$cache = false` before calling and the cacher won't send cached responses.
|
9 |
|
10 | **Example**
|
11 | ```js
|
12 | // Turn off caching for this request
|
13 | broker.call("greeter.hello", { name: "Moleculer" }, { meta: { $cache: false }}))
|
14 | ```
|
15 |
|
16 | Other solution is that you use a custom function which enables or disables caching for every request. The function gets the `ctx` Context instance so it has access any params or meta data.
|
17 |
|
18 | **Example**
|
19 | ```js
|
20 | // greeter.service.js
|
21 | module.exports = {
|
22 | name: "greeter",
|
23 | actions: {
|
24 | hello: {
|
25 | cache: {
|
26 | enabled: ctx => ctx.params.noCache !== true,
|
27 | keys: ["name"]
|
28 | },
|
29 | handler(ctx) {
|
30 | this.logger.debug(chalk.yellow("Execute handler"));
|
31 | return `Hello ${ctx.params.name}`;
|
32 | }
|
33 | }
|
34 | }
|
35 | };
|
36 |
|
37 | // Use custom `enabled` function to turn off caching for this request
|
38 | broker.call("greeter.hello", { name: "Moleculer", noCache: true }))
|
39 | ```
|
40 |
|
41 | ## LRU memory cacher
|
42 | An LRU memory cacher has been added to the core modules. It uses the familiar [lru-cache](https://github.com/isaacs/node-lru-cache) library.
|
43 |
|
44 | **Example**
|
45 | ```js
|
46 | let broker = new ServiceBroker({ cacher: "MemoryLRU" });
|
47 | ```
|
48 |
|
49 | ```js
|
50 | let broker = new ServiceBroker({
|
51 | logLevel: "debug",
|
52 | cacher: {
|
53 | type: "MemoryLRU",
|
54 | options: {
|
55 | // Maximum items
|
56 | max: 100,
|
57 | // Time-to-Live
|
58 | ttl: 3
|
59 | }
|
60 | }
|
61 | });
|
62 | ```
|
63 |
|
64 |
|
65 | # Changes
|
66 | - throw the error further in `loadService` method so that Runner prints the correct error stack.
|
67 | - new `packetLogFilter` transit option to filter packets in debug logs (e.g. HEARTBEAT packets) by [@faeron](https://github.com/faeron)
|
68 | - the Redis cacher `clean` & `del` methods handle array parameter by [@dkuida](https://github.com/dkuida)
|
69 | - the Memory cacher `clean` & `del` methods handle array parameter by [@icebob](https://github.com/icebob)
|
70 | - fix to handle `version: 0` as a valid version number by [@ngraef](https://github.com/ngraef)
|
71 |
|
72 | --------------------------------------------------
|
73 | <a name="0.13.4"></a>
|
74 | # [0.13.4](https://github.com/moleculerjs/moleculer/compare/v0.13.3...v0.13.4) (2018-11-04)
|
75 |
|
76 | # Changes
|
77 | - catch errors in `getCpuUsage()` method.
|
78 | - support multiple urls in AMQP transporter by [@urossmolnik](https://github.com/urossmolnik)
|
79 | - fix AMQP connection recovery by [@urossmolnik](https://github.com/urossmolnik)
|
80 | - add `transit.disableReconnect` option to disable reconnecting logic at broker starting by [@Gadi-Manor](https://github.com/Gadi-Manor)
|
81 | - catch `os.userInfo` errors in health action by [@katsanva](https://github.com/katsanva)
|
82 | - allow specifying 0 as `retries` [#404](https://github.com/moleculerjs/moleculer/issues/404) by [@urossmolnik](https://github.com/urossmolnik)
|
83 | - fix `GraceFulTimeoutError` bug [#400](https://github.com/moleculerjs/moleculer/issues/400)
|
84 | - fix event return handling to avoid localEvent error handling issue in middleware [#403](https://github.com/moleculerjs/moleculer/issues/403)
|
85 | - update [fastest-validator](https://github.com/icebob/fastest-validator) to the 0.6.12 version
|
86 | - update all dependencies
|
87 |
|
88 | --------------------------------------------------
|
89 | <a name="0.13.3"></a>
|
90 | # [0.13.3](https://github.com/moleculerjs/moleculer/compare/v0.13.2...v0.13.3) (2018-09-27)
|
91 |
|
92 | # Changes
|
93 | - update dependencies
|
94 | - fix MQTTS connection string protocol from `mqtt+ssl://` to `mqtts://` by [@AndreMaz](https://github.com/AndreMaz)
|
95 | - Moleculer Runner supports typescript configuration file `moleculer.config.ts`
|
96 | - fix to call service start after hot-reloading.
|
97 | - fix Bluebird warning in service loading [#381](https://github.com/moleculerjs/moleculer/issues/381) by [@faeron](https://github.com/faeron)
|
98 | - fix `waitForServices` definition in `index.d.ts` [#358](https://github.com/moleculerjs/moleculer/issues/358)
|
99 | - fix `cpuUsage` issue [#379](https://github.com/moleculerjs/moleculer/issues/379) by [@faeron](https://github.com/faeron)
|
100 |
|
101 |
|
102 | --------------------------------------------------
|
103 | <a name="0.13.2"></a>
|
104 | # [0.13.2](https://github.com/moleculerjs/moleculer/compare/v0.13.1...v0.13.2) (2018-08-16)
|
105 |
|
106 | # Changes
|
107 | - update dependencies
|
108 | - add Notepack (other MsgPack) serializer
|
109 | - `skipProcessEventRegistration` broker option to disable `process.on` shutdown event handlers which stop broker.
|
110 | - make unique service dependencies
|
111 | - add `socketOptions` to AMQP transporter options. [#330](https://github.com/moleculerjs/moleculer/pull/330)
|
112 | - fix unhandled promise in AMQP transporter `connect` method.
|
113 | - add `autoDeleteQueues` option to AMQP transporter. [#341](https://github.com/moleculerjs/moleculer/pull/341)
|
114 | - ES6 support has improved. [#348](https://github.com/moleculerjs/moleculer/pull/348)
|
115 | - add `qos` transporter option to MQTT transporter. Default: `0`
|
116 | - add `topicSeparator` transporter option to MQTT transporter. Default: `.`
|
117 | - fix MQTT transporter disconnect logic (waiting for in-flight messages)
|
118 | - add support for non-defined defaultOptions variables [#350](https://github.com/moleculerjs/moleculer/pull/350)
|
119 | - update ioredis to v4
|
120 |
|
121 | --------------------------------------------------
|
122 | <a name="0.13.1"></a>
|
123 | # [0.13.1](https://github.com/moleculerjs/moleculer/compare/v0.13.0...v0.13.1) (2018-07-13)
|
124 |
|
125 | # Changes
|
126 | - improve `index.d.ts`
|
127 | - support Duplex streams [#325](https://github.com/moleculerjs/moleculer/issues/325)
|
128 | - update dependencies
|
129 |
|
130 | --------------------------------------------------
|
131 | <a name="0.13.0"></a>
|
132 | # [0.13.0](https://github.com/moleculerjs/moleculer/compare/v0.12.8...v0.13.0) (2018-07-08)
|
133 |
|
134 | **Migration guide from v0.12.x to v0.13.x is [here](docs/MIGRATION_GUIDE_0.13.md).**
|
135 |
|
136 | # Breaking changes
|
137 |
|
138 | ## Streaming support
|
139 | Built-in streaming support has just been implemented. Node.js streams can be transferred as request `params` or as response. You can use it to transfer uploaded file from a gateway or encode/decode or compress/decompress streams.
|
140 |
|
141 | >**Why is it a breaking change?**
|
142 | >
|
143 | > Because the protocol has been extended with a new field and it caused a breaking change in schema-based serializators (ProtoBuf, Avro). Therefore, if you use ProtoBuf or Avro, you won't able to communicate with the previous (<=0.12) brokers. Using JSON or MsgPack serializer, there is nothing extra to do.
|
144 |
|
145 | ### Examples
|
146 |
|
147 | **Send a file to a service as a stream**
|
148 | ```js
|
149 | const stream = fs.createReadStream(fileName);
|
150 |
|
151 | broker.call("storage.save", stream, { meta: { filename: "avatar-123.jpg" }});
|
152 | ```
|
153 |
|
154 | Please note, the `params` should be a stream, you cannot add any more variables to the request. Use the `meta` property to transfer additional data.
|
155 |
|
156 | **Receiving a stream in a service**
|
157 | ```js
|
158 | module.exports = {
|
159 | name: "storage",
|
160 | actions: {
|
161 | save(ctx) {
|
162 | const s = fs.createWriteStream(`/tmp/${ctx.meta.filename}`);
|
163 | ctx.params.pipe(s);
|
164 | }
|
165 | }
|
166 | };
|
167 | ```
|
168 |
|
169 | **Return a stream as response in a service**
|
170 | ```js
|
171 | module.exports = {
|
172 | name: "storage",
|
173 | actions: {
|
174 | get: {
|
175 | params: {
|
176 | filename: "string"
|
177 | },
|
178 | handler(ctx) {
|
179 | return fs.createReadStream(`/tmp/${ctx.params.filename}`);
|
180 | }
|
181 | }
|
182 | }
|
183 | };
|
184 | ```
|
185 |
|
186 | **Process received stream on the caller side**
|
187 | ```js
|
188 | const filename = "avatar-123.jpg";
|
189 | broker.call("storage.get", { filename })
|
190 | .then(stream => {
|
191 | const s = fs.createWriteStream(`./${filename}`);
|
192 | stream.pipe(s);
|
193 | s.on("close", () => broker.logger.info("File has been received"));
|
194 | })
|
195 | ```
|
196 |
|
197 | **AES encode/decode example service**
|
198 | ```js
|
199 | const crypto = require("crypto");
|
200 | const password = "moleculer";
|
201 |
|
202 | module.exports = {
|
203 | name: "aes",
|
204 | actions: {
|
205 | encrypt(ctx) {
|
206 | const encrypt = crypto.createCipher("aes-256-ctr", password);
|
207 | return ctx.params.pipe(encrypt);
|
208 | },
|
209 |
|
210 | decrypt(ctx) {
|
211 | const decrypt = crypto.createDecipher("aes-256-ctr", password);
|
212 | return ctx.params.pipe(decrypt);
|
213 | }
|
214 | }
|
215 | };
|
216 | ```
|
217 |
|
218 | ## Better Service & Broker lifecycle handling
|
219 | The ServiceBroker & Service lifecycle handler logic has already been improved. The reason for amendment was a problem occuring during loading more services locally; they could call each others' actions before `started` execution. It generally causes errors if database connecting process started in the `started` event handler.
|
220 |
|
221 | This problem has been fixed with a probable side effect: causing errors (mostly in unit tests) if you call the local services without `broker.start()`.
|
222 |
|
223 | **It works in the previous version**
|
224 | ```js
|
225 | const { ServiceBroker } = require("moleculer");
|
226 |
|
227 | const broker = new ServiceBroker();
|
228 |
|
229 | broker.loadService("./math.service.js");
|
230 |
|
231 | broker.call("math.add", { a: 5, b: 3 }).then(res => console.log);
|
232 | // Prints: 8
|
233 | ```
|
234 | From v0.13 it throws a `ServiceNotFoundError` exception, because the service is only loaded but not started yet.
|
235 |
|
236 | **Correct logic**
|
237 | ```js
|
238 | const { ServiceBroker } = require("moleculer");
|
239 |
|
240 | const broker = new ServiceBroker();
|
241 |
|
242 | broker.loadService("./math.service.js");
|
243 |
|
244 | broker.start().then(() => {
|
245 | broker.call("math.add", { a: 5, b: 3 }).then(res => console.log);
|
246 | // Prints: 8
|
247 | });
|
248 | ```
|
249 |
|
250 | or with await
|
251 |
|
252 | ```js
|
253 | broker.loadService("./math.service.js");
|
254 |
|
255 | await broker.start();
|
256 |
|
257 | const res = await broker.call("math.add", { a: 5, b: 3 });
|
258 | console.log(res);
|
259 | // Prints: 8
|
260 | ```
|
261 |
|
262 | Similar issue has been fixed at broker shutdown. Previously when you stopped a broker, which while started to stop local services, it still acccepted incoming requests from remote nodes.
|
263 |
|
264 | The shutdown logic has also been changed. When you call `broker.stop`, at first broker publishes an empty service list to remote nodes, so they route the requests to other instances.
|
265 |
|
266 |
|
267 | ## Default console logger
|
268 | No longer need to set `logger: console` in broker options, because ServiceBroker uses `console` as default logger.
|
269 |
|
270 | ```js
|
271 | const broker = new ServiceBroker();
|
272 | // It will print log messages to the console
|
273 | ```
|
274 |
|
275 | **Disable loggging (e.g. in tests)**
|
276 | ```js
|
277 | const broker = new ServiceBroker({ logger: false });
|
278 | ```
|
279 |
|
280 | ## Changes in internal event sending logic
|
281 | The `$` prefixed internal events will be transferred if they are called by `emit` or `broadcast`. If you don't want to transfer them, use the `broadcastLocal` method.
|
282 |
|
283 | > From v0.13, the `$` prefixed events mean built-in core events instead of internal "only-local" events.
|
284 |
|
285 | ## Improved Circuit Breaker
|
286 | Threshold-based circuit-breaker solution has been implemented. It uses a time window to check the failed request rate. Once the `threshold` value is reached, it trips the circuit breaker.
|
287 |
|
288 | ```js
|
289 | const broker = new ServiceBroker({
|
290 | nodeID: "node-1",
|
291 | circuitBreaker: {
|
292 | enabled: true,
|
293 | threshold: 0.5,
|
294 | minRequestCount: 20,
|
295 | windowTime: 60, // in seconds
|
296 | halfOpenTime: 5 * 1000,
|
297 | check: err => err && err.code >= 500
|
298 | }
|
299 | });
|
300 | ```
|
301 |
|
302 | Instead of `failureOnTimeout` and `failureOnReject` properties, there is a new `check()` function property in the options. It is used by circuit breaker in order to detect which error is considered as a failed request.
|
303 |
|
304 | You can override these global options in action definition, as well.
|
305 |
|
306 | ```js
|
307 | module.export = {
|
308 | name: "users",
|
309 | actions: {
|
310 | create: {
|
311 | circuitBreaker: {
|
312 | // All CB options can be overwritten from broker options.
|
313 | threshold: 0.3,
|
314 | windowTime: 30
|
315 | },
|
316 | handler(ctx) {}
|
317 | }
|
318 | }
|
319 | };
|
320 | ```
|
321 |
|
322 | ### CB metrics events removed
|
323 | The metrics circuit breaker events have been removed due to internal event logic changes.
|
324 | Use the `$circuit-breaker.*` events instead of `metrics.circuit-breaker.*` events.
|
325 |
|
326 | ## Improved Retry feature (with exponential backoff)
|
327 | The old retry feature has been improved. Now it uses exponential backoff for retries. The old solution retries the request immediately in failures.
|
328 | The retry options have also been changed in the broker options. Every option is under the `retryPolicy` property.
|
329 |
|
330 | ```js
|
331 | const broker = new ServiceBroker({
|
332 | nodeID: "node-1",
|
333 | retryPolicy: {
|
334 | enabled: true,
|
335 | retries: 5,
|
336 | delay: 100,
|
337 | maxDelay: 2000,
|
338 | factor: 2,
|
339 | check: err => err && !!err.retryable
|
340 | }
|
341 | });
|
342 | ```
|
343 |
|
344 | **Overwrite the `retries` value in calling option**
|
345 | The `retryCount` calling options has been renamed to `retries`.
|
346 |
|
347 | ```js
|
348 | broker.call("posts.find", {}, { retries: 3 });
|
349 | ```
|
350 |
|
351 | There is a new `check()` function property in the options. It is used by the Retry middleware in order to detect which error is a failed request and needs a retry. The default function checks the `retryable` property of errors.
|
352 |
|
353 | These global options can be overridden in action definition, as well.
|
354 |
|
355 | ```js
|
356 | module.export = {
|
357 | name: "users",
|
358 | actions: {
|
359 | find: {
|
360 | retryPolicy: {
|
361 | // All Retry policy options can be overwritten from broker options.
|
362 | retries: 3,
|
363 | delay: 500
|
364 | },
|
365 | handler(ctx) {}
|
366 | },
|
367 | create: {
|
368 | retryPolicy: {
|
369 | // Disable retries for this action
|
370 | enabled: false
|
371 | },
|
372 | handler(ctx) {}
|
373 | }
|
374 | }
|
375 | };
|
376 | ```
|
377 |
|
378 | ## Changes in context tracker
|
379 | There are also some changes in context tracker configuration.
|
380 |
|
381 | ```js
|
382 | const broker = new ServiceBroker({
|
383 | nodeID: "node-1",
|
384 | tracking: {
|
385 | enabled: true,
|
386 | shutdownTimeout: 5000
|
387 | }
|
388 | });
|
389 | ```
|
390 |
|
391 | **Disable tracking in calling option at calling**
|
392 |
|
393 | ```js
|
394 | broker.call("posts.find", {}, { tracking: false });
|
395 | ```
|
396 |
|
397 | _The shutdown timeout can be overwritten by `$shutdownTimeout` property in service settings._
|
398 |
|
399 |
|
400 | ## Removed internal statistics module
|
401 | The internal statistics module (`$node.stats`) is removed. Yet you need it, download from [here](https://gist.github.com/icebob/99dc388ee29ae165f879233c2a9faf63), load as a service and call the `stat.snapshot` to receive the collected statistics.
|
402 |
|
403 | ## Renamed errors
|
404 | Some errors have been renamed in order to follow name conventions.
|
405 | - `ServiceNotAvailable` -> `ServiceNotAvailableError`
|
406 | - `RequestRejected` -> `RequestRejectedError`
|
407 | - `QueueIsFull` -> `QueueIsFullError`
|
408 | - `InvalidPacketData` -> `InvalidPacketDataError`
|
409 |
|
410 | ## Context nodeID changes
|
411 | The `ctx.callerNodeID` has been removed. The `ctx.nodeID` contains the target or caller nodeID. If you need the current nodeID, use `ctx.broker.nodeID`.
|
412 |
|
413 | ## Enhanced ping method
|
414 | It returns `Promise` with results of ping responses. Moreover, the method is renamed to `broker.ping`.
|
415 |
|
416 | **Ping a node with 1sec timeout**
|
417 | ```js
|
418 | broker.ping("node-123", 1000).then(res => broker.logger.info(res));
|
419 | ```
|
420 | Output:
|
421 | ```js
|
422 | {
|
423 | nodeID: 'node-123',
|
424 | elapsedTime: 16,
|
425 | timeDiff: -3
|
426 | }
|
427 | ```
|
428 |
|
429 | **Ping all known nodes**
|
430 | ```js
|
431 | broker.ping().then(res => broker.logger.info(res));
|
432 | ```
|
433 | Output:
|
434 | ```js
|
435 | {
|
436 | "node-100": {
|
437 | nodeID: 'node-100',
|
438 | elapsedTime: 10,
|
439 | timeDiff: -2
|
440 | } ,
|
441 | "node-101": {
|
442 | nodeID: 'node-101',
|
443 | elapsedTime: 18,
|
444 | timeDiff: 32
|
445 | },
|
446 | "node-102": {
|
447 | nodeID: 'node-102',
|
448 | elapsedTime: 250,
|
449 | timeDiff: 850
|
450 | }
|
451 | }
|
452 | ```
|
453 |
|
454 | ## Amended cacher key generation logic
|
455 | When you didn't define `keys` at caching, the cacher hashed the whole `ctx.params` and used as a key to store the content. This method was too slow and difficult to implement to other platforms. Therefore we have changed it. The new method is simpler, the key generator concatenates all property names & values from `ctx.params`.
|
456 |
|
457 | However, the problem with this new logic is that the key can be very long. It can cause performance issues when you use too long keys to get or save cache entries. To avoid it, there is a `maxParamsLength` option to limit the key length. If it is longer than the configured limit, the cacher calculates a hash (SHA256) from the full key and add it to the end of key.
|
458 |
|
459 | > The minimum of `maxParamsLength` is `44` (SHA 256 hash length in Base64).
|
460 | >
|
461 | > To disable this feature, set it to `0` or `null`.
|
462 |
|
463 | **Generate a full key from the whole params**
|
464 | ```js
|
465 | cacher.getCacheKey("posts.find", { id: 2, title: "New post", content: "It can be very very looooooooooooooooooong content. So this key will also be too long" });
|
466 | // Key: 'posts.find:id|2|title|New post|content|It can be very very looooooooooooooooooong content. So this key will also be too long'
|
467 | ```
|
468 |
|
469 | **Generate a limited key with hash**
|
470 | ```js
|
471 | const broker = new ServiceBroker({
|
472 | logger: console,
|
473 | cacher: {
|
474 | type: "Memory",
|
475 | options: {
|
476 | maxParamsLength: 60
|
477 | }
|
478 | }
|
479 | });
|
480 |
|
481 | cacher.getCacheKey("posts.find", { id: 2, title: "New post", content: "It can be very very looooooooooooooooooong content. So this key will also be too long" });
|
482 | // Key: 'posts.find:id|2|title|New pL4ozUU24FATnNpDt1B0t1T5KP/T5/Y+JTIznKDspjT0='
|
483 | ```
|
484 |
|
485 | Of course, you can use your custom solution with `keygen` cacher options like earlier.
|
486 |
|
487 | ## Cacher matcher changed
|
488 | The cacher matcher code also changed in `cacher.clean` method. The previous (wrong) matcher couldn't handle dots (.) properly in patterns. E.g the `posts.*` pattern cleaned the `posts.find.something` keys, too. Now it has been fixed, but it means that you should use `posts.**` pattern because the `params` and `meta` values can contain dots.
|
489 |
|
490 | ## Changed Moleculer errors signature
|
491 | The following Moleculer Error classes constructor arguments is changed to `constructor(data)`:
|
492 | - `ServiceNotFoundError`
|
493 | - `ServiceNotAvailableError`
|
494 | - `RequestTimeoutError`
|
495 | - `RequestSkippedError`
|
496 | - `RequestRejectedError`
|
497 | - `QueueIsFullError`
|
498 | - `MaxCallLevelError`
|
499 | - `ProtocolVersionMismatchError`
|
500 | - `InvalidPacketDataError`
|
501 |
|
502 | **Before**
|
503 | ```js
|
504 | throw new ServiceNotFoundError("posts.find", "node-123");
|
505 | ```
|
506 |
|
507 | **Now**
|
508 | ```js
|
509 | throw new ServiceNotFoundError({ action: "posts.find", nodeID: "node-123" });
|
510 | ```
|
511 |
|
512 |
|
513 | # New
|
514 |
|
515 | ## New state-of-the-art middlewares
|
516 | We have been improved the current middleware handler and enriched it with a lot of useful features. As a result, you can hack more internal flow logic with custom middlewares (e.g. event sending, service creating, service starting...etc)
|
517 |
|
518 | The new one is an `Object` with hooks instead of a simple `Function`. However, the new solution is backward compatible, so you don't need to migrate your old middlewares.
|
519 |
|
520 | **A new middleware with all available hooks**
|
521 | ```js
|
522 | const MyCustomMiddleware = {
|
523 | // Wrap local action handlers (legacy middleware handler)
|
524 | localAction(next, action) {
|
525 |
|
526 | },
|
527 |
|
528 | // Wrap remote action handlers
|
529 | remoteAction(next, action) {
|
530 |
|
531 | },
|
532 |
|
533 | // Wrap local event handlers
|
534 | localEvent(next, event) {
|
535 |
|
536 | }
|
537 |
|
538 | // Wrap broker.createService method
|
539 | createService(next) {
|
540 |
|
541 | }
|
542 |
|
543 | // Wrap broker.destroyService method
|
544 | destroyService(next) {
|
545 |
|
546 | }
|
547 |
|
548 | // Wrap broker.call method
|
549 | call(next) {
|
550 |
|
551 | }
|
552 |
|
553 | // Wrap broker.mcall method
|
554 | mcall(next) {
|
555 |
|
556 | }
|
557 |
|
558 | // Wrap broker.emit method
|
559 | emit(next) {
|
560 |
|
561 | },
|
562 |
|
563 | // Wrap broker.broadcast method
|
564 | broadcast(next) {
|
565 |
|
566 | },
|
567 |
|
568 | // Wrap broker.broadcastLocal method
|
569 | broadcastLocal(next) {
|
570 |
|
571 | },
|
572 |
|
573 | // After a new local service created (sync)
|
574 | serviceCreated(service) {
|
575 |
|
576 | },
|
577 |
|
578 | // Before a local service started (async)
|
579 | serviceStarting(service) {
|
580 |
|
581 | },
|
582 |
|
583 | // After a local service started (async)
|
584 | serviceStarted(service) {
|
585 |
|
586 | },
|
587 |
|
588 | // Before a local service stopping (async)
|
589 | serviceStopping(service) {
|
590 |
|
591 | },
|
592 |
|
593 | // After a local service stopped (async)
|
594 | serviceStopped(service) {
|
595 |
|
596 | },
|
597 |
|
598 | // After broker is created (async)
|
599 | created(broker) {
|
600 |
|
601 | },
|
602 |
|
603 | // Before broker starting (async)
|
604 | starting(broker) {
|
605 |
|
606 | },
|
607 |
|
608 | // After broker started (async)
|
609 | started(broker) {
|
610 |
|
611 | },
|
612 |
|
613 | // Before broker stopping (async)
|
614 | stopping(broker) {
|
615 |
|
616 | },
|
617 |
|
618 | // After broker stopped (async)
|
619 | stopped(broker) {
|
620 |
|
621 | }
|
622 | }
|
623 | ```
|
624 |
|
625 | **Use it in broker options**
|
626 | ```js
|
627 | const broker = new ServiceBroker({
|
628 | middlewares: [
|
629 | MyCustomMiddleware
|
630 | ]
|
631 | });
|
632 | ```
|
633 |
|
634 | ### Wrapping handlers
|
635 | Some hooks are wrappers. It means you need to wrap the original handler and return a new Function.
|
636 | Wrap hooks where the first parameter is `next`.
|
637 |
|
638 | **Wrap local action handler**
|
639 | ```js
|
640 | const MyDoSomethingMiddleware = {
|
641 | localAction(next, action) {
|
642 | if (action.myFunc) {
|
643 | // Wrap the handler
|
644 |
|
645 | return function(ctx) {
|
646 | doSomethingBeforeHandler(ctx);
|
647 |
|
648 | return handler(ctx)
|
649 | .then(res => {
|
650 | doSomethingAfterHandler(res);
|
651 | // Return the original result
|
652 | return res;
|
653 | })
|
654 | .catch(err => {
|
655 | doSomethingAfterHandlerIfFailed(err);
|
656 |
|
657 | // Throw further the error
|
658 | throw err;
|
659 | });
|
660 | }
|
661 | }
|
662 |
|
663 | // If the feature is disabled we don't wrap it, return the original handler
|
664 | // So it won't cut down the performance for actions where the feature is disabled.
|
665 | return handler;
|
666 | }
|
667 | };
|
668 | ```
|
669 |
|
670 | ### Decorate broker (to extend functions)
|
671 | Other hooks are to help you to decorate new features in ServiceBroker & services.
|
672 |
|
673 | **Decorate broker with a new `allCall` method**
|
674 | ```js
|
675 | const broker = new ServiceBroker({
|
676 | middlewares: [
|
677 | {
|
678 | // After broker is created
|
679 | created(broker) {
|
680 | // Call action on all available nodes
|
681 | broker.allCall = function(action, params, opts = {}) {
|
682 | const nodeIDs = this.registry.getNodeList({ onlyAvailable: true })
|
683 | .map(node => node.id);
|
684 |
|
685 | // Make direct call to the given Node ID
|
686 | return Promise.all(nodeIDs.map(nodeID => broker.call(action, params, Object.assign({ nodeID }, opts))));
|
687 | }
|
688 | }
|
689 | }
|
690 | ]
|
691 | });
|
692 |
|
693 | await broker.start();
|
694 |
|
695 | // Call `$node.health` on every nodes & collect results
|
696 | const res = await broker.allCall("$node.health");
|
697 | ```
|
698 |
|
699 | **Decorate services with a new method**
|
700 | ```js
|
701 | const broker = new ServiceBroker({
|
702 | middlewares: [
|
703 | {
|
704 | // After a new local service created
|
705 | serviceCreated(service) {
|
706 | // Call action on all available nodes
|
707 | service.customFunc = function() {
|
708 | // Do something
|
709 | }.bind(service);
|
710 | }
|
711 | }
|
712 | ]
|
713 | });
|
714 | ```
|
715 |
|
716 | In service schema:
|
717 |
|
718 | ```js
|
719 | module.export = {
|
720 | name: "users",
|
721 | actions: {
|
722 | find(ctx) {
|
723 | // Call the new custom function
|
724 | this.customFunc();
|
725 | }
|
726 | }
|
727 | };
|
728 | ```
|
729 |
|
730 | > The mixins can do similar things, so we prefer mixins to this decorating.
|
731 |
|
732 | ## Many internal features are exposed to internal middlewares
|
733 | Due to the new advanced middlewares, we could bring out many integrated features to middlewares. They are available under `require("moleculer").Middlewares` property, but they load automatically.
|
734 |
|
735 | **New internal middlewares:**
|
736 | - Action hook handling
|
737 | - Validator
|
738 | - Bulkhead
|
739 | - Cacher
|
740 | - Context tracker
|
741 | - Circuit Breaker
|
742 | - Timeout
|
743 | - Retry
|
744 | - Fallback
|
745 | - Error handling
|
746 | - Metrics
|
747 |
|
748 | > Turn off the automatic loading with `internalMiddlewares: false` broker option. In this case you have to add them to `middlewares: []` broker option.
|
749 |
|
750 | > The `broker.use` method is deprecated. Use `middlewares: []` in the broker options instead.
|
751 |
|
752 | ## Action hooks
|
753 | Define action hooks to wrap certain actions coming from mixins.
|
754 | There are `before`, `after` and `error` hooks. Assign it to a specified action or all actions (`*`) in service.
|
755 | The hook can be a `Function` or a `String`. The latter must be a local service method name.
|
756 |
|
757 | **Before hooks**
|
758 |
|
759 | ```js
|
760 | const DbService = require("moleculer-db");
|
761 |
|
762 | module.exports = {
|
763 | name: "posts",
|
764 | mixins: [DbService]
|
765 | hooks: {
|
766 | before: {
|
767 | // Define a global hook for all actions
|
768 | // The hook will call the `resolveLoggedUser` method.
|
769 | "*": "resolveLoggedUser",
|
770 |
|
771 | // Define multiple hooks
|
772 | remove: [
|
773 | function isAuthenticated(ctx) {
|
774 | if (!ctx.user)
|
775 | throw new Error("Forbidden");
|
776 | },
|
777 | function isOwner(ctx) {
|
778 | if (!this.checkOwner(ctx.params.id, ctx.user.id))
|
779 | throw new Error("Only owner can remove it.");
|
780 | }
|
781 | ]
|
782 | }
|
783 | },
|
784 |
|
785 | methods: {
|
786 | async resolveLoggedUser(ctx) {
|
787 | if (ctx.meta.user)
|
788 | ctx.user = await ctx.call("users.get", { id: ctx.meta.user.id });
|
789 | }
|
790 | }
|
791 | }
|
792 | ```
|
793 |
|
794 | **After & Error hooks**
|
795 |
|
796 | ```js
|
797 | const DbService = require("moleculer-db");
|
798 |
|
799 | module.exports = {
|
800 | name: "users",
|
801 | mixins: [DbService]
|
802 | hooks: {
|
803 | after: {
|
804 | // Define a global hook for all actions to remove sensitive data
|
805 | "*": function(ctx, res) {
|
806 | // Remove password
|
807 | delete res.password;
|
808 |
|
809 | // Please note, must return result (either the original or a new)
|
810 | return res;
|
811 | },
|
812 | get: [
|
813 | // Add a new virtual field to the entity
|
814 | async function (ctx, res) {
|
815 | res.friends = await ctx.call("friends.count", { query: { follower: res._id }});
|
816 |
|
817 | return res;
|
818 | },
|
819 | // Populate the `referrer` field
|
820 | async function (ctx, res) {
|
821 | if (res.referrer)
|
822 | res.referrer = await ctx.call("users.get", { id: res._id });
|
823 |
|
824 | return res;
|
825 | }
|
826 | ]
|
827 | },
|
828 | error: {
|
829 | // Global error handler
|
830 | "*": function(ctx, err) {
|
831 | this.logger.error(`Error occurred when '${ctx.action.name}' action was called`, err);
|
832 |
|
833 | // Throw further the error
|
834 | throw err;
|
835 | }
|
836 | }
|
837 | }
|
838 | };
|
839 | ```
|
840 |
|
841 | The recommended use case is to create mixins filling up the service with methods and in `hooks` set method names.
|
842 |
|
843 | **Mixin**
|
844 | ```js
|
845 | module.exports = {
|
846 | methods: {
|
847 | checkIsAuthenticated(ctx) {
|
848 | if (!ctx.meta.user)
|
849 | throw new Error("Unauthenticated");
|
850 | },
|
851 | checkUserRole(ctx) {
|
852 | if (ctx.action.role && ctx.meta.user.role != ctx.action.role)
|
853 | throw new Error("Forbidden");
|
854 | },
|
855 | checkOwner(ctx) {
|
856 | // Check the owner of entity
|
857 | }
|
858 | }
|
859 | }
|
860 | ```
|
861 |
|
862 | **Use mixin methods in hooks**
|
863 | ```js
|
864 | const MyAuthMixin = require("./my.mixin");
|
865 |
|
866 | module.exports = {
|
867 | name: "posts",
|
868 | mixins: [MyAuthMixin]
|
869 | hooks: {
|
870 | before: {
|
871 | "*": ["checkIsAuthenticated"],
|
872 | create: ["checkUserRole"],
|
873 | update: ["checkUserRole", "checkOwner"],
|
874 | remove: ["checkUserRole", "checkOwner"]
|
875 | }
|
876 | },
|
877 |
|
878 | actions: {
|
879 | find: {
|
880 | // No required role
|
881 | handler(ctx) {}
|
882 | },
|
883 | create: {
|
884 | role: "admin",
|
885 | handler(ctx) {}
|
886 | },
|
887 | update: {
|
888 | role: "user",
|
889 | handler(ctx) {}
|
890 | }
|
891 | }
|
892 | };
|
893 | ```
|
894 |
|
895 | ## New Bulkhead fault-tolerance feature
|
896 | Bulkhead feature is an internal middleware in Moleculer. Use it to control the concurrent request handling of actions.
|
897 |
|
898 | **Global settings in the broker options.** _Applied to all registered local actions._
|
899 | ```js
|
900 | const broker = new ServiceBroker({
|
901 | bulkhead: {
|
902 | enabled: true,
|
903 | concurrency: 3,
|
904 | maxQueueSize: 10,
|
905 | }
|
906 | });
|
907 | ```
|
908 |
|
909 | The `concurrency` value restricts the concurrent request executions. If `maxQueueSize` is bigger than 0, broker queues additional requests, if all slots are taken. If queue size reaches `maxQueueSize` limit or it is 0, broker will throw `QueueIsFull` error for every addition request.
|
910 |
|
911 | These global options can be overriden in action definition, as well.
|
912 |
|
913 | ```js
|
914 | module.export = {
|
915 | name: "users",
|
916 | actions: {
|
917 | find: {
|
918 | bulkhead: {
|
919 | enabled: false
|
920 | },
|
921 | handler(ctx) {}
|
922 | },
|
923 | create: {
|
924 | bulkhead: {
|
925 | // Increment the concurrency value
|
926 | // for this action
|
927 | concurrency: 10
|
928 | },
|
929 | handler(ctx) {}
|
930 | }
|
931 | }
|
932 | };
|
933 | ```
|
934 |
|
935 | ## Fallback in action definition
|
936 | Due to the exposed Fallback middleware, fallback response can be set in the action definition, too.
|
937 |
|
938 | > Please note, this fallback response will only be used if the error occurs within action handler. If the request is called from a remote node and the request is timed out on the remote node, the fallback response is not be used. In this case, use the `fallbackResponse` in calling option.
|
939 |
|
940 | **Fallback as function**
|
941 | ```js
|
942 | module.exports = {
|
943 | name: "recommends",
|
944 | actions: {
|
945 | add: {
|
946 | fallback: (ctx, err) => "Some cached result",
|
947 | //fallback: "fakeResult",
|
948 | handler(ctx) {
|
949 | // Do something
|
950 | }
|
951 | }
|
952 | }
|
953 | };
|
954 | ```
|
955 |
|
956 | **Fallback as method name string**
|
957 | ```js
|
958 | module.exports = {
|
959 | name: "recommends",
|
960 | actions: {
|
961 | add: {
|
962 | // Call the 'getCachedResult' method when error occurred
|
963 | fallback: "getCachedResult",
|
964 | handler(ctx) {
|
965 | // Do something
|
966 | }
|
967 | }
|
968 | },
|
969 |
|
970 | methods: {
|
971 | getCachedResult(ctx, err) {
|
972 | return "Some cached result";
|
973 | }
|
974 | }
|
975 | };
|
976 | ```
|
977 |
|
978 | ## Action visibility
|
979 | The action has a new `visibility` property to control the visibility & callability of service actions.
|
980 |
|
981 | **Available values:**
|
982 | - `published` or `null`: public action. It can be called locally, remotely and can be published via API Gateway
|
983 | - `public`: public action, can be called locally & remotely but not published via API GW
|
984 | - `protected`: can be called only locally (from local services)
|
985 | - `private`: can be called only internally (via `this.actions.xy()` within service)
|
986 |
|
987 | ```js
|
988 | module.exports = {
|
989 | name: "posts",
|
990 | actions: {
|
991 | // It's published by default
|
992 | find(ctx) {},
|
993 | clean: {
|
994 | // Callable only via `this.actions.clean`
|
995 | visibility: "private",
|
996 | handler(ctx) {}
|
997 | }
|
998 | },
|
999 | methods: {
|
1000 | cleanEntities() {
|
1001 | // Call the action directly
|
1002 | return this.actions.clean();
|
1003 | }
|
1004 | }
|
1005 | }
|
1006 | ```
|
1007 |
|
1008 | > The default value is `null` (means `published`) due to backward compatibility.
|
1009 |
|
1010 | ## New Thrift serializer
|
1011 | There is a new built-in [Thrift](http://thrift.apache.org/) serializer.
|
1012 |
|
1013 | ```js
|
1014 | const broker = new ServiceBroker({
|
1015 | serializer: "Thrift"
|
1016 | });
|
1017 | ```
|
1018 | > To use this serializer install the `thrift` module with `npm install thrift --save` command.
|
1019 |
|
1020 | ## Enhanced log level configuration
|
1021 | A new module-based log level configuration was added. The log level can be set for every Moleculer module. Use of wildcard is allowed.
|
1022 |
|
1023 | ```js
|
1024 | const broker = new ServiceBroker({
|
1025 | logger: console,
|
1026 | logLevel: {
|
1027 | "MY.**": false, // Disable logs
|
1028 | "TRANS": "warn", // Only 'warn ' and 'error' log entries
|
1029 | "*.GREETER": "debug", // All log entries
|
1030 | "**": "debug", // All other modules use this level
|
1031 | }
|
1032 | });
|
1033 | ```
|
1034 |
|
1035 | **Please note, it works only with default console logger. In case of external loggers (Pino, Windows, Bunyan, ...etc), these log levels must be applied.**
|
1036 |
|
1037 | > These settings are evaluated from top to bottom, so the `**` level must be the last property.
|
1038 |
|
1039 | > Internal modules: `BROKER`, `TRANS`, `TX` as transporter, `CACHER`, `REGISTRY`.
|
1040 | >
|
1041 | > For services, the name comes from the service name. E.g. `POSTS`.
|
1042 | > A version is used as a prefix. E.g. `V2.POSTS`
|
1043 |
|
1044 | The old global log level settings works, as well.
|
1045 | ```js
|
1046 | const broker = new ServiceBroker({
|
1047 | logger: console,
|
1048 | logLevel: "warn"
|
1049 | });
|
1050 | ```
|
1051 |
|
1052 | ## New `short` log formatter
|
1053 | A new `short` log formatter was also added. It is similar to the default, but doesn't print the date and `nodeID`.
|
1054 |
|
1055 | ```js
|
1056 | const broker = new ServiceBroker({
|
1057 | logFormatter: "short"
|
1058 | });
|
1059 | ```
|
1060 |
|
1061 | **Output**
|
1062 | ```
|
1063 | [19:42:49.055Z] INFO MATH: Service started.
|
1064 | ```
|
1065 |
|
1066 | ## Load services also with glob patterns
|
1067 | Moleculer Runner loads services also from glob patterns. It is useful when loading all services except certain ones.
|
1068 |
|
1069 | ```bash
|
1070 | $ moleculer-runner services !services/others/**/*.service.js services/others/mandatory/main.service.js
|
1071 | ```
|
1072 |
|
1073 | **Explanations:**
|
1074 | - `services` - legacy mode. Load all services from the `services` folder with `**/*.service.js` file mask
|
1075 | - `!services/others/**/*.service.js` - skip all services in the `services/others` folder and sub-folders.
|
1076 | - `services/others/mandatory/main.service.js` - load the exact service
|
1077 |
|
1078 | > Glob patterns work in the `SERVICES` enviroment variables, as well.
|
1079 |
|
1080 | ## MemoryCacher cloning
|
1081 | There is a new `clone` property in the `MemoryCacher` options. If it's `true`, the cacher clones the cached data before returning.
|
1082 | If received value is modified, enable this option. Note: it cuts down the performance.
|
1083 |
|
1084 | **Enable cloning**
|
1085 | ```js
|
1086 | const broker = new ServiceBroker({
|
1087 | cacher: {
|
1088 | type: "Memory",
|
1089 | options: {
|
1090 | clone: true
|
1091 | }
|
1092 | }
|
1093 | });
|
1094 | ```
|
1095 |
|
1096 | This feature uses the lodash `_.cloneDeep` method. To change cloning method set a `Function` to the `clone` option instead of a `Boolean`.
|
1097 |
|
1098 | **Custom clone function with JSON parse & stringify:**
|
1099 | ```js
|
1100 | const broker = new ServiceBroker({
|
1101 | cacher: {
|
1102 | type: "Memory",
|
1103 | options: {
|
1104 | clone: data => JSON.parse(JSON.stringify(data))
|
1105 | }
|
1106 | }
|
1107 | });
|
1108 | ```
|
1109 |
|
1110 | # Changes
|
1111 | - service instances has a new property named `fullName` containing service version & service name.
|
1112 | - the `Action` has a `rawName` property containing action name without service name.
|
1113 | - new `$node.options` internal action to get the current broker options.
|
1114 | - `Context.create` & `new Context` signature changed.
|
1115 | - removed Context metrics methods. All metrics feature moved to the `Metrics` middleware.
|
1116 | - `ctx.timeout` moved to `ctx.options.timeout`.
|
1117 | - removed `ctx.callerNodeID`.
|
1118 | - `ctx.endpoint` is a new property pointing to target `Endpoint`. For example you can check with `ctx.endpoint.local` flag whether the request is remote or local.
|
1119 | - lazily generated `ctx.id`, i.e. only generated at access. `ctx.generateID()` was removed.
|
1120 | - renamed service lifecycle methods in service instances (not in service schema!)
|
1121 | - extended `transit.stat.packets` with byte-based statistics.
|
1122 | - `utils.deprecate` method was created for deprecation.
|
1123 | - Transporter supports `mqtt+ssl://`, `rediss://` & `amqps://` protocols in connection URIs.
|
1124 | - fixed circular objects handling in service schema (e.g.: Joi validator problem)
|
1125 |
|
1126 | # Deprecations
|
1127 |
|
1128 | - `broker.use()` has been deprecated. Use `middlewares: [...]` in broker options instead.
|
1129 |
|
1130 | --------------------------------------------------
|
1131 | <a name="0.12.8"></a>
|
1132 | # [0.12.8](https://github.com/moleculerjs/moleculer/compare/v0.12.6...v0.12.8) (2018-06-14)
|
1133 |
|
1134 | # Changes
|
1135 | - fix action disabling with mixins [#298](https://github.com/moleculerjs/moleculer/issues/298)
|
1136 | - Fix metrics options and add findNextActionEndpoint to index.d.ts
|
1137 | - update dependencies
|
1138 | - set `maxReconnectAttempts` to `-1` in NATS client to try reconnecting continuously
|
1139 |
|
1140 | --------------------------------------------------
|
1141 | <a name="0.12.6"></a>
|
1142 | # [0.12.6](https://github.com/moleculerjs/moleculer/compare/v0.12.5...v0.12.6) (2018-06-07)
|
1143 |
|
1144 | # Changes
|
1145 | - update dependencies
|
1146 | - The `breakLength` is changed to `Infinity` (single-line printing) for better log processing when logger prints objects and arrays.
|
1147 | - adds ability to customise console object/array printing [#285](https://github.com/moleculerjs/moleculer/issues/285)
|
1148 | ```js
|
1149 | const util = require("util");
|
1150 |
|
1151 | const broker = new ServiceBroker({
|
1152 | logger: true,
|
1153 | logObjectPrinter: o => util.inspect(o, { depth: 4, colors: false, breakLength: 50 }) // `breakLength: 50` activates multi-line object
|
1154 | });
|
1155 | ```
|
1156 |
|
1157 | --------------------------------------------------
|
1158 | <a name="0.12.5"></a>
|
1159 | # [0.12.5](https://github.com/moleculerjs/moleculer/compare/v0.12.4...v0.12.5) (2018-05-21)
|
1160 |
|
1161 | # Changes
|
1162 | - fix AMQP logs. [#270](https://github.com/moleculerjs/moleculer/issues/270)
|
1163 | - fix transferred retryable error handling
|
1164 | - `broker.createService` supports ES6 classes
|
1165 | - fix broken promise chain if trackContext is enabled
|
1166 |
|
1167 | --------------------------------------------------
|
1168 | <a name="0.12.4"></a>
|
1169 | # [0.12.4](https://github.com/moleculerjs/moleculer/compare/v0.12.3...v0.12.4) (2018-05-10)
|
1170 |
|
1171 | # New
|
1172 |
|
1173 | ## Graceful shutdown
|
1174 | Thanks for [@rmccallum81](https://github.com/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.
|
1175 |
|
1176 | ```js
|
1177 | const broker = new ServiceBroker({
|
1178 | trackContext: true,
|
1179 | gracefulStopTimeout: 5 * 1000 // waiting max 5 sec
|
1180 | });
|
1181 | ```
|
1182 |
|
1183 | _This timeout can be overwrite in service settings with `$gracefulStopTimeout` property._
|
1184 |
|
1185 | # Changes
|
1186 | - fix service registry update after reconnecting. [#262](https://github.com/moleculerjs/moleculer/issues/262)
|
1187 | - update index.d.ts
|
1188 | - update dependencies
|
1189 | - fix distributed timeout handling
|
1190 |
|
1191 | --------------------------------------------------
|
1192 | <a name="0.12.3"></a>
|
1193 | # [0.12.3](https://github.com/moleculerjs/moleculer/compare/v0.12.2...v0.12.3) (2018-04-19)
|
1194 |
|
1195 | # Changes
|
1196 | - fix empty service mixins issue (`mixins: []`).
|
1197 | - update index.d.ts
|
1198 |
|
1199 | --------------------------------------------------
|
1200 | <a name="0.12.2"></a>
|
1201 | # [0.12.2](https://github.com/moleculerjs/moleculer/compare/v0.12.0...v0.12.2) (2018-04-11)
|
1202 |
|
1203 | # New
|
1204 |
|
1205 | ## Latency strategy
|
1206 | 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.
|
1207 |
|
1208 | **Usage**
|
1209 | ```js
|
1210 | let broker = new ServiceBroker({
|
1211 | registry: {
|
1212 | strategy: "Latency"
|
1213 | }
|
1214 | });
|
1215 | ```
|
1216 |
|
1217 | **Strategy options**
|
1218 |
|
1219 | | Name | Type | Default | Description |
|
1220 | | ---- | ---- | --------| ----------- |
|
1221 | | `sampleCount` | `Number` | `5` | the number of samples. If you have a lot of hosts/nodes, it's recommended to *increase* the value. |
|
1222 | | `lowLatency` | `Number` | `10` | the low latency (ms). The node which has lower latency than this value is selected immediately. |
|
1223 | | `collectCount` | `Number` | `5` | the number of measured latency per host to keep in order to calculate the average latency. |
|
1224 | | `pingInterval` | `Number` | `10` | ping interval (s). If you have a lot of host/nodes, it's recommended to *increase* the value. |
|
1225 |
|
1226 | **Usage with custom options**
|
1227 | ```js
|
1228 | let broker = new ServiceBroker({
|
1229 | registry: {
|
1230 | strategy: "Latency",
|
1231 | strategyOptions: {
|
1232 | sampleCount: 15,
|
1233 | lowLatency: 20,
|
1234 | collectCount: 10,
|
1235 | pingInterval: 15
|
1236 | }
|
1237 | }
|
1238 | });
|
1239 | ```
|
1240 |
|
1241 | ## Filemask for Moleculer Runner
|
1242 | There is a new Moleculer Runner option `--mask` to define filemask when load all services from folders.
|
1243 |
|
1244 | **Example**
|
1245 | ```
|
1246 | $ moleculer-runner.js -r --mask **/user*.service.js examples
|
1247 | ```
|
1248 |
|
1249 | **Example to load Typescript services**
|
1250 | ```
|
1251 | $ node -r ts-node/register node_modules/moleculer/bin/moleculer-runner --hot --repl --mask **/*.service.ts services
|
1252 | ```
|
1253 |
|
1254 | # Changes
|
1255 | - fix `d.ts` issues
|
1256 | - fix event `group` handling in mixins ([#217](https://github.com/moleculerjs/moleculer/issues/217))
|
1257 | - move `mergeSchemas` from `utils` to `Service` static method. It can be overwritten in a custom ServiceFactory
|
1258 | - improve `d.ts`
|
1259 | - fix `prefix` option in Redis Cacher ([223](https://github.com/moleculerjs/moleculer/issues/223))
|
1260 | - remove `nanomatch` dependency, use own implementation
|
1261 | - fix ContextFactory issue ([235](https://github.com/moleculerjs/moleculer/issues/235))
|
1262 | - expose utility functions as `require("moleculer").Utils`
|
1263 | - overwritable `mergeSchemas` static method in `Service` class.
|
1264 | - Moleculer Runner precedence order is changed. The `SERVICES` & `SERVICEDIR` env vars overwrites the paths in CLI arguments.
|
1265 |
|
1266 | --------------------------------------------------
|
1267 | <a name="0.12.0"></a>
|
1268 | # [0.12.0](https://github.com/moleculerjs/moleculer/compare/v0.11.10...v0.12.0) (2018-03-03)
|
1269 |
|
1270 | This version contains the most changes in the history of Moleculer! More than 200 commits with 17k additions and a lot of new features.
|
1271 |
|
1272 | # Breaking changes
|
1273 |
|
1274 | ## Github organization is renamed
|
1275 | The Github organization name (Ice Services) has been renamed to MoleculerJS. Please update your bookmarks.
|
1276 |
|
1277 | * Github organization: https://github.com/moleculerjs
|
1278 | * Website: https://moleculer.services or http://moleculerjs.com/
|
1279 | * Gitter chat: https://gitter.im/moleculerjs/moleculer
|
1280 |
|
1281 | ## Mixin merging logic is changed
|
1282 | To support [#188](https://github.com/moleculerjs/moleculer/issues/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`.
|
1283 |
|
1284 | **Add extra action properties but `handler` is untouched**
|
1285 | ```js
|
1286 | // mixin.service.js
|
1287 | module.exports = {
|
1288 | actions: {
|
1289 | create(ctx) {
|
1290 | // Action handler without `params`
|
1291 | }
|
1292 | }
|
1293 | };
|
1294 | ```
|
1295 |
|
1296 | ```js
|
1297 | // my.service.js
|
1298 | module.exports = {
|
1299 | mixins: [MixinService]
|
1300 | actions: {
|
1301 | create: {
|
1302 | // Add only `params` property to the `create` action
|
1303 | // The handler is merged from mixin
|
1304 | params: {
|
1305 | name: "string"
|
1306 | }
|
1307 | }
|
1308 | }
|
1309 |
|
1310 | };
|
1311 | ```
|
1312 |
|
1313 | ## Wrapper removed from transporter options
|
1314 | 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`).
|
1315 |
|
1316 | **Before**
|
1317 | ```js
|
1318 | // NATS transporter
|
1319 | const broker = new ServiceBroker({
|
1320 | transporter: {
|
1321 | type: "NATS",
|
1322 | options: {
|
1323 | nats: {
|
1324 | user: "admin",
|
1325 | pass: "1234"
|
1326 | }
|
1327 | }
|
1328 | }
|
1329 | });
|
1330 |
|
1331 | // Redis transporter
|
1332 | const broker = new ServiceBroker({
|
1333 | transporter: {
|
1334 | type: "Redis",
|
1335 | options: {
|
1336 | redis: {
|
1337 | port: 6379,
|
1338 | db: 0
|
1339 | }
|
1340 | }
|
1341 | }
|
1342 | });
|
1343 |
|
1344 | // MQTT transporter
|
1345 | const broker = new ServiceBroker({
|
1346 | transporter: {
|
1347 | type: "MQTT",
|
1348 | options: {
|
1349 | mqtt: {
|
1350 | user: "admin",
|
1351 | pass: "1234"
|
1352 | }
|
1353 | }
|
1354 | }
|
1355 | });
|
1356 |
|
1357 | // AMQP transporter
|
1358 | const broker = new ServiceBroker({
|
1359 | transporter: {
|
1360 | type: "AMQP",
|
1361 | options: {
|
1362 | amqp: {
|
1363 | prefetch: 1
|
1364 | }
|
1365 | }
|
1366 | }
|
1367 | });
|
1368 | ```
|
1369 |
|
1370 | **After**
|
1371 | ```js
|
1372 | // NATS transporter
|
1373 | const broker = new ServiceBroker({
|
1374 | transporter: {
|
1375 | type: "NATS",
|
1376 | options: {
|
1377 | user: "admin",
|
1378 | pass: "1234"
|
1379 | }
|
1380 | }
|
1381 | });
|
1382 |
|
1383 | // Redis transporter
|
1384 | const broker = new ServiceBroker({
|
1385 | transporter: {
|
1386 | type: "Redis",
|
1387 | options: {
|
1388 | port: 6379,
|
1389 | db: 0
|
1390 | }
|
1391 | }
|
1392 | });
|
1393 |
|
1394 | // MQTT transporter
|
1395 | const broker = new ServiceBroker({
|
1396 | transporter: {
|
1397 | type: "MQTT",
|
1398 | options: {
|
1399 | user: "admin",
|
1400 | pass: "1234"
|
1401 | }
|
1402 | }
|
1403 | });
|
1404 |
|
1405 | // AMQP transporter
|
1406 | const broker = new ServiceBroker({
|
1407 | transporter: {
|
1408 | type: "AMQP",
|
1409 | options: {
|
1410 | prefetch: 1
|
1411 | }
|
1412 | }
|
1413 | });
|
1414 | ```
|
1415 |
|
1416 | ## Default `nodeID` generator changed
|
1417 | 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.
|
1418 |
|
1419 | ## Protocol changed
|
1420 | The transport protocol is changed. The new version is `3`. [Check the changes.](https://github.com/moleculerjs/moleculer/blob/aa56e0072f4726dcd3a72ef164c3e13ad377bfc2/docs/PROTOCOL.md)
|
1421 |
|
1422 | **It means, the >=0.12.x versions can't communicate with old <=0.11 versions.**
|
1423 |
|
1424 | **Changes:**
|
1425 | - the `RESPONSE` packet has a new field `meta`.
|
1426 | - the `EVENT` packet has a new field `broadcast`.
|
1427 | - the `port` field is removed from `INFO` packet.
|
1428 | - the `INFO` packet has a new field `hostname`.
|
1429 |
|
1430 | # New features
|
1431 |
|
1432 | ## New ServiceBroker options
|
1433 | There are some new properties in ServiceBroker option: `middlewares`, `created`, `started`, `stopped`.
|
1434 |
|
1435 | They can be useful when you use broker config file and start your project with Moleculer Runner.
|
1436 |
|
1437 | ```js
|
1438 | // moleculer.config.js
|
1439 | module.exports = {
|
1440 | logger: true,
|
1441 |
|
1442 | // Add middlewares
|
1443 | middlewares: [myMiddleware()],
|
1444 |
|
1445 | // Fired when the broker created
|
1446 | created(broker) {
|
1447 | },
|
1448 |
|
1449 | // Fired when the broker started
|
1450 | started(broker) {
|
1451 | // You can return Promise
|
1452 | return broker.Promise.resolve();
|
1453 | },
|
1454 |
|
1455 | // Fired when the broker stopped
|
1456 | stopped(broker) {
|
1457 | // You can return Promise
|
1458 | return broker.Promise.resolve();
|
1459 | }
|
1460 | };
|
1461 | ```
|
1462 |
|
1463 | ## Broadcast events with group filter
|
1464 |
|
1465 | The `broker.broadcast` function has a third `groups` argument similar to `broker.emit`.
|
1466 | ```js
|
1467 | // Send to all "mail" service instances
|
1468 | broker.broadcast("user.created", { user }, "mail");
|
1469 |
|
1470 | // Send to all "user" & "purchase" service instances.
|
1471 | broker.broadcast("user.created", { user }, ["user", "purchase"]);
|
1472 | ```
|
1473 |
|
1474 | ## CPU usage-based strategy
|
1475 | There is a new `CpuUsageStrategy` strategy. It selects a node which has the lowest CPU usage.
|
1476 | 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.
|
1477 |
|
1478 | There are 2 options for the strategy:
|
1479 | - `sampleCount`: the number of samples. Default: `3`
|
1480 | - `lowCpuUsage`: the low CPU usage percent. The node which has lower CPU usage than this value is selected immediately. Default: `10`
|
1481 |
|
1482 | **Usage:**
|
1483 | ```js
|
1484 | const broker = new ServiceBroker({
|
1485 | registry: {
|
1486 | strategy: "CpuUsage"
|
1487 | }
|
1488 | });
|
1489 | ```
|
1490 |
|
1491 | **Usage with custom options**
|
1492 | ```js
|
1493 | const broker = new ServiceBroker({
|
1494 | registry: {
|
1495 | strategy: "CpuUsage",
|
1496 | strategyOptions: {
|
1497 | sampleCount: 3,
|
1498 | lowCpuUsage: 10
|
1499 | }
|
1500 | }
|
1501 | });
|
1502 | ```
|
1503 |
|
1504 | ## Starting logic is changed
|
1505 | The broker & services starting logic has been changed.
|
1506 |
|
1507 | **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.
|
1508 |
|
1509 | **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.
|
1510 | >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.
|
1511 |
|
1512 | ## Metadata is sent back to requester
|
1513 | At requests, `ctx.meta` is sent back to the caller service. You can use it to send extra meta information back to the caller.
|
1514 | E.g.: send response headers back to API gateway or set resolved logged in user to metadata.
|
1515 |
|
1516 | **Export & download a file with API gateway:**
|
1517 | ```js
|
1518 | // Export data
|
1519 | export(ctx) {
|
1520 | const rows = this.adapter.find({});
|
1521 |
|
1522 | // Set response headers to download it as a file
|
1523 | ctx.meta.headers = {
|
1524 | "Content-Type": "application/json; charset=utf-8",
|
1525 | "Content-Disposition": 'attachment; filename=\"book.json\"'
|
1526 | }
|
1527 |
|
1528 | return rows;
|
1529 | }
|
1530 | ```
|
1531 |
|
1532 | **Authenticate:**
|
1533 | ```js
|
1534 | auth(ctx) {
|
1535 | let user = this.getUserByJWT(ctx.params.token);
|
1536 | if (ctx.meta.user) {
|
1537 | ctx.meta.user = user;
|
1538 |
|
1539 | return true;
|
1540 | }
|
1541 |
|
1542 | throw new Forbidden();
|
1543 | }
|
1544 | ```
|
1545 |
|
1546 | ## Better ES6 class support
|
1547 | If you like better ES6 classes than Moleculer service schema, you can write your services in ES6 classes.
|
1548 |
|
1549 | There are two ways to do it:
|
1550 |
|
1551 | 1. **Native ES6 classes with schema parsing**
|
1552 |
|
1553 | 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.
|
1554 | ```js
|
1555 | const Service = require("moleculer").Service;
|
1556 |
|
1557 | class GreeterService extends Service {
|
1558 |
|
1559 | constructor(broker) {
|
1560 | super(broker);
|
1561 |
|
1562 | this.parseServiceSchema({
|
1563 | name: "greeter",
|
1564 | version: "v2",
|
1565 | meta: {
|
1566 | scalable: true
|
1567 | },
|
1568 | dependencies: [
|
1569 | "auth",
|
1570 | "users"
|
1571 | ],
|
1572 |
|
1573 | settings: {
|
1574 | upperCase: true
|
1575 | },
|
1576 | actions: {
|
1577 | hello: this.hello,
|
1578 | welcome: {
|
1579 | cache: {
|
1580 | keys: ["name"]
|
1581 | },
|
1582 | params: {
|
1583 | name: "string"
|
1584 | },
|
1585 | handler: this.welcome
|
1586 | }
|
1587 | },
|
1588 | events: {
|
1589 | "user.created": this.userCreated
|
1590 | },
|
1591 | created: this.serviceCreated,
|
1592 | started: this.serviceStarted,
|
1593 | stopped: this.serviceStopped,
|
1594 | });
|
1595 | }
|
1596 |
|
1597 | // Action handler
|
1598 | hello() {
|
1599 | return "Hello Moleculer";
|
1600 | }
|
1601 |
|
1602 | // Action handler
|
1603 | welcome(ctx) {
|
1604 | return this.sayWelcome(ctx.params.name);
|
1605 | }
|
1606 |
|
1607 | // Private method
|
1608 | sayWelcome(name) {
|
1609 | this.logger.info("Say hello to", name);
|
1610 | return `Welcome, ${this.settings.upperCase ? name.toUpperCase() : name}`;
|
1611 | }
|
1612 |
|
1613 | // Event handler
|
1614 | userCreated(user) {
|
1615 | this.broker.call("mail.send", { user });
|
1616 | }
|
1617 |
|
1618 | serviceCreated() {
|
1619 | this.logger.info("ES6 Service created.");
|
1620 | }
|
1621 |
|
1622 | serviceStarted() {
|
1623 | this.logger.info("ES6 Service started.");
|
1624 | }
|
1625 |
|
1626 | serviceStopped() {
|
1627 | this.logger.info("ES6 Service stopped.");
|
1628 | }
|
1629 | }
|
1630 |
|
1631 | module.exports = GreeterService;
|
1632 | ```
|
1633 |
|
1634 | 2. **Use decorators**
|
1635 |
|
1636 | Thanks for [@ColonelBundy](https://github.com/ColonelBundy), you can use ES7/TS decorators as well: [moleculer-decorators](https://github.com/ColonelBundy/moleculer-decorators)
|
1637 |
|
1638 | >Please note, you need to use Typescript or Babel to compile decorators.
|
1639 |
|
1640 | **Example service**
|
1641 | ```js
|
1642 | const moleculer = require('moleculer');
|
1643 | const { Service, Action, Event, Method } = require('moleculer-decorators');
|
1644 | const web = require('moleculer-web');
|
1645 | const broker = new moleculer.ServiceBroker({
|
1646 | logger: console,
|
1647 | logLevel: "debug",
|
1648 | });
|
1649 |
|
1650 | @Service({
|
1651 | mixins: [web],
|
1652 | settings: {
|
1653 | port: 3000,
|
1654 | routes: [
|
1655 | ...
|
1656 | ]
|
1657 | }
|
1658 | })
|
1659 | class ServiceName {
|
1660 | @Action()
|
1661 | Login(ctx) {
|
1662 | ...
|
1663 | }
|
1664 |
|
1665 | // With options
|
1666 | @Action({
|
1667 | cache: false,
|
1668 | params: {
|
1669 | a: "number",
|
1670 | b: "number"
|
1671 | }
|
1672 | })
|
1673 | Login2(ctx) {
|
1674 | ...
|
1675 | }
|
1676 |
|
1677 | @Event
|
1678 | 'event.name'(payload, sender, eventName) {
|
1679 | ...
|
1680 | }
|
1681 |
|
1682 | @Method
|
1683 | authorize(ctx, route, req, res) {
|
1684 | ...
|
1685 | }
|
1686 |
|
1687 | hello() { // Private
|
1688 | ...
|
1689 | }
|
1690 |
|
1691 | started() { // Reserved for moleculer, fired when started
|
1692 | ...
|
1693 | }
|
1694 |
|
1695 | created() { // Reserved for moleculer, fired when created
|
1696 | ...
|
1697 | }
|
1698 |
|
1699 | stopped() { // Reserved for moleculer, fired when stopped
|
1700 | ...
|
1701 | }
|
1702 | }
|
1703 |
|
1704 | broker.createService(ServiceName);
|
1705 | broker.start();
|
1706 | ```
|
1707 |
|
1708 | ## Event group option
|
1709 | The broker groups the event listeners by group name. The group name is the name of the service where your event handler is declared. You can change it in the event definition.
|
1710 |
|
1711 | ```js
|
1712 | module.export = {
|
1713 | name: "payment",
|
1714 | events: {
|
1715 | "order.created": {
|
1716 | // Register handler to "other" group instead of "payment" group.
|
1717 | group: "other",
|
1718 | handler(payload) {
|
1719 | // ...
|
1720 | }
|
1721 | }
|
1722 | }
|
1723 | }
|
1724 | ```
|
1725 |
|
1726 | ## New experimental TCP transporter with UDP discovery
|
1727 | There is a new built-in zero-config TCP transporter. It uses [Gossip protocol](https://en.wikipedia.org/wiki/Gossip_protocol) to disseminate node info, service list and heartbeats. It has an integrated UDP discovery to detect new nodes on the network. It uses multicast discovery messages.
|
1728 | If the UDP is prohibited on your network, you can use `urls` option. It is a list of remote endpoints (host/ip, port, nodeID). It can be a static list in your configuration or a file path which contains the list.
|
1729 |
|
1730 | >Please note, you don't need to list all remote nodes. It's enough at least one node which is online. For example, you can create a "serviceless" gossiper node, which does nothing, just shares remote nodes addresses by gossip messages. So all nodes need to know only the gossiper node address to be able to detect all other nodes.
|
1731 |
|
1732 |
|
1733 | It means, you don't have to configure any transporter, just start the brokers/nodes, use same namespaces and the nodes will find each others.
|
1734 | >If you don't want to use transporter, set `transporter: null` in broker options.
|
1735 | -->
|
1736 |
|
1737 | **Use TCP transporter with default options**
|
1738 | ```js
|
1739 | const broker = new ServiceBroker({
|
1740 | transporter: "TCP"
|
1741 | });
|
1742 | ```
|
1743 |
|
1744 | **Use TCP transporter with static node list**
|
1745 | ```js
|
1746 | const broker = new ServiceBroker({
|
1747 | transporter: "tcp://172.17.0.1:6000/node-1,172.17.0.2:6000/node-2"
|
1748 | });
|
1749 | ```
|
1750 | or
|
1751 | ```js
|
1752 | const broker = new ServiceBroker({
|
1753 | nodeID: "node-1",
|
1754 | transporter: {
|
1755 | type: "TCP",
|
1756 | options: {
|
1757 | udpDiscovery: false,
|
1758 | urls: [
|
1759 | "172.17.0.1:6000/node-1",
|
1760 | "172.17.0.2:6000/node-2",
|
1761 | "172.17.0.3:6000/node-3"
|
1762 | ]
|
1763 | }
|
1764 | }
|
1765 | });
|
1766 | ```
|
1767 |
|
1768 |
|
1769 | **All TCP transporter options with default values**
|
1770 | ```js
|
1771 | const broker = new ServiceBroker({
|
1772 | logger: true,
|
1773 | transporter: {
|
1774 | type: "TCP",
|
1775 | options: {
|
1776 | // Enable UDP discovery
|
1777 | udpDiscovery: true,
|
1778 | // Reusing UDP server socket
|
1779 | udpReuseAddr: true,
|
1780 |
|
1781 | // UDP port
|
1782 | udpPort: 4445,
|
1783 | // UDP bind address (if null, bind on all interfaces)
|
1784 | udpBindAddress: null,
|
1785 | // UDP sending period (seconds)
|
1786 | udpPeriod: 30,
|
1787 |
|
1788 | // Multicast address.
|
1789 | udpMulticast: "239.0.0.0",
|
1790 | // Multicast TTL setting
|
1791 | udpMulticastTTL: 1,
|
1792 |
|
1793 | // Send broadcast (Boolean, String, Array<String>)
|
1794 | udpBroadcast: false,
|
1795 |
|
1796 | // TCP server port. Null or 0 means random port
|
1797 | port: null,
|
1798 | // Static remote nodes address list (when UDP discovery is not available)
|
1799 | urls: null,
|
1800 | // Use hostname as preffered connection address
|
1801 | useHostname: true,
|
1802 |
|
1803 | // Gossip sending period in seconds
|
1804 | gossipPeriod: 2,
|
1805 | // Maximum enabled outgoing connections. If reach, close the old connections
|
1806 | maxConnections: 32,
|
1807 | // Maximum TCP packet size
|
1808 | maxPacketSize: 1 * 1024 * 1024
|
1809 | }
|
1810 | }
|
1811 | });
|
1812 | ```
|
1813 |
|
1814 | ## New experimental transporter for Kafka
|
1815 | There is a new transporter for [Kafka](https://kafka.apache.org/). It is a very simple implementation. It transfers Moleculer packets to consumers via pub/sub. There are not implemented offset, replay...etc features.
|
1816 | Please note, it is an **experimental** transporter. **Do not use it in production yet!**
|
1817 |
|
1818 | >To use it, install `kafka-node` with `npm install kafka-node --save` command.
|
1819 |
|
1820 | **Connect to Zookeeper**
|
1821 | ```js
|
1822 | const broker = new ServiceBroker({
|
1823 | logger: true,
|
1824 | transporter: "kafka://192.168.51.29:2181"
|
1825 | });
|
1826 | ```
|
1827 |
|
1828 | **Connect to Zookeeper with custom options**
|
1829 | ```js
|
1830 | const broker = new ServiceBroker({
|
1831 | logger: true,
|
1832 | transporter: {
|
1833 | type: "kafka",
|
1834 | options: {
|
1835 | host: "192.168.51.29:2181",
|
1836 |
|
1837 | // KafkaClient options. More info: https://github.com/SOHU-Co/kafka-node#clientconnectionstring-clientid-zkoptions-noackbatchoptions-ssloptions
|
1838 | client: {
|
1839 | zkOptions: undefined,
|
1840 | noAckBatchOptions: undefined,
|
1841 | sslOptions: undefined
|
1842 | },
|
1843 |
|
1844 | // KafkaProducer options. More info: https://github.com/SOHU-Co/kafka-node#producerclient-options-custompartitioner
|
1845 | producer: {},
|
1846 | customPartitioner: undefined,
|
1847 |
|
1848 | // ConsumerGroup options. More info: https://github.com/SOHU-Co/kafka-node#consumergroupoptions-topics
|
1849 | consumer: {
|
1850 | },
|
1851 |
|
1852 | // Advanced options for `send`. More info: https://github.com/SOHU-Co/kafka-node#sendpayloads-cb
|
1853 | publish: {
|
1854 | partition: 0,
|
1855 | attributes: 0
|
1856 | }
|
1857 | }
|
1858 | }
|
1859 |
|
1860 | });
|
1861 | ```
|
1862 |
|
1863 | ## New experimental transporter for NATS Streaming
|
1864 | There is a new transporter for [NATS Streaming](https://nats.io/documentation/streaming/nats-streaming-intro/). It is a very simple implementation. It transfers Moleculer packets to consumers via pub/sub. There are not implemented offset, replay...etc features.
|
1865 | Please note, it is an **experimental** transporter. **Do not use it in production yet!**
|
1866 |
|
1867 | >To use it, install `node-nats-streaming` with `npm install node-nats-streaming --save` command.
|
1868 |
|
1869 | **Connect to NATS Streaming server**
|
1870 | ```js
|
1871 | // Shorthand to local server
|
1872 | const broker = new ServiceBroker({
|
1873 | logger: true,
|
1874 | transporter: "STAN"
|
1875 | });
|
1876 |
|
1877 | // Shorthand
|
1878 | const broker = new ServiceBroker({
|
1879 | logger: true,
|
1880 | transporter: "stan://192.168.0.120:4222"
|
1881 | });
|
1882 |
|
1883 | // Shorthand with options
|
1884 | const broker = new ServiceBroker({
|
1885 | logger: true,
|
1886 | transporter: {
|
1887 | type: "STAN",
|
1888 | options: {
|
1889 | url: "stan://127.0.0.1:4222",
|
1890 | clusterID: "my-cluster"
|
1891 | }
|
1892 | }
|
1893 | });
|
1894 |
|
1895 | ```
|
1896 |
|
1897 | ## Define custom REPL commands in broker options
|
1898 | You can define your custom REPL commands in broker options to extend Moleculer REPL commands.
|
1899 |
|
1900 | ```js
|
1901 | const broker = new ServiceBroker({
|
1902 | logger: true,
|
1903 | replCommands: [
|
1904 | {
|
1905 | command: "hello <name>",
|
1906 | description: "Call the greeter.hello service with name",
|
1907 | alias: "hi",
|
1908 | options: [
|
1909 | { option: "-u, --uppercase", description: "Uppercase the name" }
|
1910 | ],
|
1911 | types: {
|
1912 | string: ["name"],
|
1913 | boolean: ["u", "uppercase"]
|
1914 | },
|
1915 | //parse(command, args) {},
|
1916 | //validate(args) {},
|
1917 | //help(args) {},
|
1918 | allowUnknownOptions: true,
|
1919 | action(broker, args) {
|
1920 | const name = args.options.uppercase ? args.name.toUpperCase() : args.name;
|
1921 | return broker.call("greeter.hello", { name }).then(console.log);
|
1922 | }
|
1923 | }
|
1924 | ]
|
1925 | });
|
1926 |
|
1927 | broker.repl();
|
1928 | ```
|
1929 |
|
1930 | # Changes
|
1931 | - MemoryCacher clears all cache entries after the transporter connected/reconnected.
|
1932 | - `broker.loadServices` file mask is changed from `*.service.js` to `**/*.service.js` in order to load all services from subfolders, too.
|
1933 | - `ServiceNotFoundError` and `ServiceNotAvailableError` errors are retryable errors.
|
1934 | - `Strategy.select` method gets only available endpoint list.
|
1935 | - old unavailable nodes are removed from registry after 10 minutes.
|
1936 | - CPU usage in `HEARTBEAT` packet is working properly in Windows, too.
|
1937 | - register middlewares before internal service (`$node.*`) loading.
|
1938 | - `broker.getAction` deprecated method is removed.
|
1939 | - `PROTOCOL_VERSION` constant is available via broker as `ServiceBroker.PROTOCOL_VERSION` or `broker.PROTOCOL_VERSION`
|
1940 | - serialization functions are moved from transit to transporter codebase.
|
1941 | - `ctx.broadcast` shortcut method is created to send broadcast events from action handler.
|
1942 | - `broker.started` property is created to indicate broker starting state.
|
1943 |
|
1944 | # Fixes
|
1945 | - handles invalid `dependencies` value in service schema [#164](https://github.com/moleculerjs/moleculer/pull/164)
|
1946 | - fix event emit error if payload is `null`,
|
1947 |
|
1948 | --------------------------------------------------
|
1949 | <a name="0.11.10"></a>
|
1950 | # [0.11.10](https://github.com/moleculerjs/moleculer/compare/v0.11.9...v0.11.10) (2018-01-19)
|
1951 |
|
1952 | # New
|
1953 |
|
1954 | ## Built-in clustering in Moleculer Runner [#169](https://github.com/moleculerjs/moleculer/pull/169)
|
1955 | By [@tinchoz49](https://github.com/tinchoz49 ) Moleculer Runner has a new built-in clustering function. With it, you can start multiple instances from your broker.
|
1956 |
|
1957 | Example to start all services from the `services` folder in 4 instances.
|
1958 | ```bash
|
1959 | $ moleculer-runner --instances 4 services
|
1960 | ```
|
1961 | > Please note, the `nodeID` will be suffixed with the worker ID.
|
1962 |
|
1963 |
|
1964 | ## Context meta & params in metrics events [#166](https://github.com/moleculerjs/moleculer/pull/166)
|
1965 | By [@dani8art](https://github.com/dani8art) you can set that the broker put some `ctx.meta` and `ctx.params` fields to the metrics events.
|
1966 | You can define it in the action definition:
|
1967 |
|
1968 | ```js
|
1969 | module.exports = {
|
1970 | name: "test",
|
1971 | actions: {
|
1972 | import: {
|
1973 | cache: true,
|
1974 | metrics: {
|
1975 | // Disable to add `ctx.params` to metrics payload. Default: false
|
1976 | params: false,
|
1977 | // Enable to add `ctx.meta` to metrics payload. Default: true
|
1978 | meta: true
|
1979 | },
|
1980 | handler(ctx) {
|
1981 | // ...
|
1982 | }
|
1983 | }
|
1984 | }
|
1985 | }
|
1986 | ```
|
1987 |
|
1988 | If the value is `true`, it adds all fields. If `Array`, it adds the specified fields. If `Function`, it calls with `params` or `meta`and you need to return an `Object`.
|
1989 |
|
1990 | --------------------------------------------------
|
1991 | <a name="0.11.9"></a>
|
1992 | # [0.11.9](https://github.com/moleculerjs/moleculer/compare/v0.11.8...v0.11.9) (2018-01-08)
|
1993 |
|
1994 | # New
|
1995 |
|
1996 | ## Strategy resolver
|
1997 |
|
1998 | ServiceBroker can resolve the `strategy` from a string.
|
1999 | ```js
|
2000 | const broker = new ServiceBroker({
|
2001 | registry: {
|
2002 | strategy: "Random"
|
2003 | // strategy: "RoundRobin"
|
2004 | }
|
2005 | });
|
2006 | ```
|
2007 |
|
2008 | You can set it via env variables as well, if you are using the Moleculer Runner:
|
2009 | ```sh
|
2010 | $ REGISTRY_STRATEGY=random
|
2011 | ```
|
2012 |
|
2013 | ## Load env files in Moleculer Runner [#158](https://github.com/moleculerjs/moleculer/issues/158)
|
2014 | Moleculer runner can load `.env` file at starting. There are two new cli options to load env file:
|
2015 |
|
2016 | * `-e, --env` - Load envorinment variables from the '.env' file from the current folder.
|
2017 | * `-E, --envfile <filename>` - Load envorinment variables from the specified file.
|
2018 |
|
2019 | **Example**
|
2020 | ```sh
|
2021 | # Load the default .env file from current directory
|
2022 | $ moleculer-runner --env
|
2023 |
|
2024 | # Load the specified .my-env file
|
2025 | $ moleculer-runner --envfile .my-env
|
2026 | ```
|
2027 |
|
2028 | # Fixes
|
2029 | - fixed hot reloading after broken service files by @askuzminov ([#155](https://github.com/moleculerjs/moleculer/pull/155))
|
2030 | - allow fallbackResponse to be falsy values
|
2031 |
|
2032 |
|
2033 | --------------------------------------------------
|
2034 | <a name="0.11.8"></a>
|
2035 | # [0.11.8](https://github.com/moleculerjs/moleculer/compare/v0.11.7...v0.11.8) (2017-12-15)
|
2036 |
|
2037 | # Changes
|
2038 | - `d.ts` has been improved.
|
2039 |
|
2040 | --------------------------------------------------
|
2041 | <a name="0.11.7"></a>
|
2042 | # [0.11.7](https://github.com/moleculerjs/moleculer/compare/v0.11.6...v0.11.7) (2017-12-05)
|
2043 |
|
2044 | # Changes
|
2045 | - `d.ts` has been improved.
|
2046 |
|
2047 | --------------------------------------------------
|
2048 | <a name="0.11.6"></a>
|
2049 | # [0.11.6](https://github.com/moleculerjs/moleculer/compare/v0.11.5...v0.11.6) (2017-11-07)
|
2050 |
|
2051 | # New
|
2052 |
|
2053 | ## New cacher features
|
2054 | In action cache keys you can use meta keys with `#` prefix.
|
2055 | ```js
|
2056 | broker.createService({
|
2057 | name: "posts",
|
2058 | actions: {
|
2059 | list: {
|
2060 | cache: {
|
2061 | // Cache key: "limit" & "offset" from ctx.params, "user.id" from ctx.meta
|
2062 | keys: ["limit", "offset", "#user.id"],
|
2063 | ttl: 5
|
2064 | },
|
2065 | handler(ctx) {...}
|
2066 | }
|
2067 | }
|
2068 | });
|
2069 | ```
|
2070 |
|
2071 | You can override the cacher default TTL setting in action definition.
|
2072 | ```js
|
2073 | const broker = new ServiceBroker({
|
2074 | cacher: {
|
2075 | type: "memory",
|
2076 | options: {
|
2077 | ttl: 30 // 30 seconds
|
2078 | }
|
2079 | }
|
2080 | });
|
2081 |
|
2082 | broker.createService({
|
2083 | name: "posts",
|
2084 | actions: {
|
2085 | list: {
|
2086 | cache: {
|
2087 | // This cache entries will be expired after 5 seconds instead of 30.
|
2088 | ttl: 5
|
2089 | },
|
2090 | handler(ctx) {...}
|
2091 | }
|
2092 | }
|
2093 | });
|
2094 | ```
|
2095 |
|
2096 | You can change the built-in cacher keygen function to your own one.
|
2097 | ```js
|
2098 | const broker = new ServiceBroker({
|
2099 | cacher: {
|
2100 | type: "memory",
|
2101 | options: {
|
2102 | keygen(name, params, meta, keys) {
|
2103 | // Generate a cache key
|
2104 | return ...;
|
2105 | }
|
2106 | }
|
2107 | }
|
2108 | });
|
2109 | ```
|
2110 |
|
2111 | # Others
|
2112 | - `d.ts` has been improved by [@rmccallum81](https://github.com/rmccallum81)
|
2113 |
|
2114 | --------------------------------------------------
|
2115 | <a name="0.11.5"></a>
|
2116 | # [0.11.5](https://github.com/moleculerjs/moleculer/compare/v0.11.4...v0.11.5) (2017-10-12)
|
2117 |
|
2118 | # Changes
|
2119 | - `strategy` option has been fixed in broker option [#121](https://github.com/moleculerjs/moleculer/pull/121)
|
2120 |
|
2121 |
|
2122 | --------------------------------------------------
|
2123 | <a name="0.11.4"></a>
|
2124 | # [0.11.4](https://github.com/moleculerjs/moleculer/compare/v0.11.3...v0.11.4) (2017-10-11)
|
2125 |
|
2126 | # Changes
|
2127 | - Moleculer Runner arguments have been fixed (`services` arg)
|
2128 | - update AMQP default queue options by @Nathan-Schwartz [#119](https://github.com/moleculerjs/moleculer/pull/119)
|
2129 |
|
2130 |
|
2131 | --------------------------------------------------
|
2132 | <a name="0.11.3"></a>
|
2133 | # [0.11.3](https://github.com/moleculerjs/moleculer/compare/v0.11.2...v0.11.3) (2017-10-10)
|
2134 |
|
2135 | # Changes
|
2136 | - The `ack` handling has been fixed in AMQP transporter.
|
2137 | - AMQP RCP integration tests are added.
|
2138 |
|
2139 |
|
2140 | --------------------------------------------------
|
2141 | <a name="0.11.2"></a>
|
2142 | # [0.11.2](https://github.com/moleculerjs/moleculer/compare/v0.11.1...v0.11.2) (2017-10-06)
|
2143 |
|
2144 | # New
|
2145 |
|
2146 | ## Service dependencies [#102](https://github.com/moleculerjs/moleculer/issues/102)
|
2147 | The `Service` schema has a new `dependencies` property. The serice can wait for other dependening ones when it starts. This way you don't need to call `waitForServices` in `started` any longer.
|
2148 |
|
2149 | ```js
|
2150 | module.exports = {
|
2151 | name: "posts",
|
2152 | settings: {
|
2153 | $dependencyTimeout: 30000 // Default: 0 - no timeout
|
2154 | },
|
2155 | dependencies: [
|
2156 | "likes", // shorthand w/o version
|
2157 | { name: "users", version: 2 }, // with numeric version
|
2158 | { name: "comments", version: "staging" } // with string version
|
2159 | ],
|
2160 | started() {
|
2161 | this.logger.info("Service started after the dependent services available.");
|
2162 | }
|
2163 | ....
|
2164 | }
|
2165 | ```
|
2166 | The `started` service handler is called once the `likes`, `users` and `comments` services are registered (on the local or remote nodes).
|
2167 |
|
2168 | ## Pending request queue size limit [#111](https://github.com/moleculerjs/moleculer/issues/111)
|
2169 | The `ServiceBroker` has a new `maxQueueSize` option under `transit` key. The broker protects the process to avoid crash during a high load with it. The `maxQueueSize` default value is 50,000. If pending request queue size reaches it, broker rejects the request with a `QueueIsFull` (retryable) error.
|
2170 |
|
2171 | ```js
|
2172 | const broker = new ServiceBroker({
|
2173 | transporter: "NATS",
|
2174 | transit: {
|
2175 | maxQueueSize: 10 * 1000
|
2176 | }
|
2177 | }
|
2178 | ```
|
2179 |
|
2180 | # Changes
|
2181 |
|
2182 | ## The `waitForServices` method supports service versions [#112](https://github.com/moleculerjs/moleculer/issues/112)
|
2183 | By [@imatefx](https://github.com/imatefx), the `waitForServices` broker & service methods support service versions. Use the following formats to define version in a dependency:
|
2184 |
|
2185 | ```js
|
2186 | module.exports = {
|
2187 | name: "test",
|
2188 | dependencies: { name: "users", version: 2 }
|
2189 | };
|
2190 | ```
|
2191 |
|
2192 | --------------------------------------------------
|
2193 | <a name="0.11.1"></a>
|
2194 | # [0.11.1](https://github.com/moleculerjs/moleculer/compare/v0.11.0...v0.11.1) (2017-09-27)
|
2195 |
|
2196 | # New
|
2197 |
|
2198 | ## Service metadata [#91](https://github.com/moleculerjs/moleculer/issues/91)
|
2199 | The `Service` schema has a new `metadata` property. The Moleculer modules doesn't use it, so you can use it whatever you want.
|
2200 |
|
2201 | ```js
|
2202 | broker.createService({
|
2203 | name: "posts",
|
2204 | settings: {},
|
2205 | metadata: {
|
2206 | scalable: true,
|
2207 | priority: 5
|
2208 | },
|
2209 |
|
2210 | actions: { ... }
|
2211 | });
|
2212 | ```
|
2213 |
|
2214 | > The `metadata` is transferred between nodes, you can access it via `$node.services`. Or inside service with `this.metadata` like settings.
|
2215 |
|
2216 | ## NATS transporter supports to use the built-in balancer
|
2217 | The NATS transporter has been changed. It supports to use the NATS built-in balancer instead of Moleculer balancer. In this case every `call` & `emit` will be transferred through NATS message broker.
|
2218 |
|
2219 | ```js
|
2220 | const broker = new ServiceBroker({
|
2221 | transporter: "NATS",
|
2222 | disableBalancer: true
|
2223 | });
|
2224 | ```
|
2225 |
|
2226 | # Changes
|
2227 | - ping nodes with `broker.sendPing` instead of `broker.transit.sendPing`.
|
2228 | - `index.d.ts` updated to v0.11
|
2229 | - AMQP integration tests has been rewritten.
|
2230 | - process exit code changed from `2` to `1` in `broker.fatal`. Reason: `2` is reserved by Bash for builtin misuse. [More info](https://nodejs.org/dist/latest-v6.x/docs/api/process.html#process_exit_codes)
|
2231 |
|
2232 | --------------------------------------------------
|
2233 | <a name="0.11.0"></a>
|
2234 | # [0.11.0](https://github.com/moleculerjs/moleculer/compare/v0.10.0...v0.11.0) (2017-09-12)
|
2235 |
|
2236 | # Breaking changes
|
2237 |
|
2238 | ## Protocol changed [#86](https://github.com/moleculerjs/moleculer/issues/86)
|
2239 | The Moleculer transportation protocol has been changed. It means, **the new (>= v0.11) versions can't communicate with the old (<= v0.10.x) ones.**
|
2240 | You can find more information about changes in [#86](https://github.com/moleculerjs/moleculer/issues/86) issue.
|
2241 |
|
2242 | ## Balanced events
|
2243 | The whole event handling has been rewritten. By now Moleculer supports [event driven architecture](http://microservices.io/patterns/data/event-driven-architecture.html). It means that event emits are balanced like action calls are.
|
2244 |
|
2245 | For example, you have 2 main services: `users` & `payments`. Both subscribe to the `user.created` event. You start 3 instances from `users` service and 2 instances from `payments` service. If you emit the event with `broker.emit('user.created')`, broker groups & balances the event, so only one `users` and one `payments` service receive the event.
|
2246 | You can also send broadcast events with the `broker.broadcast('user.created')` command. This way every service instance on every node receives the event.
|
2247 | The `broker.broadcastLocal('user.created')` command sends events only to the local services.
|
2248 |
|
2249 | ## Renamed & new internal events
|
2250 | Every internal event name starts with '$'. These events are not transferred to remote nodes.
|
2251 |
|
2252 | **Renamed events:**
|
2253 | - `node.connected` -> `$node.connected`
|
2254 | - `node.updated` -> `$node.updated`
|
2255 | - `node.disconnected` -> `$node.disconnected`
|
2256 | - `services.changed` -> `$services.changed`. It is called if local or remote service list is changed.
|
2257 | - `circuit-breaker.closed` -> `$circuit-breaker.closed`
|
2258 | - `circuit-breaker.opened` -> `$circuit-breaker.opened`
|
2259 | - `circuit-breaker.half-opened` -> `$circuit-breaker.half-opened`
|
2260 |
|
2261 | **New events:**
|
2262 | - global circuit breaker events for metrics: `metrics.circuit-breaker.closed`, `metrics.circuit-breaker.opened`, `metrics.circuit-breaker.half-opened`
|
2263 |
|
2264 | ## Switchable built-in load balancer
|
2265 | The built-in Moleculer load balancer is switchable. You can turn it off, if the transporter has internal balancer (currently AMQP has it).
|
2266 |
|
2267 | ```js
|
2268 | const broker = new ServiceBroker({
|
2269 | disableBalancer: false
|
2270 | });
|
2271 | ```
|
2272 |
|
2273 | > Please note! If built-in balancer is disabled, every call & emit (including local ones too) are transferred via transporter.
|
2274 |
|
2275 | ## Removed broker methods
|
2276 | Some internal broker methods have been removed or renamed.
|
2277 | - `broker.bus` has been removed.
|
2278 | - `broker.on` has been removed. Use `events` in service schema instead.
|
2279 | - `broker.once` has been removed.
|
2280 | - `broker.off` has been removed.
|
2281 | - `broker.getService` has been renamed to `broker.getLocalService`
|
2282 | - `broker.hasService` has been removed.
|
2283 | - `broker.hasAction` has been removed.
|
2284 | - `broker.getAction` has been deprecated.
|
2285 | - `broker.isActionAvailable` has been removed.
|
2286 |
|
2287 | ## Changed local service responses
|
2288 | Internal action (`$node.list`, `$node.services`, `$node.actions`, `$node.health`) responses are changed. New internal action (`$node.events`) to list event subscriptiion is added.
|
2289 |
|
2290 | ## Broker option changes
|
2291 | - `heartbeatInterval` default value is changed from `10` to `5`.
|
2292 | - `heartbeatTimeout` default value is changed from `30` to `15`.
|
2293 | - `circuitBreaker.maxFailures` default value is changed from `5` to `3`.
|
2294 | - `logFormatter` accepts string. The `simple` value is a new formatter to show only log level & log messages.
|
2295 |
|
2296 | # New
|
2297 |
|
2298 | ## Ping command
|
2299 | New PING & PONG feature has been implemented. Ping remite nodes to measure the network latency and system time differences.
|
2300 |
|
2301 | ```js
|
2302 | broker.createService({
|
2303 | name: "test",
|
2304 | events: {
|
2305 | "$node.pong"({ nodeID, elapsedTime, timeDiff }) {
|
2306 | this.logger.info(`Pong received from '${nodeID}' - Time: ${elapsedTime}ms, System time difference: ${timeDiff}ms`);
|
2307 | }
|
2308 | }
|
2309 | });
|
2310 |
|
2311 | broker.start().then(() => broker.transit.sendPing(/*nodeID*/));
|
2312 | ```
|
2313 |
|
2314 | ## Pluggable validator
|
2315 | The Validator in ServiceBroker is plugable. So you can change the built-in `fastest-validator` to a slower one :) [Example Joi validator](https://gist.github.com/icebob/07024c0ac22589a5496473c2a8a91146)
|
2316 |
|
2317 | ## Waiting for other services feature
|
2318 | If your services depend on other ones, use the `waitForService` method to make services wait until dependencies start.
|
2319 |
|
2320 | ```js
|
2321 | let svc = broker.createService({
|
2322 | name: "seed",
|
2323 | started() {
|
2324 | return this.waitForServices(["posts", "users"]).then(() => {
|
2325 | // Do work...
|
2326 | });
|
2327 | }
|
2328 | });
|
2329 | ```
|
2330 |
|
2331 | Signature:
|
2332 | ```js
|
2333 | this.waitForServices(serviceNames: String|Array<String>, timeout: Number/*milliseconds*/, interval: Number/*milliseconds*/): Promise
|
2334 | ```
|
2335 |
|
2336 | ## New error types
|
2337 | We added some new Moleculer error classes.
|
2338 | - `MoleculerRetryableError` - Common Retryable error. Caller retries the request if `retryCount > 0`.
|
2339 | - `MoleculerServerError` - Common server error (5xx).
|
2340 | - `MoleculerClientError` - Common client/request error (4xx).
|
2341 | - `ServiceNotAvailable` - Raises if the service is registered but isn't available (no live nodes or CB disabled them).
|
2342 | - `ProtocolVersionMismatchError` - Raises if connect a node with an older client (<= v0.10.0)).
|
2343 |
|
2344 | # Other changes
|
2345 | - The cachers don't listen "cache.clean" event.
|
2346 |
|
2347 | --------------------------------------------------
|
2348 | <a name="0.10.0"></a>
|
2349 | # [0.10.0](https://github.com/moleculerjs/moleculer/compare/v0.9.0...v0.10.0) (2017-08-20)
|
2350 |
|
2351 | # Breaking changes
|
2352 |
|
2353 | ## No more `nodeID == null` in local stuff
|
2354 | In all core modules removed the nullable `nodeID`. Every places (context, events, $node.* results) the nodeID contains a valid (local or remote) nodeID. On local nodes it equals with `broker.nodeID`.
|
2355 |
|
2356 | **Migration guide**
|
2357 |
|
2358 | Before:
|
2359 | ```js
|
2360 | if (ctx.nodeID == null) { ... }
|
2361 | // ---------
|
2362 | events: {
|
2363 | "users.created"(payload, sender) {
|
2364 | if (sender == null) { ... }
|
2365 | }
|
2366 | }
|
2367 | ```
|
2368 |
|
2369 | After:
|
2370 | ```js
|
2371 | if (ctx.nodeID == ctx.broker.nodeID) { ... }
|
2372 | // ---------
|
2373 | events: {
|
2374 | "users.created"(payload, sender) {
|
2375 | if (sender == this.broker.nodeID) { ... }
|
2376 | }
|
2377 | }
|
2378 | ```
|
2379 |
|
2380 | ## `internalActions` is renamed to `internalServices`
|
2381 | The `internalActions` broker option is renamed to `internalServices`.
|
2382 |
|
2383 | ## Removed `broker.createNewContext` method
|
2384 | The `createNewContext` broker method is moved to `Context`class as a static method.
|
2385 |
|
2386 | **Migration guide:**
|
2387 |
|
2388 | Before:
|
2389 | ```js
|
2390 | let ctx = broker.createNewContext(action, nodeID, params, opts);
|
2391 | ```
|
2392 |
|
2393 | After:
|
2394 | ```js
|
2395 | let ctx = Context.create(broker, action, nodeID, params, opts);
|
2396 | // or better
|
2397 | let ctx = broker.ContextFactory.create(broker, action, nodeID, params, opts);
|
2398 | ```
|
2399 |
|
2400 | ## Removed `LOCAL_NODE_ID` constant
|
2401 | The recently added `LOCAL_NODE_ID` constant is removed. If you want to check the nodeID is local, please use the `if (nodeID == broker.nodeID)` syntax.
|
2402 |
|
2403 | ## Class based pluggable Service registry strategies [#75](https://github.com/moleculerjs/moleculer/pull/75)
|
2404 | By @WoLfulus, the service registry balancer strategy is now pluggable.
|
2405 |
|
2406 | **New syntax:**
|
2407 | ```js
|
2408 | let Strategies = require("moleculer").Strategies;
|
2409 |
|
2410 | const broker = new ServiceBroker({
|
2411 | registry: {
|
2412 | strategy: new Strategies.RoundRobin()
|
2413 | }
|
2414 | });
|
2415 | ```
|
2416 |
|
2417 | **Custom strategy**
|
2418 |
|
2419 | You can create you custom strategy.
|
2420 |
|
2421 | ```js
|
2422 | let BaseStrategy = require("moleculer").Strategies.Base;
|
2423 |
|
2424 | class CustomStrategy extends BaseStrategy {
|
2425 | select(list) {
|
2426 | return list[0];
|
2427 | }
|
2428 | };
|
2429 |
|
2430 | const broker = new ServiceBroker({
|
2431 | registry: {
|
2432 | strategy: new CustomStrategy()
|
2433 | }
|
2434 | });
|
2435 | ```
|
2436 |
|
2437 | ## Metrics event payloads are changed
|
2438 | The metrics payload contains `remoteCall` and `callerNodeID` properties. The `remoteCall` is true if the request is called from a remote node. In this case the `callerNodeID` contains the caller nodeID.
|
2439 |
|
2440 | `metrics.trace.span.start`:
|
2441 | ```js
|
2442 | {
|
2443 | "action": {
|
2444 | "name": "users.get"
|
2445 | },
|
2446 | "id": "123123123",
|
2447 | "level": 1,
|
2448 | "parent": 123,
|
2449 | "remoteCall": true,
|
2450 | "requestID": "abcdef",
|
2451 | "startTime": 123456789,
|
2452 | "nodeID": "node-1",
|
2453 | "callerNodeID": "node-2"
|
2454 | }
|
2455 | ```
|
2456 |
|
2457 | `metrics.trace.span.start`:
|
2458 | ```js
|
2459 | {
|
2460 | "action": {
|
2461 | "name": "users.get"
|
2462 | },
|
2463 | "duration": 45,
|
2464 | "id": "123123123",
|
2465 | "parent": 123,
|
2466 | "requestID": "abcdef",
|
2467 | "startTime": 123456789,
|
2468 | "endTime": 123456795,
|
2469 | "fromCache": false,
|
2470 | "level": 1,
|
2471 | "remoteCall": true,
|
2472 | "nodeID": "node-1",
|
2473 | "callerNodeID": "node-2"
|
2474 | }
|
2475 | ```
|
2476 |
|
2477 | # New
|
2478 |
|
2479 | ## Hot reload services [#82](https://github.com/moleculerjs/moleculer/pull/82)
|
2480 | The ServiceBroker supports hot reloading services. If you enable it broker will watch file changes. If you modify service file, broker will reload it on-the-fly.
|
2481 | [Demo video](https://www.youtube.com/watch?v=l9FsAvje4F4)
|
2482 |
|
2483 | > Note: Hot reloading is only working with Moleculer Runner or if you load your services with `broker.loadService` or `broker.loadServices`.
|
2484 |
|
2485 | **Usage**
|
2486 |
|
2487 | ```js
|
2488 | const broker = new ServiceBroker({
|
2489 | logger: console,
|
2490 | hotReload: true
|
2491 | });
|
2492 |
|
2493 | broker.loadService("./services/test.service.js");
|
2494 | ```
|
2495 |
|
2496 | **Usage with Moleculer Runner**
|
2497 |
|
2498 | Turn it on with `--hot` or `-H` flags.
|
2499 |
|
2500 | ```bash
|
2501 | $ moleculer-runner --hot ./services/test.service.js
|
2502 | ```
|
2503 |
|
2504 | ## Protocol documentation
|
2505 | Moleculer protocol documentation is available in [docs/PROTOCOL.md](docs/PROTOCOL.md) file.
|
2506 |
|
2507 | # AMQP transporter [#72](https://github.com/moleculerjs/moleculer/pull/72)
|
2508 | By @Nathan-Schwartz, AMQP (for RabbitMQ) transporter added to Moleculer project.
|
2509 |
|
2510 | ```js
|
2511 | const broker = new ServiceBroker({
|
2512 | transporter: "amqp://guest:guest@rabbitmq-server:5672"
|
2513 | });
|
2514 |
|
2515 | const broker = new ServiceBroker({
|
2516 | transporter: new AmqpTransporter({
|
2517 | amqp: {
|
2518 | url: "amqp://guest:guest@localhost:5672",
|
2519 | eventTimeToLive: 5000,
|
2520 | prefetch: 1
|
2521 | }
|
2522 | });
|
2523 | });
|
2524 | ```
|
2525 |
|
2526 | --------------------------------------------------
|
2527 |
|
2528 | <a name="0.9.0"></a>
|
2529 | # [0.9.0](https://github.com/moleculerjs/moleculer/compare/v0.8.5...v0.9.0) (2017-08-10)
|
2530 |
|
2531 | # Breaking changes
|
2532 |
|
2533 | ## Namespace support, removed `prefix` options [#57](https://github.com/moleculerjs/moleculer/issues/57)
|
2534 | The broker has a new `namespace` option to segment your services. For example, you are running development & production services (or more production services) on the same transporter. If you are using different `namespace` you can avoid collisions between different environments.
|
2535 |
|
2536 | > You can reach it in your services as `this.broker.namespace`.
|
2537 |
|
2538 | Thereupon the `prefix` option in transporters & cachers is removed.
|
2539 |
|
2540 | **Example**
|
2541 | ```js
|
2542 | const broker = new ServiceBroker({
|
2543 | logger: console,
|
2544 | namespace: "DEV",
|
2545 | transporter: "NATS",
|
2546 | cacher: "Redis"
|
2547 | });
|
2548 | ```
|
2549 | In this case the transporter & cacher prefix will be `MOL-DEV`.
|
2550 |
|
2551 |
|
2552 | ## Renamed internal service settings
|
2553 | The `useVersionPrefix` is renamed to `$noVersionPrefix`. The `serviceNamePrefix` is renamed to `$noServiceNamePrefix`. Both settings logical state is changed.
|
2554 | The `cache` setting is renamed to `$cache`.
|
2555 |
|
2556 | ### Migration guide
|
2557 |
|
2558 | **Before**
|
2559 | ```js
|
2560 | broker.createService({
|
2561 | name: "test",
|
2562 | settings: {
|
2563 | useVersionPrefix: false,
|
2564 | serviceNamePrefix: false,
|
2565 | cache: true
|
2566 | }
|
2567 | });
|
2568 | ```
|
2569 |
|
2570 | **After**
|
2571 | ```js
|
2572 | broker.createService({
|
2573 | name: "test",
|
2574 | settings: {
|
2575 | $noVersionPrefix: true,
|
2576 | $noServiceNamePrefix: true,
|
2577 | $cache: true
|
2578 | }
|
2579 | });
|
2580 | ```
|
2581 |
|
2582 | ## Changed versioned action names [#58](https://github.com/moleculerjs/moleculer/issues/58)
|
2583 | Based on [#58](https://github.com/moleculerjs/moleculer/issues/58) if service version is a `String`, the version in action names won't be prefixed with `v`, expect if it is a `Number`.
|
2584 |
|
2585 | **Example**
|
2586 | ```js
|
2587 | broker.createService({
|
2588 | name: "test",
|
2589 | version: 3,
|
2590 | actions: {
|
2591 | hello(ctx) {}
|
2592 | }
|
2593 | });
|
2594 | broker.call("v3.test.hello");
|
2595 |
|
2596 | broker.createService({
|
2597 | name: "test",
|
2598 | version: "staging",
|
2599 | actions: {
|
2600 | hello(ctx) {}
|
2601 | }
|
2602 | });
|
2603 | broker.call("staging.test.hello");
|
2604 | ```
|
2605 |
|
2606 | ## Module log level configuration is removed
|
2607 | The module log level is not supported. The `logLevel` option can be only `String`. It is used if the logger is the `console`. **In case of external loggers you have to handle log levels.**
|
2608 |
|
2609 | # New
|
2610 |
|
2611 | ## Better logging [#61](https://github.com/moleculerjs/moleculer/issues/61)
|
2612 | The whole Moleculer logger is rewritten. It supports better the external loggers. The built-in log message format is also changed.
|
2613 |
|
2614 | ### Built-in `console` logger
|
2615 | ```js
|
2616 | const broker = createBroker({
|
2617 | logger: console,
|
2618 | logLevel: "info"
|
2619 | });
|
2620 | ```
|
2621 | New console output:
|
2622 | ![image](https://user-images.githubusercontent.com/306521/29127309-011bd0e0-7d21-11e7-87e2-c2d83352a857.png)
|
2623 |
|
2624 | **With custom `logFormatter`**
|
2625 | ```js
|
2626 | const broker = new ServiceBroker({
|
2627 | logger: console,
|
2628 | logFormatter(level, args, bindings) {
|
2629 | return level.toUpperCase() + " " + bindings.nodeID + ": " + args.join(" ");
|
2630 | }
|
2631 | });
|
2632 | broker.logger.warn("Warn message");
|
2633 | broker.logger.error("Error message");
|
2634 | ```
|
2635 | Output:
|
2636 | ```
|
2637 | WARN dev-pc: Warn message
|
2638 | ERROR dev-pc: Error message
|
2639 | ```
|
2640 |
|
2641 | ### External loggers
|
2642 |
|
2643 | **[Pino](http://getpino.io/)**
|
2644 | ```js
|
2645 | const pino = require("pino")({ level: "info" });
|
2646 | const broker = new ServiceBroker({
|
2647 | logger: bindings => pino.child(bindings)
|
2648 | });
|
2649 | ```
|
2650 |
|
2651 | Sample output:
|
2652 | ![image](https://user-images.githubusercontent.com/306521/29127258-e151e3bc-7d20-11e7-9995-025f53cf41ec.png)
|
2653 |
|
2654 |
|
2655 | **[Bunyan](https://github.com/trentm/node-bunyan)**
|
2656 | ```js
|
2657 | const bunyan = require("bunyan");
|
2658 | const logger = bunyan.createLogger({ name: "moleculer", level: "info" });
|
2659 | const broker = new ServiceBroker({
|
2660 | logger: bindings => logger.child(bindings)
|
2661 | });
|
2662 | ```
|
2663 |
|
2664 | Sample output:
|
2665 | ![image](https://user-images.githubusercontent.com/306521/29127286-f2203428-7d20-11e7-84f1-c81aeaaaef53.png)
|
2666 |
|
2667 | **[Winston](https://github.com/winstonjs/winston)**
|
2668 | ```js
|
2669 | const broker = new ServiceBroker({
|
2670 | logger: bindings => new winston.Logger({
|
2671 | transports: [
|
2672 | new (winston.transports.Console)({
|
2673 | timestamp: true,
|
2674 | colorize: true,
|
2675 | prettyPrint: true
|
2676 | })
|
2677 | ]
|
2678 | })
|
2679 | });
|
2680 | ```
|
2681 |
|
2682 | **[Winston context](https://github.com/citrix-research/node-winston-context)**
|
2683 | ```js
|
2684 | const WinstonContext = require("winston-context");
|
2685 | const winston = require("winston");
|
2686 | const broker = createBroker({
|
2687 | logger: bindings => new WinstonContext(winston, "", bindings)
|
2688 | });
|
2689 | ```
|
2690 |
|
2691 | > Please note! Some external loggers have not `trace` & `fatal` log methods (e.g.: winston). In this case you have to extend your logger.
|
2692 | ```js
|
2693 | const WinstonContext = require("winston-context");
|
2694 | const winston = require("winston");
|
2695 | const { extend } = require("moleculer").Logger;
|
2696 | const broker = createBroker({
|
2697 | logger: bindings => extend(new WinstonContext(winston, "", bindings))
|
2698 | });
|
2699 | ```
|
2700 |
|
2701 | The `bindings` contains the following properties:
|
2702 | - `ns` - namespace
|
2703 | - `nodeID` - nodeID
|
2704 | - `mod` - type of core module: `broker`, `cacher`, `transit`, `transporter`
|
2705 | - `svc` - service name
|
2706 | - `ver` - service version
|
2707 |
|
2708 | **Please avoid to use these property names when you log an `Object`.** For example: the `broker.logger.error({ mod: "peanut" })` overrides the original `mod` value!
|
2709 |
|
2710 |
|
2711 | ## Dynamic service load & destroy
|
2712 | Available to load & destroy services after the broker started. For example you can hot-reload your services in runtime. The remote nodes will be notified about changes and the broker will emit a `services.changed` event locally.
|
2713 |
|
2714 | **Example**
|
2715 | ```js
|
2716 |
|
2717 | broker.start().then(() => {
|
2718 |
|
2719 | setTimeout(() => {
|
2720 | // Create a new service after 5s
|
2721 | broker.createService({
|
2722 | name: "math",
|
2723 | actions: {
|
2724 | add(ctx) {
|
2725 | return Number(ctx.params.a) + Number(ctx.params.b);
|
2726 | },
|
2727 | }
|
2728 | });
|
2729 |
|
2730 | }, 5000);
|
2731 |
|
2732 | setTimeout(() => {
|
2733 | // Destroy a created service after 10s
|
2734 | let svc = broker.getService("math");
|
2735 | broker.destroyService(svc);
|
2736 |
|
2737 | }, 10000);
|
2738 |
|
2739 | });
|
2740 | ```
|
2741 |
|
2742 | ## Multiple service calls [#31](https://github.com/moleculerjs/moleculer/issues/31)
|
2743 | With `broker.mcall` method you can call multiple actions (in parallel).
|
2744 |
|
2745 | **Example with `Array`**
|
2746 | ```js
|
2747 | broker.mcall([
|
2748 | { action: "posts.find", params: {limit: 5, offset: 0}, options: { timeout: 500 } },
|
2749 | { action: "users.find", params: {limit: 5, sort: "username"} }
|
2750 | ]).then(results => {
|
2751 | let posts = results[0];
|
2752 | let users = results[1];
|
2753 | })
|
2754 | ```
|
2755 |
|
2756 | **Example with `Object`**
|
2757 | ```js
|
2758 | broker.mcall({
|
2759 | posts: { action: "posts.find", params: {limit: 5, offset: 0}, options: { timeout: 500 } },
|
2760 | users: { action: "users.find", params: {limit: 5, sort: "username"} }
|
2761 | }).then(results => {
|
2762 | let posts = results.posts;
|
2763 | let users = results.users;
|
2764 | })
|
2765 | ```
|
2766 |
|
2767 | # Fixes
|
2768 |
|
2769 | --------------------------------------------------
|
2770 | <a name="0.8.5"></a>
|
2771 | # [0.8.5](https://github.com/moleculerjs/moleculer/compare/v0.8.4...v0.8.5) (2017-08-06)
|
2772 |
|
2773 | # Fixes
|
2774 | - fixed logger method bindings.
|
2775 | - fixed transporter shutdown errors [#62](https://github.com/moleculerjs/moleculer/issues/62)
|
2776 |
|
2777 | --------------------------------------------------
|
2778 | <a name="0.8.4"></a>
|
2779 | # [0.8.4](https://github.com/moleculerjs/moleculer/compare/v0.8.3...v0.8.4) (2017-07-24)
|
2780 |
|
2781 | # Fixes
|
2782 | - fixed `Calling error! TypeError : Cannot read property 'requestID' of undefined` error when you call a local action from other one directly.
|
2783 |
|
2784 | --------------------------------------------------
|
2785 | <a name="0.8.3"></a>
|
2786 | # [0.8.3](https://github.com/moleculerjs/moleculer/compare/v0.8.2...v0.8.3) (2017-07-24)
|
2787 |
|
2788 | # New
|
2789 |
|
2790 | ## Removable actions in mixins
|
2791 | You can remove an existing action when mixing a service.
|
2792 |
|
2793 | ```
|
2794 | broker.createService({
|
2795 | name: "test",
|
2796 | mixins: [OtherService],
|
2797 | actions: {
|
2798 | dangerAction: false
|
2799 | }
|
2800 | });
|
2801 | ```
|
2802 | In the `test` service the `dangerAction` action won't be registered.
|
2803 |
|
2804 | ## Support NPM modules in `moleculer-runner`
|
2805 | You can load services from NPM module in `moleculer-runner`.
|
2806 |
|
2807 | **With CLI arguments**
|
2808 | ```bash
|
2809 | $ moleculer-runner -r npm:moleculer-fake npm:moleculer-twilio
|
2810 | ```
|
2811 |
|
2812 | **With env**
|
2813 | ```bash
|
2814 | $ SERVICES=posts,users,npm:moleculer-fale,npm:moleculer-twilio
|
2815 |
|
2816 | $ moleculer-runner
|
2817 | ```
|
2818 |
|
2819 | --------------------------------------------------
|
2820 | <a name="0.8.2"></a>
|
2821 | # [0.8.2](https://github.com/moleculerjs/moleculer/compare/v0.8.1...v0.8.2) (2017-07-06)
|
2822 |
|
2823 | # Fixes
|
2824 | - fixed Redis cacher option resolver in ServiceBroker. Now it accepts connection string.
|
2825 |
|
2826 | ```js
|
2827 | const broker = new ServiceBroker({
|
2828 | cacher: "redis://localhost"
|
2829 | });
|
2830 | ```
|
2831 |
|
2832 | # New
|
2833 |
|
2834 | ## Validator updated
|
2835 | The fastest-validator is updated to [v0.5.0](https://github.com/icebob/fastest-validator/releases/tag/v0.5.0). It supports multi rules & custom validators.
|
2836 |
|
2837 | --------------------------------------------------
|
2838 | <a name="0.8.1"></a>
|
2839 | # [0.8.1](https://github.com/moleculerjs/moleculer/compare/v0.8.0...v0.8.1) (2017-07-03)
|
2840 |
|
2841 | # New
|
2842 |
|
2843 | ## Improved mixin's merge logic [#50](https://github.com/moleculerjs/moleculer/issues/50/)
|
2844 | The mixins merge logic is handle better events & lifecycle events. If you have a `created`, `started`, `stopped` lifecycle event or any other service event handler in your services, but your mixin has the same event, Moleculer will call all of them in your service and in mixins.
|
2845 |
|
2846 | [Read more about mixins](http://moleculer.services/docs/service.html#Mixins)
|
2847 |
|
2848 | --------------------------------------------------
|
2849 |
|
2850 | <a name="0.8.0"></a>
|
2851 | # [0.8.0](https://github.com/moleculerjs/moleculer/compare/v0.7.0...v0.8.0) (2017-06-21)
|
2852 |
|
2853 | # New
|
2854 |
|
2855 | ## Project runner script
|
2856 | There is a new Moleculer project runner script in the `bin` folder.
|
2857 | You can use it if you want to create small repos for services. In this case you needn't to create a ServiceBroker with options. Just create a `moleculer.config.js` or `moleculer.config.json` file in the root of repo fill it with your options and call the `moleculer-runner` within the NPM scripts.
|
2858 | As an other solution you can put it to the environment variables instead of putting options to file.
|
2859 |
|
2860 | [Read more about runner](http://moleculer.services/docs/runner.html)
|
2861 |
|
2862 | ## Shorthand for transporters, cachers and serializers in broker options
|
2863 | Some new resolvers are implemented in broker options to support shorthand configurations. This feature is enabled to load broker options easily from a JSON file or load from environment variables.
|
2864 |
|
2865 | **Usage for transporters**
|
2866 | ```js
|
2867 | // Connect to the NATS default (localhost) server
|
2868 | const broker = new ServiceBroker({
|
2869 | transporter: "NATS"
|
2870 | });
|
2871 |
|
2872 | // Connect to a NATS server with connection string
|
2873 | const broker = new ServiceBroker({
|
2874 | transporter: "nats://nats-server:4222"
|
2875 | });
|
2876 |
|
2877 | // Connect to a NATS server with transporter options
|
2878 | const broker = new ServiceBroker({
|
2879 | transporter: {
|
2880 | type: "NATS",
|
2881 | options: {
|
2882 | prefix: "TEST",
|
2883 | nats: {
|
2884 | host: "nats-server",
|
2885 | user: "admin",
|
2886 | pass: "nats-pass"
|
2887 | }
|
2888 | }
|
2889 | }
|
2890 | });
|
2891 | ```
|
2892 |
|
2893 | **Usage for cachers**
|
2894 | ```js
|
2895 | // Use a memory cacher
|
2896 | const broker = new ServiceBroker({
|
2897 | cacher: true
|
2898 | // or
|
2899 | // cacher: "Memory"
|
2900 | });
|
2901 |
|
2902 | // Use a Redis cacher with default options
|
2903 | const broker = new ServiceBroker({
|
2904 | cacher: "Redis"
|
2905 | });
|
2906 |
|
2907 | // Use a Redis cacher with options
|
2908 | const broker = new ServiceBroker({
|
2909 | cacher: {
|
2910 | type: "Redis",
|
2911 | options: {
|
2912 | ttl: 100
|
2913 | }
|
2914 | }
|
2915 | });
|
2916 | ```
|
2917 |
|
2918 | **Usage for serializers**
|
2919 | ```js
|
2920 | // Use the Avro serializer
|
2921 | const broker = new ServiceBroker({
|
2922 | serializers: "Avro"
|
2923 | });
|
2924 |
|
2925 | // Use the Protocol Buffer serializer
|
2926 | const broker = new ServiceBroker({
|
2927 | serializers: {
|
2928 | type: "ProtoBuf"
|
2929 | }
|
2930 | });
|
2931 | ```
|
2932 |
|
2933 | ## Built-in circuit breaker [#22](https://github.com/moleculerjs/moleculer/issues/22/)
|
2934 | A better circuit breaker solution has recently been implemented. As a result of this improvement every call (local and remote) is protected by the built-in circuit breaker.
|
2935 | You only need to enable it in broker options.
|
2936 |
|
2937 | **Usage**
|
2938 | ```js
|
2939 | const broker = new ServiceBroker({
|
2940 | circuitBreaker: {
|
2941 | enabled: true, // Enable this feature
|
2942 | maxFailures: 5, // Trip breaker on 5 failures
|
2943 | halfOpenTime: 10 * 1000 // 10 sec to switch to `half-open` state
|
2944 | failureOnTimeout: true // Failure if request timed out
|
2945 | failureOnReject: true // Failure if request rejected with error code >= 500
|
2946 | }
|
2947 | });
|
2948 | ```
|
2949 |
|
2950 | *`nodeUnavailable` method is dropped.*
|
2951 |
|
2952 | ## Service Registry module
|
2953 | A built-in Service Registry module was created. It handles actions of services on nodes, circuit breaker logic...etc. It would be pluggable in the future.
|
2954 |
|
2955 | You can change the load balancing strategies of Service Registry via broker options.
|
2956 |
|
2957 | **Example**
|
2958 |
|
2959 | ```js
|
2960 | const { STRATEGY_ROUND_ROBIN, STRATEGY_RANDOM } = require("moleculer");
|
2961 |
|
2962 | const broker = new ServiceBroker({
|
2963 | registry: {
|
2964 | strategy: STRATEGY_ROUND_ROBIN, // Load balancing strategy
|
2965 | preferLocal: true // First call local service if available
|
2966 | }
|
2967 | });
|
2968 | ```
|
2969 |
|
2970 | ## REPL mode [#30](https://github.com/moleculerjs/moleculer/issues/30/)
|
2971 | Broker module has an interactive REPL mode. You can call actions, load services, also emit events, subscribe to & unsubscribe from events from your console. You can list registered nodes & actions.
|
2972 | > To use REPL mode please install the [moleculer-repl](https://github.com/moleculerjs/moleculer-repl) module with `npm install moleculer-repl --save` command.
|
2973 |
|
2974 | **Start REPL mode**
|
2975 | ```js
|
2976 | const broker = new ServiceBroker({ logger: console });
|
2977 |
|
2978 | // Start REPL
|
2979 | broker.repl();
|
2980 | ```
|
2981 |
|
2982 | **Commands**
|
2983 | ```
|
2984 | Commands:
|
2985 |
|
2986 | help [command...] Provides help for a given command.
|
2987 | exit Exits application.
|
2988 | q Exit application
|
2989 | call <actionName> [params] Call an action
|
2990 | dcall <nodeID> <actionName> [params] Call a direct action
|
2991 | emit <eventName> [payload] Emit an event
|
2992 | load <servicePath> Load a service from file
|
2993 | loadFolder <serviceFolder> [fileMask] Load all service from folder
|
2994 | subscribe <eventName> Subscribe to an event
|
2995 | unsubscribe <eventName> Unsubscribe from an event
|
2996 | actions [options] List of actions
|
2997 | nodes List of nodes
|
2998 | info Information from broker
|
2999 | ```
|
3000 |
|
3001 | ### REPL Commands
|
3002 |
|
3003 | **List nodes**
|
3004 | ```
|
3005 | mol $ nodes
|
3006 | ```
|
3007 | ![image](https://cloud.githubusercontent.com/assets/306521/26260893/67a579d4-3ccf-11e7-955a-70f252aa260d.png)
|
3008 |
|
3009 | **List services**
|
3010 | ```
|
3011 | mol $ services
|
3012 | ```
|
3013 |
|
3014 | **List actions**
|
3015 | ```
|
3016 | mol $ actions
|
3017 | ```
|
3018 | ![image](https://cloud.githubusercontent.com/assets/306521/26260954/8ef9d44e-3ccf-11e7-995a-ccbe035b2a9a.png)
|
3019 |
|
3020 | **Show common informations**
|
3021 | ```
|
3022 | mol $ info
|
3023 | ```
|
3024 | ![image](https://cloud.githubusercontent.com/assets/306521/26260974/aaea9b02-3ccf-11e7-9e1c-ec9150518791.png)
|
3025 |
|
3026 | **Call an action**
|
3027 | ```
|
3028 | mol $ call "test.hello"
|
3029 | ```
|
3030 |
|
3031 | **Call an action with params**
|
3032 | ```
|
3033 | mol $ call "math.add" '{"a": 5, "b": 4}'
|
3034 | ```
|
3035 |
|
3036 | **Direct call**
|
3037 | ```
|
3038 | mol $ dcall server-2 "$node.health"
|
3039 | ```
|
3040 |
|
3041 | **Emit an event**
|
3042 | ```
|
3043 | mol $ emit "user.created"
|
3044 | ```
|
3045 |
|
3046 | **Subscribe to an event**
|
3047 | ```
|
3048 | mol $ subscribe "user.created"
|
3049 | ```
|
3050 |
|
3051 | **Unsubscribe from an event**
|
3052 | ```
|
3053 | mol $ unsubscribe "user.created"
|
3054 | ```
|
3055 |
|
3056 | **Load a service**
|
3057 | ```
|
3058 | mol $ load "./math.service.js"
|
3059 | ```
|
3060 |
|
3061 | **Load services from folder**
|
3062 | ```
|
3063 | mol $ load "./services"
|
3064 | ```
|
3065 |
|
3066 | ## Direct call
|
3067 | It is available to call an action directly on a specified node. To use it set `nodeID` in options of call.
|
3068 |
|
3069 | **Example**
|
3070 |
|
3071 | ```js
|
3072 | broker.call("user.create", {}, { timeout: 5000, nodeID: "server-12" });
|
3073 | ```
|
3074 |
|
3075 | ## Mergeable schemas in `createService`
|
3076 | Now there is a second parameter of `broker.createService`. With it you can override the schema properties. You can use it to use a built-in service & override some props.
|
3077 |
|
3078 | **Example**
|
3079 |
|
3080 | ```js
|
3081 | broker.createService(apiGwService, {
|
3082 | settings: {
|
3083 | // Change port setting
|
3084 | port: 8080
|
3085 | },
|
3086 | actions: {
|
3087 | myAction() {
|
3088 | // Add a new action to apiGwService service
|
3089 | }
|
3090 | },
|
3091 |
|
3092 | created() {
|
3093 | // Overwrite apiGwService.created handler
|
3094 | }
|
3095 | });
|
3096 | ```
|
3097 |
|
3098 | Or you can merge it manually with `mergeSchemas` method.
|
3099 | ```js
|
3100 | let mergedSchema = broker.mergeSchemas(origSchema, modifications);
|
3101 | broker.createService(mergedSchema);
|
3102 | ```
|
3103 |
|
3104 | ## Service mixins
|
3105 | Like mergeable schemas, the service may include any mixin schemas. The constructor of Service merges these mixins with the schema of Service. It is to reuse an other Service in your service or extend an other Service.
|
3106 |
|
3107 | **Examples**
|
3108 |
|
3109 | ```js
|
3110 | const ApiGwService = require("moleculer-web");
|
3111 |
|
3112 | module.exports = {
|
3113 | name: "api",
|
3114 | mixins: [ApiGwService]
|
3115 | settings: {
|
3116 | // Change port setting
|
3117 | port: 8080
|
3118 | },
|
3119 | actions: {
|
3120 | myAction() {
|
3121 | // Add a new action to apiGwService service
|
3122 | }
|
3123 | }
|
3124 | }
|
3125 | ```
|
3126 |
|
3127 | ## New option to protect calling loop
|
3128 | You can protect your app against calling loop with the new `maxCallLevel` option. If the `ctx.level` value reaches this limit, it throwns a `MaxCallLevelError` error.
|
3129 |
|
3130 | ```js
|
3131 | const broker = new ServiceBroker({
|
3132 | maxCallLevel: 100
|
3133 | });
|
3134 | ```
|
3135 |
|
3136 | ## New Service setting
|
3137 | There is a new `useVersionPrefix` option in Service settings. If it is `false`, Moleculer can't use the version number of service as prefix for action names. The name of service will be `users.find` instead of `v2.users.find`. The default is `true`.
|
3138 |
|
3139 | # Changes
|
3140 |
|
3141 | ## Removed the `node.reconnected` and `node.broken` events (breaking)
|
3142 | We merged the `node.connected` and `node.reconnected` events. The payload is changed:
|
3143 | ```js
|
3144 | {
|
3145 | node: {...},
|
3146 | reconnected: false // it indicates the node is connected or reconnected
|
3147 | }
|
3148 | ```
|
3149 |
|
3150 | We merged also the `node.disconnected` and `node.broken` events. The payload is changed:
|
3151 | ```js
|
3152 | {
|
3153 | node: {...},
|
3154 | unexpected: true // True: broken, not coming heart-beat, False: received "DISCONNECT" packet
|
3155 | }
|
3156 | ```
|
3157 |
|
3158 | ## Remove Transporter, Cacher and Serializers dependencies (breaking)
|
3159 | Moleculer doesn't contain dependencies for NATS, Redis, MQTT, MsgPack, Avro and Protobuf. So it need install manually in your project.
|
3160 | If you want to create a Moleculer project which communicates via NATS and your Redis cacher, you have to install `npm install moleculer nats redis --save`
|
3161 |
|
3162 | ## Changed code of ServiceNotFoundError
|
3163 | The code of `ServiceNotFoundError` is changed from `501` to `404`. [More info](https://github.com/moleculerjs/moleculer-web/issues/7)
|
3164 |
|
3165 | ## Using Nanomatch instead of micromatch
|
3166 | Memory cacher is using [nanomatch](https://github.com/micromatch/nanomatch) instead of [micromatch](https://github.com/micromatch/micromatch). The `nanomatch` is ~10x faster.
|
3167 |
|
3168 | ## Removed `metricsSendInterval` option [#24](https://github.com/moleculerjs/moleculer/issues/24/)
|
3169 | The `metricsSendInterval` option is removed from broker options. If you want to access statistics & health info, call the `$node.health` and `$node.stats` actions.
|
3170 |
|
3171 | ## Metrics & Statistics separated [#24](https://github.com/moleculerjs/moleculer/issues/24/)
|
3172 | The metrics & statistics features separated. You can use just metrics or just statistics.
|
3173 |
|
3174 | ## Metrics nodeID
|
3175 | Metrics events contains two nodeID properties.
|
3176 | - `nodeID`: the "caller" nodeID
|
3177 | - `targetNodeID`: in case of remote call this is the remote nodeID
|
3178 |
|
3179 | ## Response error with stack trace
|
3180 | If an action responses an error on a remote node, the transporter will send back the error to the caller with the stack traces.
|
3181 | ```js
|
3182 | // It will print the original error stack trace.
|
3183 | broker.call("account.deposit").catch(err => console.log(err.stack));
|
3184 | ```
|
3185 |
|
3186 | ## Type property in custom error
|
3187 | The `CustomError` class renamed to `MoleculerError`. also it has a `type` new property. You can store here a custom error type. For example, if you have a `ValidationError`, in some cases the `name` & `code` is not enough. By `type` error causes are to be stored.
|
3188 |
|
3189 | **Example**
|
3190 | ```js
|
3191 | const ERR_MISSING_ID = "ERR_MISSING_ID";
|
3192 | const ERR_ENTITY_NOT_FOUND = "ERR_ENTITY_NOT_FOUND";
|
3193 |
|
3194 | broker.createService({
|
3195 | actions: {
|
3196 | get(ctx) {
|
3197 | if (ctx.params.id) {
|
3198 | const entity = this.searchEntity(ctx.params.id);
|
3199 | if (entity)
|
3200 | return entity;
|
3201 | else
|
3202 | return Promise.reject(new ValidationError("Not found entity!", ERR_ENTITY_NOT_FOUND));
|
3203 | } else
|
3204 | return Promise.reject(new ValidationError("Please set the ID field!", ERR_MISSING_ID));
|
3205 | }
|
3206 | }
|
3207 | });
|
3208 | ```
|
3209 |
|
3210 | ## Renamed `appendServiceName` settings to `serviceNamePrefix` in Service schema
|
3211 |
|
3212 | ## Fatal crash
|
3213 | The `ServiceBroker` has a new `fatal` method. If you call it, broker will log the message with `fatal` level and exit the process with code `2`.
|
3214 |
|
3215 | `broker.fatal(message, err, needExit = true)`
|
3216 | > If you are running your app in containers and it has restart policy, you can use it to restart your app.
|
3217 |
|
3218 | **Usage**
|
3219 | ```js
|
3220 |
|
3221 | try {
|
3222 | // Do something dangerous
|
3223 | } catch(err) {
|
3224 | broker.fatal("Dangerous thing is happened!", err, true);
|
3225 | }
|
3226 |
|
3227 | ```
|
3228 |
|
3229 | ## Low-level changes
|
3230 | - new output of `$node.actions` and `$node.services`
|
3231 | - In packet `INFO` & `DISCOVER` changed the `actions` property to `services` and now it contains all services with actions of node
|
3232 | - splitted `broker.registerService` to `registerLocalService` and `registerRemoteService`
|
3233 | - new `broker.unregisterServicesByNode`. It will be called when a node disconnected
|
3234 |
|
3235 | --------------------------------------------------
|
3236 |
|
3237 |
|
3238 | <a name="0.7.0"></a>
|
3239 | # 0.7.0 (2017-04-24)
|
3240 |
|
3241 | # New
|
3242 | ## Serializers for transporters [#10](https://github.com/moleculerjs/moleculer/issues/10/)
|
3243 | Implemented pluggable serializers.
|
3244 | Built-in serializers:
|
3245 | - [x] JSON (default)
|
3246 | - [x] [Avro](https://github.com/mtth/avsc)
|
3247 | - [x] [MsgPack](https://github.com/mcollina/msgpack5)
|
3248 | - [x] [ProtoBuf](https://developers.google.com/protocol-buffers/)
|
3249 |
|
3250 | **Usage**
|
3251 | ```js
|
3252 | let JSONSerializer = require("moleculer").Serializers.JSON;
|
3253 |
|
3254 | const broker = new ServiceBroker({
|
3255 | serializer: new JSONSerializer(),
|
3256 | transporter: new Transporter(),
|
3257 | nodeID: "node-1"
|
3258 | });
|
3259 | ```
|
3260 |
|
3261 | ## Typescript definition file [#5](https://github.com/moleculerjs/moleculer/issues/5)
|
3262 | Created an index.d.ts file. I'm not familiar in Typescript, so if you found error please help me and open a PR with fix. Thank you!
|
3263 |
|
3264 | ## Metrics rate option
|
3265 | Added `metricsRate` options to broker. This property sets the rate of sampled calls.
|
3266 | - `1` means to metric all calls
|
3267 | - `0.5` means to metric 50% of calls
|
3268 | - `0.1` means to metric 10% of calls
|
3269 |
|
3270 | **Usage**
|
3271 | ```js
|
3272 | const broker = new ServiceBroker({
|
3273 | metrics: true,
|
3274 | metricsRate: 0.1
|
3275 | });
|
3276 | ```
|
3277 |
|
3278 | ## Context meta data ([#16](https://github.com/moleculerjs/moleculer/pull/16))
|
3279 | Added `meta` prop to `Context`. The `meta` will be merged if has parent context.
|
3280 | In case of remote calls the metadata will be transfered to the target service.
|
3281 |
|
3282 | **Usage**
|
3283 |
|
3284 | Set meta in `broker.call`:
|
3285 | ```js
|
3286 | // Broker call with meta data
|
3287 | broker.call("user.create", { name: "Adam", status: true}, {
|
3288 | timeout: 1000,
|
3289 | meta: {
|
3290 | // Send logged in user data with request to the service
|
3291 | loggedInUser: {
|
3292 | userID: 45,
|
3293 | roles: [ "admin" ]
|
3294 | }
|
3295 | }
|
3296 | })
|
3297 | ```
|
3298 |
|
3299 | Access meta in action:
|
3300 | ```js
|
3301 | broker.createService({
|
3302 | name: "user",
|
3303 | actions: {
|
3304 | create(ctx) {
|
3305 | const meta = ctx.meta;
|
3306 | if (meta.loggedInUser && meta.loggedInUser.roles.indexOf("admin") !== -1)
|
3307 | return Promise.resolve(...);
|
3308 | else
|
3309 | throw new MoleculerError("Access denied!");
|
3310 | }
|
3311 | }
|
3312 | });
|
3313 | ```
|
3314 |
|
3315 | # Changes
|
3316 |
|
3317 | ## Update benchmarkify
|
3318 | Benchmarkify updated & created continuous benchmarking with [bench-bot](https://github.com/icebob/bench-bot).
|
3319 | Bench-bot is a benchmark runner. If a new Pull Request opened, bench-bot will run benchmarks against the `master` branch and it will post the results to the PR conversation.
|
3320 |
|
3321 | ## Timeout & fallback response handling in local calls too
|
3322 | - Can be use timeout & fallback response in local calls.
|
3323 | - Timeout handling move from `Transit` to `ServiceBroker`
|
3324 | - Remove `wrapContentAction`
|
3325 | - In case of calling error, Node will be unavailable only if the error code >= `500`
|
3326 |
|
3327 | ## Context changes
|
3328 | - Removed `createSubContext`
|
3329 | - Removed `ctx.parent` and added `ctx.parentID`
|
3330 | - Removed options in constructor. New constructor syntax:
|
3331 | ```js
|
3332 | let ctx = new Context(broker, action);
|
3333 | ctx.setParams({ a: 5 });
|
3334 | ctx.generateID(); // for metrics
|
3335 | ctx.requestID = requestID;
|
3336 | ```
|
3337 | - Add Context reference to returned Promise
|
3338 | ```js
|
3339 | const p = broker.call("user.create");
|
3340 | console.log("Context:", p.ctx);
|
3341 | ```
|
3342 |
|
3343 | ## Sender in event handlers
|
3344 | If an event triggered remotely on an other node, broker passes the nodeID of sender to the event handler as 2nd parameter.
|
3345 | ```js
|
3346 | // Usage in subscription
|
3347 | broker.on("**", (payload, sender) => console.log(`Event from ${sender || "local"}:`, payload));
|
3348 |
|
3349 | // Usage in Service schema
|
3350 | broker.createService({
|
3351 | ...
|
3352 | events: {
|
3353 | something(payload, sender) {
|
3354 | console.log(`Something happened on '${sender}':`, payload);
|
3355 | }
|
3356 | }
|
3357 | });
|
3358 | ```
|
3359 |
|
3360 | ## Distributed timeout handling
|
3361 | Moleculer uses [distributed timeouts](https://www.datawire.io/guide/traffic/deadlines-distributed-timeouts-microservices/).In the chained calls the `ctx.call` decrement the original timeout value with the elapsed time. If the new calculated timeout is less or equal than 0, it'll skip the next calls because the first call is rejected with `RequestTimeoutError` error.
|
3362 |
|
3363 |
|
3364 | --------------------------------------------------
|
3365 |
|
3366 |
|
3367 | <a name="0.6.0"></a>
|
3368 | # 0.6.0 (2017-03-31)
|
3369 |
|
3370 | # New
|
3371 |
|
3372 | ## Validator library changed
|
3373 | The previous `validatorjs` validator removed and added own very fast [fastest-validator](https://github.com/icebob/fastest-validator) library. It can 3M validations/sec. Hereafter validation is not the bottle-neck. Only -7% slower with validation.
|
3374 |
|
3375 | **Here is the new benchmark result:**
|
3376 | ```
|
3377 | Suite: Call with param validator
|
3378 | √ No validator x 588,463 ops/sec ±1.11% (84 runs sampled)
|
3379 | √ With validator passes x 541,903 ops/sec ±1.41% (84 runs sampled)
|
3380 | √ With validator fail x 25,648 ops/sec ±1.62% (85 runs sampled)
|
3381 | No validator 0.00% (588,463 ops/sec)
|
3382 | With validator passes -7.91% (541,903 ops/sec)
|
3383 | With validator fail -95.64% (25,648 ops/sec)
|
3384 | ```
|
3385 |
|
3386 | **Example params definition:**
|
3387 | ```js
|
3388 | mult: {
|
3389 | params: {
|
3390 | a: { type: "number" },
|
3391 | b: { type: "number" }
|
3392 | },
|
3393 | handler(ctx) {
|
3394 | return Number(ctx.params.a) * Number(ctx.params.b);
|
3395 | }
|
3396 | }
|
3397 | ```
|
3398 |
|
3399 | **Validation error object:**
|
3400 | ```js
|
3401 | [ {
|
3402 | type: 'number',
|
3403 | field: 'b',
|
3404 | message: 'The \'b\' field must be a number!'
|
3405 | } ]
|
3406 | ```
|
3407 |
|
3408 | # Changes
|
3409 | ## Added & removed log levels
|
3410 | * Added 2 new log levels (`fatal` and `trace`);
|
3411 | * Removed unused `log` level. Use `info` level instead.
|
3412 |
|
3413 | **Available levels:**
|
3414 | ```js
|
3415 | logger.trace("trace level");
|
3416 | logger.debug("debug level");
|
3417 | logger.info("info level");
|
3418 | logger.warn("warn level");
|
3419 | logger.error("error level");
|
3420 | logger.fatal("fatal level");
|
3421 | ```
|
3422 |
|
3423 | **Logger fallback levels:**
|
3424 | * `trace` -> `debug` -> `info`
|
3425 | * `debug` -> `info`
|
3426 | * `info`: main level, no fallback
|
3427 | * `warn` -> `error` -> `info`
|
3428 | * `error` -> `info`
|
3429 | * `fatal` -> `error` -> `info`
|
3430 |
|
3431 |
|
3432 | --------------------------------------------------
|
3433 |
|
3434 |
|
3435 | <a name="0.5.0"></a>
|
3436 | # 0.5.0 (2017-02-26)
|
3437 |
|
3438 | First release.
|