2022-01-05 16:49:45 +00:00
|
|
|
/**
|
2022-01-19 17:38:43 +00:00
|
|
|
* @module connectivityManager
|
2022-01-05 16:49:45 +00:00
|
|
|
* @author Martin Karkowski
|
|
|
|
* @email m.karkowski@zema.de
|
2022-01-20 11:59:32 +00:00
|
|
|
*
|
|
|
|
* # NoPE - Connectivity Manager
|
|
|
|
*
|
|
|
|
* The NoPE-Dispatcher uses one `ConnectivityManager`. The manager observes the connection and remotly connected dispatchers (and their `ConnectivityManager`). The Manager detects newly connected dispatchers and disconnected dispatchers. Additionally, it sends a StatusMessage (in the form of `INopeStatusInfo`). This status message is interpreted as heartbeat. The `ConnectivityManager` checks those heartbeats with a defined interval. If a specific amount of time is ellapsed, the remote dispatcher is marked as `slow` -> `warning` -> `dead`. After an additional delay in the state `dead` the dispatcher is altough removed.
|
|
|
|
*
|
|
|
|
* ## Master
|
|
|
|
*
|
|
|
|
* Defaultly a `ConnectivityManager` is elected as `master`. The master is defined as the `ConnectivityManager` with the highest `upTime`.
|
|
|
|
*
|
|
|
|
* > Alternativly a master can be forced.
|
|
|
|
*
|
|
|
|
* ## Synchronizing time
|
|
|
|
*
|
|
|
|
* Because we asume, that **NoPE** is running on different computing nodes, we have to be able to synchronize the time between those elements. Therefore the `ConnectivityManager` is able to sync the time (by providing a `timestamp` and an additional `delay` that was needed to get to the call (for instance `ping / 2`))
|
|
|
|
*
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* ```javascript
|
|
|
|
* // First lets install nope using npm
|
|
|
|
* const nope = require("../dist-nodejs/index.nodejs")
|
|
|
|
*
|
|
|
|
* // Create a communicator:
|
|
|
|
* // We will use the event layer (which just runs internally)
|
|
|
|
* const communicator = nope.getLayer("event");
|
|
|
|
*
|
|
|
|
* // Lets create our dispatcher
|
|
|
|
*
|
|
|
|
* // 1. Dispatcher simulates our local system
|
|
|
|
* const localDispatcher = nope.dispatcher.getDispatcher({
|
|
|
|
* communicator,
|
|
|
|
* id: "local"
|
|
|
|
* }, {
|
|
|
|
* singleton: false,
|
|
|
|
* useBaseServices: false
|
|
|
|
* });
|
|
|
|
* ```
|
|
|
|
*
|
|
|
|
* > For Jupyter we need an extra async wrapper to wait for initalizing the dispatcher:
|
|
|
|
*
|
|
|
|
* see here for the details in Jupyter: https://n-riesco.github.io/ijavascript/doc/async.ipynb.html
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* ```javascript
|
|
|
|
* $$.async();
|
|
|
|
* // Lets wait for our element to be ready.
|
|
|
|
* localDispatcher.ready.waitFor().then($$.done);
|
|
|
|
* ```
|
|
|
|
*
|
|
|
|
* Now we want to listen to newly connected dispatchers. For this purpose, we create an observer, which will listen to changes.
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* ```javascript
|
|
|
|
* // Subscribe to changes
|
|
|
|
* const observer = localDispatcher.connectivityManager.dispatchers.onChange.subscribe(data => {
|
|
|
|
* // Log the changes
|
|
|
|
* console.log((new Date()).toISOString(),"onChange - listener");
|
|
|
|
* console.log("\tadded =", data.added);
|
|
|
|
* console.log("\tremoved =", data.removed);
|
|
|
|
* });
|
|
|
|
* ```
|
|
|
|
*
|
|
|
|
* Additionally we want to show the currently connected dispatchers. In this data the own dispatcher will **allways** be included:
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* ```javascript
|
|
|
|
* // Show our connected Dispatchers
|
|
|
|
* let connectedDispatchers = localDispatcher.connectivityManager.dispatchers.data.getContent();
|
|
|
|
* let localDispatcherIncluded = connectedDispatchers.includes(localDispatcher.id);
|
|
|
|
*
|
|
|
|
* // Now lets log our results.
|
|
|
|
* console.log("connectedDispatchers =", connectedDispatchers);
|
|
|
|
* console.log("localDispatcherIncluded =", localDispatcherIncluded);
|
|
|
|
* ```
|
|
|
|
*
|
|
|
|
* >```
|
|
|
|
* > connectedDispatchers = [ 'local' ]
|
|
|
|
* > localDispatcherIncluded = true
|
|
|
|
* >```
|
|
|
|
*
|
|
|
|
* Now that we have implemented our listeners and have seen the connected dispatchers (which is only the `"local"`-dispatchre), We will add an additional dispatcher. This should result in calling our `onChange`-listener. Additionally, we wait until our `remoteDispatcher` is initalized
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* ```javascript
|
|
|
|
* // 2. Dispatcher simulates our remote system
|
|
|
|
* const remoteDispatcher = nope.dispatcher.getDispatcher({
|
|
|
|
* communicator,
|
|
|
|
* id: "remote"
|
|
|
|
* }, {
|
|
|
|
* singleton: false,
|
|
|
|
* useBaseServices: false
|
|
|
|
* });
|
|
|
|
*
|
|
|
|
* ```
|
|
|
|
*
|
|
|
|
* >```
|
|
|
|
* > 2022-01-20T11:39:55.766Z onChange - listener
|
|
|
|
* > added = [ 'remote' ]
|
|
|
|
* > removed = []
|
|
|
|
* >```
|
|
|
|
*
|
|
|
|
* Now we want to see, which system is the current master. This should be our `local`.
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* ```javascript
|
|
|
|
* // We expect to be the master, because the localDispatcher has been created first.
|
|
|
|
* console.log("master =", localDispatcher.connectivityManager.master.id);
|
|
|
|
* ```
|
|
|
|
*
|
|
|
|
* > `master = local`
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* We can now force the remote dispatcher to be our master, by setting the master. (For this purpose we can later use a base service ==> then we just have to call the service)
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* ```javascript
|
|
|
|
* $$.async();
|
|
|
|
*
|
|
|
|
* remoteDispatcher.connectivityManager.isMaster = true;
|
|
|
|
* localDispatcher.connectivityManager.isMaster = false;
|
|
|
|
*
|
|
|
|
* // Our messaging is async ==> we wait an amount of time
|
|
|
|
* setTimeout(() => $$.done(),1000);
|
|
|
|
* ```
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* ```javascript
|
|
|
|
* // We expect the master to be the remote.
|
|
|
|
* console.log("master =", localDispatcher.connectivityManager.master.id);
|
|
|
|
* console.log("master-info =", localDispatcher.connectivityManager.master);
|
|
|
|
* ```
|
|
|
|
*
|
|
|
|
* >```
|
|
|
|
* > master = remote
|
|
|
|
* > master-info = {
|
|
|
|
* > id: 'remote',
|
|
|
|
* > env: 'javascript',
|
|
|
|
* > version: '1.0.0',
|
|
|
|
* > isMaster: true,
|
|
|
|
* > host: {
|
|
|
|
* > cores: 8,
|
|
|
|
* > cpu: {
|
|
|
|
* > model: 'Intel(R) Core(TM) i7-8565U CPU',
|
|
|
|
* > speed: 1992,
|
|
|
|
* > usage: 0.0038778477944740875
|
|
|
|
* > },
|
|
|
|
* > os: 'win32',
|
|
|
|
* > ram: { usedPerc: 0.362681220626356, free: 20676, total: 32442 },
|
|
|
|
* > name: 'nz-078'
|
|
|
|
* > },
|
|
|
|
* > pid: 18068,
|
|
|
|
* > timestamp: 1642678798813,
|
|
|
|
* > upTime: 3049,
|
|
|
|
* > status: 0
|
|
|
|
* > }
|
|
|
|
* >```
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* Now lets see what happens if we adapt the heartbeat intervall of our *local* instance. We want to receive every 50 ms a heartbeat:
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* ```javascript
|
|
|
|
* $$.async()
|
|
|
|
*
|
|
|
|
* const renderStatus = () => {
|
|
|
|
* console.log((new Date()).toISOString(),"master-info =", localDispatcher.connectivityManager.master.status)
|
|
|
|
* }
|
|
|
|
*
|
|
|
|
* setTimeout(renderStatus, 50);
|
|
|
|
* setTimeout(renderStatus, 750);
|
|
|
|
* setTimeout(renderStatus, 1500);
|
|
|
|
* setTimeout(renderStatus, 2500);
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* localDispatcher.connectivityManager.setTimings({
|
|
|
|
* // our system will send every 50 ms an heartbeat.
|
|
|
|
* sendAliveInterval: 250,
|
|
|
|
* // we will check that after
|
|
|
|
* checkInterval: 125,
|
|
|
|
* // will mark dispatchers as slow after not receiving heartbeats for 50ms
|
|
|
|
* slow: 500,
|
|
|
|
* // we will mark dispatchers with a warning flag after 50 ms
|
|
|
|
* warn: 1000,
|
|
|
|
* // we mark it as dead after 0.5 s
|
|
|
|
* dead: 2000,
|
|
|
|
* // We will remove the dispatcher after 1 s
|
|
|
|
* remove: 3000,
|
|
|
|
* });
|
|
|
|
*
|
|
|
|
* remoteDispatcher.connectivityManager.setTimings({
|
|
|
|
* // our system will send every 50 ms an heartbeat.
|
|
|
|
* sendAliveInterval: 5000,
|
|
|
|
* });
|
|
|
|
*
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* // We reset the timeouts.
|
|
|
|
* setTimeout(() => localDispatcher.connectivityManager.setTimings({}), 3000);
|
|
|
|
* setTimeout(() => remoteDispatcher.connectivityManager.setTimings({}), 3000);
|
|
|
|
* setTimeout(() => $$.done(), 5000);
|
|
|
|
*
|
|
|
|
* ```
|
|
|
|
*
|
|
|
|
* >```
|
|
|
|
* > 2022-01-20T11:40:01.089Z master-info = 0
|
|
|
|
* > 2022-01-20T11:40:01.789Z master-info = 1
|
|
|
|
* > 2022-01-20T11:40:02.536Z master-info = 2
|
|
|
|
* > 2022-01-20T11:40:03.543Z master-info = 3
|
|
|
|
* > 2022-01-20T11:40:03.977Z onChange - listener
|
|
|
|
* > added = []
|
|
|
|
* > removed = [ 'remote' ]
|
|
|
|
* > 2022-01-20T11:40:04.547Z onChange - listener
|
|
|
|
* > added = [ 'remote' ]
|
|
|
|
* > removed = []
|
|
|
|
* >```
|
|
|
|
*
|
2022-01-05 16:49:45 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
export {
|
|
|
|
INopeConnectivityManager,
|
|
|
|
INopeINopeConnectivityOptions,
|
2022-01-07 17:12:08 +00:00
|
|
|
INopeINopeConnectivityTimeOptions,
|
2022-01-10 06:52:05 +00:00
|
|
|
} from "../../types/nope/nopeConnectivityManager.interface";
|
2022-01-05 17:14:54 +00:00
|
|
|
export { NopeConnectivityManager } from "./ConnectivityManager";
|