Updating doku
This commit is contained in:
parent
79227d5950
commit
cf5aec61c0
@ -263,6 +263,8 @@ export class NopeConnectivityManager implements INopeConnectivityManager {
|
|||||||
*/
|
*/
|
||||||
public set isMaster(value: boolean) {
|
public set isMaster(value: boolean) {
|
||||||
this.__isMaster = value;
|
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) {
|
if (masters.length === 0) {
|
||||||
throw Error("No Master has been found !");
|
throw Error("No Master has been found !");
|
||||||
} else if (masters.length > 1) {
|
} 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];
|
return masters[0];
|
||||||
@ -375,6 +377,7 @@ export class NopeConnectivityManager implements INopeConnectivityManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await this.emitBonjour();
|
await this.emitBonjour();
|
||||||
|
await this._sendStatus();
|
||||||
|
|
||||||
this.ready.setContent(true);
|
this.ready.setContent(true);
|
||||||
}
|
}
|
||||||
@ -473,7 +476,10 @@ export class NopeConnectivityManager implements INopeConnectivityManager {
|
|||||||
* @memberof NopeConnectivityManager
|
* @memberof NopeConnectivityManager
|
||||||
*/
|
*/
|
||||||
protected _sendStatus(): void {
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -25,30 +25,9 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 2,
|
"execution_count": null,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"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)"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"source": [
|
"source": [
|
||||||
"// First lets install nope using npm\n",
|
"// First lets install nope using npm\n",
|
||||||
"const nope = require(\"../dist-nodejs/index.nodejs\")\n",
|
"const nope = require(\"../dist-nodejs/index.nodejs\")\n",
|
||||||
@ -88,6 +67,10 @@
|
|||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"source": [
|
"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",
|
"## Settings for creating:\n",
|
||||||
"\n",
|
"\n",
|
||||||
"The relevant Settings are described by the `INopeDispatcherOptions`. This options allows to define:\n",
|
"The relevant Settings are described by the `INopeDispatcherOptions`. This options allows to define:\n",
|
||||||
|
238
wiki/13-ConnectivityManager.ipynb
Normal file
238
wiki/13-ConnectivityManager.ipynb
Normal 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
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user