ClassCAD instance manager based on Node.js
This is a Node.js implementation using Socket.IO as the communications protocol.
Install
npm install awv3-node
Instance server
The instance manager represents a single endpoint where users connect in order to work with ClassCAD sessions. It may ship ClassCAD binaries and boot them up when starting, but it can also rely on seperate 'farms', servers that host ClassCAD processes.
const Server = require('awv3-node');
// Start server using custom ClassCAD binaries
let server = new Server({
// optional: zip that contains ClassCAD binaries
binaries: path.join(__dirname, '/binary.zip'),
// output folder where ClassCAD unpacks or is already present (in that case 'binaries' can be undefined)
output: path.join(__dirname, '/temp/'),
// name of ClassCAD runtime
executable: 'CCWebSocketService.exe',
// ClassCAD ini configuration
config: 'release.ini',
// number of ClassCAD instances, default: 1
instances: 3,
// public port, default: 8181
publicport: 8181,
// optional: internal port used for ClassCAD instances, default: random starting from 9000
privateport: 9191,
// allow socketIO to receive binary frames, default: true
binary: true,
// log ClassCAD instance stdout, default: false
stdout: false
});
server.start();
Using the server locally
You are not constrained to remote clients. You can use awv3-node locally as well by creating your own users which can execute and receive commands. The resulting task has a 'results' collection in which all the ClassCAD output will be put. Note that this excludes binary packages for now.
const User = require('awv3-node/modules/user');
// Wait for the server to boot up ...
server.start().then( () => {
// Create virtual user
let user = new User(server);
// Execute command
user.execute({ command: "Content", action: "getArticle" }).then(results => {
console.log(results)
});
});
Middleware
Middleware lets you hook into the servers task-stack. Whenever a user creates a task the resulting object is passed to the middleware, which can change the task or even discard it. Middleware is also allowed to create new tasks on the spot, which can be executed for the original user.
server.use(task => {
// Intercept 'Content' tasks
if (task.payload.command === 'Content') {
// Discard task, meaning it won't be processed at all
task.discard();
switch (task.payload.action) {
case 'getArticle':
// Return a promise. This is what the original receiver will resolve to in the end
// In this case we execute a different command in the users context, using the same task id
// The user will receive these results as if he requested them in the first place
return task.user.execute({ command: 'OpenStream', type: 'SAT', data }, task.id)));
break;
}
}
});
REST API
Log in a new user with the /login endpoint. The user will be treated in the same way as a socket.IO user. This has been abstracted in the awv3 library under the /communication/rest class which features the same API as the socketio or the signalr classes.
GET /login/:id
response => { op: 'system', status: [either 'permitted' or 'rejected'], id: [session ID] }
Log out with the /logout endpoint
GET /logout/:id
response => "disconnected"
Execute tasks with the /execute endpoint. Note that due to the limitations of REST, there is no server push available. Results will be sent in a single, complete chunk. Another limitation is that requests may time out, should the server be too busy. Such errors need to be handled by the client.
POST /execute/:id
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
method: "POST",
body: JSON.stringify(command)
response => Array of results
Custom endpoints
By default awv3-node will create three REST endpoints, /status for a JSON saity-check and /analyzer for debugging and prototyping and /health for load balancing.
It is possible to create more endpoints by using the express API.
// /test
server.express.get('/test', (req, res) => res.type("text/plain").send("hi there!");
// /color?value=#ff00ff
server.express.get('/color', (req, res) => res.type("text/plain").send(`You said: ${req.query.value}`);
// /arg/hello
server.express.get('/arg/:arg', (req, res) => res.type("text/plain").send(`You said: ${req.params.arg}`);
Custom load balancing
Override the [server.health] function to determine the instances health status. The function must return a HTTP status code. Load balancers can probe the /health endpoint to flag single instances as busy, which will remove them from the set of balanced instances until they become active again. The default criteria seeks to flag an instance as not-healthy (status code 503, service unavailable) if 90% of its ClassCAD sessions are busy.
server.health = function() {
let sessions = Array.from(server.sessions.values());
let busy = 0;
for (let session of sessions)
if (session.tasks.size > 0)
busy++;
// If 90% are busy, start flagging the instance as unhealthy
return busy > sessions.length * 0.9 ? 503 : 200;
};
Farms
A farm is a node instance that hosts a number of ClassCAD processes which can connect to an instance manager. It must bring either ClassCAD binaries or ship files inside the [output] folder.
const Farm = require('awv3-node/farm');
let farm = new Farm({
// optional: zip that contains ClassCAD binaries
binaries: path.join(__dirname, '/binary.zip'),
// output folder where ClassCAD unpacks or is already present (in that case 'binaries' can be undefined)
output: path.join(__dirname, '/temp/'),
// name of ClassCAD runtime
executable: 'CCWebSocketService.exe',
// local IP, if needed
ip: undefined,
// public port, default: 9292
publicport: 9292,
// maximal number of ClassCAD instances this farm can hold
instancesMax: 4
});
It has two APIs, one is functional:
// Boot up ClassCAD instances
farm.start({
ip: 'http://url', // Servers address
port: 9191, // Servers private port!
binary: true,
debug: false,
show: false,
instances: 1
});
The other depends on REST
GET /require/:ip/:port/:instances?
response => [ array of created processes ]