UNPKG

@stoplight/moleculer

Version:

Fast & powerful microservices framework for Node.JS

270 lines (232 loc) 5.23 kB
/* * moleculer * Copyright (c) 2018 MoleculerJS (https://github.com/moleculerjs/moleculer) * MIT Licensed */ "use strict"; const _ = require("lodash"); const { MoleculerServerError } = require("../errors"); /** * Endpoint list class * * @class EndpointList */ class EndpointList { /** * Creates an instance of EndpointList. * @param {Registry} registry * @param {ServiceBroker} broker * @param {String} name * @param {String} group * @param {EndPointClass} EndPointFactory * @param {StrategyClass} StrategyFactory * @param {Object?} strategyOptions * @memberof EndpointList */ constructor(registry, broker, name, group, EndPointFactory, StrategyFactory, strategyOptions) { this.registry = registry; this.broker = broker; this.logger = registry.logger; this.strategy = new StrategyFactory(registry, broker, strategyOptions); this.name = name; this.group = group; this.internal = name.startsWith("$"); this.EndPointFactory = EndPointFactory; this.endpoints = []; this.localEndpoints = []; } /** * Add a new endpoint * * @param {Node} node * @param {Service} service * @param {any} data * @returns * @memberof EndpointList */ add(node, service, data) { const found = this.endpoints.find(ep => ep.node == node && ep.service.name == service.name); if (found) { found.update(data); return found; } const ep = new this.EndPointFactory(this.registry, this.broker, node, service, data); this.endpoints.push(ep); this.setLocalEndpoints(); return ep; } /** * Get first endpoint * * @returns {Endpoint} * @memberof EndpointList */ getFirst() { if (this.endpoints.length > 0) return this.endpoints[0]; return null; } /** * Select next endpoint with balancer strategy * * @param {Array<Endpoint>} list * @param {Context} ctx * @returns {Endpoint} * @memberof EndpointList */ select(list, ctx) { const ret = this.strategy.select(list, ctx); if (!ret) { /* istanbul ignore next */ throw new MoleculerServerError( "Strategy returned an invalid endpoint.", 500, "INVALID_ENDPOINT", { strategy: typeof this.strategy } ); } return ret; } /** * Get next endpoint * * @param {Context} ctx * @returns * @memberof EndpointList */ next(ctx) { // No items if (this.endpoints.length === 0) { return null; } // If internal (service), return the local always if (this.internal && this.hasLocal()) { return this.nextLocal(); } // Only 1 item if (this.endpoints.length === 1) { // No need to select a node, return the only one const item = this.endpoints[0]; if (item.isAvailable) return item; return null; } // Search local item if (this.registry.opts.preferLocal === true && this.hasLocal()) { const ep = this.nextLocal(ctx); if (ep && ep.isAvailable) return ep; } const epList = this.endpoints.filter(ep => ep.isAvailable); if (epList.length == 0) return null; return this.select(epList, ctx); } /** * Get next local endpoint * * @param {Context} ctx * @returns * @memberof EndpointList */ nextLocal(ctx) { // No items if (this.localEndpoints.length === 0) { return null; } // Only 1 item if (this.localEndpoints.length === 1) { // No need to select a node, return the only one const item = this.localEndpoints[0]; if (item.isAvailable) return item; return null; } const epList = this.localEndpoints.filter(ep => ep.isAvailable); if (epList.length == 0) return null; return this.select(epList, ctx); } /** * Check there is available endpoint * * @returns * @memberof EndpointList */ hasAvailable() { return this.endpoints.find(ep => ep.isAvailable) != null; } /** * Check there is local endpoint * * @returns * @memberof EndpointList */ hasLocal() { return this.localEndpoints.length > 0; } /** * Set local endpoint * * @memberof EndpointList */ setLocalEndpoints() { this.localEndpoints = this.endpoints.filter(ep => ep.local); } /** * Get count of endpoints * * @returns * @memberof EndpointList */ count() { return this.endpoints.length; } /** * Get endpoint on a specified node * * @param {String} nodeID * @returns * @memberof EndpointList */ getEndpointByNodeID(nodeID) { const ep = this.endpoints.find(ep => ep.id == nodeID); if (ep && ep.isAvailable) return ep; return null; } /** * Check nodeID in the endpoint list * * @param {String} nodeID * @returns * @memberof EndpointList */ hasNodeID(nodeID) { return this.endpoints.find(ep => ep.id == nodeID) != null; } /** * Remove all endpoints by service * * @param {ServiceItem} service * @memberof EndpointList */ removeByService(service) { _.remove(this.endpoints, ep => { if (ep.service == service) { ep.destroy(); return true; } }); this.setLocalEndpoints(); } /** * Remove endpoints by node ID * * @param {String} nodeID * @memberof EndpointList */ removeByNodeID(nodeID) { _.remove(this.endpoints, ep => { if (ep.id == nodeID) { ep.destroy(); return true; } }); this.setLocalEndpoints(); } } module.exports = EndpointList;