Updating doku

This commit is contained in:
Martin Karkowski 2022-01-20 11:02:38 +01:00
parent 79227d5950
commit cf5aec61c0
3 changed files with 252 additions and 25 deletions

View File

@ -263,6 +263,8 @@ export class NopeConnectivityManager implements INopeConnectivityManager {
*/
public set isMaster(value: boolean) {
this.__isMaster = value;
// We want to forward our new status.
this._sendStatus();
}
/**
@ -306,7 +308,7 @@ export class NopeConnectivityManager implements INopeConnectivityManager {
if (masters.length === 0) {
throw Error("No Master has been found !");
} else if (masters.length > 1) {
throw Error("Multiple Masters has been found!");
throw Error("Multiple Masters has been found!" + JSON.stringify(masters));
}
return masters[0];
@ -375,6 +377,7 @@ export class NopeConnectivityManager implements INopeConnectivityManager {
}
await this.emitBonjour();
await this._sendStatus();
this.ready.setContent(true);
}
@ -473,7 +476,10 @@ export class NopeConnectivityManager implements INopeConnectivityManager {
* @memberof NopeConnectivityManager
*/
protected _sendStatus(): void {
this._communicator.emit("StatusChanged", this.info);
// Test if we are connected
if (this._communicator.connected.getContent()) {
this._communicator.emit("StatusChanged", this.info);
}
}
/**

View File

@ -25,30 +25,9 @@
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": null,
"metadata": {},
"outputs": [
{
"ename": "SyntaxError",
"evalue": "Identifier 'nope' has already been declared",
"output_type": "error",
"traceback": [
"evalmachine.<anonymous>:1",
"// First lets install nope using npm",
"^",
"",
"SyntaxError: Identifier 'nope' has already been declared",
" at Script.runInThisContext (node:vm:129:12)",
" at Object.runInThisContext (node:vm:305:38)",
" at run ([eval]:1054:15)",
" at onRunRequest ([eval]:888:18)",
" at onMessage ([eval]:848:13)",
" at process.emit (node:events:390:28)",
" at emit (node:internal/child_process:915:12)",
" at processTicksAndRejections (node:internal/process/task_queues:84:21)"
]
}
],
"outputs": [],
"source": [
"// First lets install nope using npm\n",
"const nope = require(\"../dist-nodejs/index.nodejs\")\n",
@ -88,6 +67,10 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"### `getDispatcher`-function:\n",
"The `getDispatcher`- function automatically trys to return the dispatcher as `Singleton`. This means, that their will be exactly ***1*** dispatcher in a process. To receive a second dispatcher-instance (which is for performance reasons not recommend) in your process you must set the ``singleton``-flag to `false`\n",
"\n",
"\n",
"## Settings for creating:\n",
"\n",
"The relevant Settings are described by the `INopeDispatcherOptions`. This options allows to define:\n",

View File

@ -0,0 +1,238 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# NoPE - Connectivity Manager\n",
"\n",
"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.\n",
"\n",
"## Master\n",
"\n",
"Defaultly a `ConnectivityManager` is elected as `master`. The master is defined as the `ConnectivityManager` with the highest `upTime`. \n",
"\n",
"> Alternativly a master can be forced.\n",
"\n",
"## Synchronizing time\n",
"\n",
"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`))\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"// First lets install nope using npm\n",
"const nope = require(\"../dist-nodejs/index.nodejs\")\n",
"\n",
"// Create a communicator:\n",
"// We will use the event layer (which just runs internally)\n",
"const communicator = nope.getLayer(\"event\");\n",
"\n",
"// Lets create our dispatcher\n",
"\n",
"// 1. Dispatcher simulates our local system\n",
"const localDispatcher = nope.dispatcher.getDispatcher({\n",
" communicator,\n",
" id: \"local\"\n",
"}, null, false);"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"> For Jupyter we need an extra async wrapper to wait for initalizing the dispatcher:\n",
"\n",
"see here for the details in Jupyter: https://n-riesco.github.io/ijavascript/doc/async.ipynb.html"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"$$.async();\n",
"// Lets wait for our element to be ready.\n",
"localDispatcher.ready.waitFor().then($$.done);"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now we want to listen to newly connected dispatchers. For this purpose, we create an observer, which will listen to changes."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"// Subscribe to changes\n",
"const observer = localDispatcher.connectivityManager.dispatchers.onChange.subscribe(data => {\n",
" // Log the changes\n",
" console.log(\"onChange - listener\");\n",
" console.log(\"-------------------\");\n",
" console.log(\"added =\", data.added);\n",
" console.log(\"removed =\", data.removed);\n",
"});"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Additionally we want to show the currently connected dispatchers. In this data the own dispatcher will **allways** be included:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"// Show our connected Dispatchers\n",
"let connectedDispatchers = localDispatcher.connectivityManager.dispatchers.data.getContent();\n",
"let localDispatcherIncluded = connectedDispatchers.includes(localDispatcher.id);\n",
"\n",
"// Now lets log our results.\n",
"console.log(\"connectedDispatchers =\", connectedDispatchers);\n",
"console.log(\"localDispatcherIncluded =\", localDispatcherIncluded);"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"// 2. Dispatcher simulates our remote system\n",
"const remoteDispatcher = nope.dispatcher.getDispatcher({\n",
" communicator,\n",
" id: \"remote\"\n",
"}, null, false);\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now we want to see, which system is the current master. This should be our `local`."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"// We expect to be the master, because the localDispatcher has been created first.\n",
"console.log(\"master =\", localDispatcher.connectivityManager.master.id);"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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) "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"$$.async();\n",
"\n",
"remoteDispatcher.connectivityManager.isMaster = true;\n",
"localDispatcher.connectivityManager.isMaster = false;\n",
"\n",
"// Our messaging is async ==> we wait an amount of time\n",
"setTimeout($$.done,1000)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"// We expect the master to be the remote.\n",
"console.log(\"master =\", localDispatcher.connectivityManager.master.id);\n",
"console.log(\"master-info =\", localDispatcher.connectivityManager.master);"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now lets see what happens if we adapt the heartbeat intervall of our *local* instance. We want to receive every 50 ms a heartbeat:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"$$.async()\n",
"\n",
"localDispatcher.connectivityManager.setTimings({\n",
" // our system will send every 50 ms an heartbeat.\n",
" sendAliveInterval: 500,\n",
" // we will check that after\n",
" checkInterval: 25,\n",
" // will mark dispatchers as slow after not receiving heartbeats for 50ms\n",
" slow: 50,\n",
" // we will mark dispatchers with a warning flag after 50 ms\n",
" warn: 100,\n",
" // we mark it as dead after 0.5 s\n",
" dead: 500,\n",
" // We will remove the dispatcher after 1 s\n",
" remove: 1000,\n",
"});\n",
"\n",
"const renderStatus = () => {\n",
" console.log(\"master-info =\", localDispatcher.connectivityManager.master.status)\n",
"}\n",
"\n",
"setTimeout(renderStatus, 75);\n",
"setTimeout(renderStatus, 250);\n",
"setTimeout(renderStatus, 750);\n",
"\n",
"// We reset the timeouts.\n",
"setTimeout(() => localDispatcher.connectivityManager.setTimings({}), 1200);\n",
"setTimeout(() => $$.done(), 2000);\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "JavaScript (Node.js)",
"language": "javascript",
"name": "javascript"
},
"language_info": {
"file_extension": ".js",
"mimetype": "application/javascript",
"name": "javascript",
"version": "17.3.1"
}
},
"nbformat": 4,
"nbformat_minor": 2
}