Initial project structure
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
import * as amqp from "amqplib";
|
||||
|
||||
|
||||
export class CommandService {
|
||||
|
||||
|
||||
constructor(
|
||||
private channel: amqp.Channel
|
||||
){}
|
||||
|
||||
|
||||
|
||||
async send(
|
||||
queue:string,
|
||||
command:any
|
||||
){
|
||||
|
||||
|
||||
await this.channel.sendToQueue(
|
||||
|
||||
queue,
|
||||
|
||||
Buffer.from(
|
||||
JSON.stringify(command)
|
||||
)
|
||||
|
||||
);
|
||||
|
||||
|
||||
console.log(
|
||||
"Command gesendet:",
|
||||
command
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
import { Rabbit } from "./rabbit";
|
||||
import { WrapperManager } from "./wrapperManager";
|
||||
import { WrapperRegistry } from "./wrapperRegistry";
|
||||
import { ServerRegistry } from "./serverRegistry";
|
||||
import { ServerManager } from "./serverManager";
|
||||
import { PortManager } from "./portManager";
|
||||
import { ServerStatusManager } from "./serverStatusManager";
|
||||
import { ServerLifecycle } from "./serverLifecycle";
|
||||
|
||||
|
||||
const wrapperRegistry =
|
||||
new WrapperRegistry();
|
||||
|
||||
|
||||
|
||||
const serverRegistry =
|
||||
new ServerRegistry();
|
||||
|
||||
const portManager =
|
||||
new PortManager();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
async function main(){
|
||||
|
||||
|
||||
const rabbit =
|
||||
new Rabbit();
|
||||
|
||||
|
||||
|
||||
await rabbit.connect();
|
||||
|
||||
|
||||
|
||||
const wrapperManager =
|
||||
new WrapperManager(
|
||||
|
||||
rabbit,
|
||||
|
||||
wrapperRegistry,
|
||||
|
||||
serverRegistry
|
||||
|
||||
);
|
||||
|
||||
|
||||
|
||||
await wrapperManager.start();
|
||||
|
||||
|
||||
const serverManager =
|
||||
new ServerManager(
|
||||
|
||||
rabbit,
|
||||
|
||||
wrapperRegistry,
|
||||
|
||||
serverRegistry,
|
||||
|
||||
portManager
|
||||
|
||||
);
|
||||
|
||||
const lifecycle =
|
||||
new ServerLifecycle(
|
||||
|
||||
serverRegistry,
|
||||
|
||||
serverManager
|
||||
|
||||
);
|
||||
|
||||
|
||||
lifecycle.start();
|
||||
|
||||
const statusManager =
|
||||
new ServerStatusManager(
|
||||
|
||||
rabbit,
|
||||
|
||||
serverRegistry
|
||||
|
||||
);
|
||||
|
||||
|
||||
await statusManager.start();
|
||||
|
||||
|
||||
|
||||
|
||||
console.log(
|
||||
"Manager gestartet"
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
main();
|
||||
@@ -0,0 +1,70 @@
|
||||
const locations = [
|
||||
|
||||
"Crimson",
|
||||
"Azure",
|
||||
"Emerald",
|
||||
"Amber",
|
||||
"Sapphire",
|
||||
"Ruby",
|
||||
"Obsidian",
|
||||
"Pearl",
|
||||
"Onyx",
|
||||
"Ivory",
|
||||
"Copper",
|
||||
"Platinum",
|
||||
"Titanium",
|
||||
"Neon",
|
||||
"Shadow",
|
||||
"Frost",
|
||||
"Solar",
|
||||
"Void",
|
||||
"Storm",
|
||||
"Ocean"
|
||||
|
||||
];
|
||||
|
||||
|
||||
|
||||
export function generateServerName(
|
||||
existingNames:string[]
|
||||
){
|
||||
|
||||
|
||||
for(const location of locations){
|
||||
|
||||
|
||||
const name =
|
||||
"[EU] druecktieR | " + location;
|
||||
|
||||
|
||||
|
||||
if(!existingNames.includes(name)){
|
||||
|
||||
|
||||
return name;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Falls alle Namen vergeben sind
|
||||
*/
|
||||
|
||||
|
||||
return (
|
||||
|
||||
"[EU] druecktieR | " +
|
||||
|
||||
Math.random()
|
||||
.toString(36)
|
||||
.substring(2,6)
|
||||
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
export class PortManager {
|
||||
|
||||
|
||||
|
||||
private usedPorts =
|
||||
new Set<number>();
|
||||
|
||||
|
||||
|
||||
private usedQueryPorts =
|
||||
new Set<number>();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private gamePortStart =
|
||||
7777;
|
||||
|
||||
|
||||
private queryPortStart =
|
||||
27015;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
allocate(){
|
||||
|
||||
|
||||
|
||||
let port =
|
||||
this.gamePortStart;
|
||||
|
||||
|
||||
|
||||
while(
|
||||
this.usedPorts.has(port)
|
||||
){
|
||||
|
||||
port++;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
let queryPort =
|
||||
this.queryPortStart;
|
||||
|
||||
|
||||
|
||||
while(
|
||||
this.usedQueryPorts.has(queryPort)
|
||||
){
|
||||
|
||||
queryPort++;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
this.usedPorts.add(
|
||||
port
|
||||
);
|
||||
|
||||
|
||||
this.usedQueryPorts.add(
|
||||
queryPort
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
return {
|
||||
|
||||
|
||||
port,
|
||||
|
||||
|
||||
queryPort
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
release(
|
||||
port:number,
|
||||
queryPort:number
|
||||
){
|
||||
|
||||
|
||||
this.usedPorts.delete(
|
||||
port
|
||||
);
|
||||
|
||||
|
||||
this.usedQueryPorts.delete(
|
||||
queryPort
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import * as amqp from "amqplib";
|
||||
|
||||
|
||||
export class Rabbit {
|
||||
|
||||
connection!: amqp.ChannelModel;
|
||||
|
||||
channel!: amqp.Channel;
|
||||
|
||||
|
||||
async connect(){
|
||||
|
||||
this.connection =
|
||||
await amqp.connect(
|
||||
"amqp://manager:gigi1337@192.168.1.74"
|
||||
);
|
||||
|
||||
|
||||
this.channel =
|
||||
await this.connection.createChannel();
|
||||
|
||||
|
||||
console.log(
|
||||
"RabbitMQ verbunden"
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,670 @@
|
||||
import { ServerRegistry } from "./serverRegistry";
|
||||
import { ServerManager } from "./serverManager";
|
||||
|
||||
|
||||
export class ServerLifecycle {
|
||||
|
||||
|
||||
private creatingServers = 0;
|
||||
|
||||
|
||||
private emptyTimeout = 300000; // 5 Minuten
|
||||
|
||||
|
||||
private lastScaleUp = 0;
|
||||
|
||||
|
||||
private scaleCooldown = 60000; // 1 Minute
|
||||
|
||||
|
||||
|
||||
|
||||
constructor(
|
||||
|
||||
private serverRegistry: ServerRegistry,
|
||||
|
||||
private serverManager: ServerManager,
|
||||
|
||||
private desiredServers:number = 1
|
||||
|
||||
){}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
start(){
|
||||
|
||||
|
||||
console.log(
|
||||
"Server Lifecycle gestartet"
|
||||
);
|
||||
|
||||
|
||||
setInterval(()=>{
|
||||
|
||||
|
||||
this.checkServers();
|
||||
|
||||
|
||||
},10000);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
setDesiredServers(count:number){
|
||||
|
||||
|
||||
console.log(
|
||||
"Neue Soll Server Anzahl:",
|
||||
count
|
||||
);
|
||||
|
||||
|
||||
this.desiredServers =
|
||||
count;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
checkServers(){
|
||||
|
||||
|
||||
const servers =
|
||||
this.serverRegistry.getAll();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
STATUS AKTUALISIEREN
|
||||
*/
|
||||
|
||||
|
||||
for(const server of servers){
|
||||
|
||||
|
||||
|
||||
switch(server.status){
|
||||
|
||||
|
||||
|
||||
case "STARTING":
|
||||
|
||||
|
||||
if(server.pid){
|
||||
|
||||
|
||||
server.status =
|
||||
"RUNNING";
|
||||
|
||||
|
||||
this.serverRegistry.update(
|
||||
server
|
||||
);
|
||||
|
||||
|
||||
console.log(
|
||||
"Server RUNNING:",
|
||||
server.name,
|
||||
"PID:",
|
||||
server.pid
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
case "RUNNING":
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Crash Detection
|
||||
*/
|
||||
|
||||
|
||||
if(
|
||||
Date.now() -
|
||||
server.lastSeen
|
||||
>
|
||||
30000
|
||||
){
|
||||
|
||||
|
||||
server.status =
|
||||
"CRASHED";
|
||||
|
||||
|
||||
this.serverRegistry.update(
|
||||
server
|
||||
);
|
||||
|
||||
|
||||
console.log(
|
||||
"Server abgestürzt:",
|
||||
server.name
|
||||
);
|
||||
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Leere Server tracken
|
||||
*/
|
||||
|
||||
|
||||
if(server.players === 0){
|
||||
|
||||
|
||||
|
||||
if(!server.emptySince){
|
||||
|
||||
|
||||
server.emptySince =
|
||||
Date.now();
|
||||
|
||||
|
||||
this.serverRegistry.update(
|
||||
server
|
||||
);
|
||||
|
||||
|
||||
console.log(
|
||||
"Server leer:",
|
||||
server.name
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
else{
|
||||
|
||||
|
||||
if(server.emptySince){
|
||||
|
||||
|
||||
console.log(
|
||||
"Server wieder aktiv:",
|
||||
server.name
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
server.emptySince =
|
||||
undefined;
|
||||
|
||||
|
||||
|
||||
this.serverRegistry.update(
|
||||
server
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
break;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
case "CRASHED":
|
||||
|
||||
|
||||
|
||||
console.log(
|
||||
"Server abgestürzt erkannt:",
|
||||
server.name
|
||||
);
|
||||
|
||||
|
||||
break;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
AKTIVE SERVER
|
||||
*/
|
||||
|
||||
|
||||
const activeServers =
|
||||
|
||||
this.serverRegistry
|
||||
.getAll()
|
||||
.filter(
|
||||
|
||||
server =>
|
||||
server.status === "RUNNING" ||
|
||||
server.status === "STARTING"
|
||||
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
SLOT AUSLASTUNG
|
||||
*/
|
||||
|
||||
|
||||
const runningServers =
|
||||
|
||||
activeServers.filter(
|
||||
|
||||
server =>
|
||||
server.status === "RUNNING"
|
||||
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const totalPlayers =
|
||||
|
||||
runningServers.reduce(
|
||||
|
||||
(sum,server)=>
|
||||
sum + server.players,
|
||||
|
||||
0
|
||||
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const totalSlots =
|
||||
|
||||
runningServers.reduce(
|
||||
|
||||
(sum,server)=>
|
||||
sum + server.maxPlayers,
|
||||
|
||||
0
|
||||
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const usage =
|
||||
|
||||
totalSlots > 0
|
||||
|
||||
?
|
||||
|
||||
totalPlayers / totalSlots
|
||||
|
||||
:
|
||||
|
||||
0;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
console.log(
|
||||
|
||||
"Kapazität:",
|
||||
|
||||
`${totalPlayers}/${totalSlots}`,
|
||||
|
||||
`${(usage * 100).toFixed(1)}%`
|
||||
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
MINDEST SERVER ANZAHL
|
||||
*/
|
||||
|
||||
|
||||
const totalActive =
|
||||
|
||||
activeServers.length +
|
||||
this.creatingServers;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if(
|
||||
totalActive <
|
||||
this.desiredServers
|
||||
){
|
||||
|
||||
|
||||
|
||||
const missing =
|
||||
|
||||
this.desiredServers -
|
||||
totalActive;
|
||||
|
||||
|
||||
|
||||
this.startServers(
|
||||
missing
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
SCALE UP BEI 90%
|
||||
*/
|
||||
|
||||
|
||||
const shouldScale =
|
||||
|
||||
|
||||
runningServers.length > 0 &&
|
||||
|
||||
|
||||
this.creatingServers === 0 &&
|
||||
|
||||
|
||||
usage >= 0.90 &&
|
||||
|
||||
|
||||
Date.now() -
|
||||
this.lastScaleUp
|
||||
>
|
||||
this.scaleCooldown;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if(shouldScale){
|
||||
|
||||
|
||||
|
||||
console.log(
|
||||
|
||||
"Auslastung hoch - starte zusätzlichen Server"
|
||||
|
||||
);
|
||||
|
||||
|
||||
|
||||
this.lastScaleUp =
|
||||
Date.now();
|
||||
|
||||
|
||||
|
||||
this.startServers(1);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
LEERE SERVER ENTFERNEN
|
||||
*/
|
||||
|
||||
|
||||
|
||||
const removableServers =
|
||||
|
||||
|
||||
activeServers
|
||||
|
||||
.filter(server =>
|
||||
|
||||
|
||||
server.status === "RUNNING" &&
|
||||
|
||||
|
||||
server.players === 0 &&
|
||||
|
||||
|
||||
server.emptySince &&
|
||||
|
||||
|
||||
Date.now() -
|
||||
server.emptySince
|
||||
>
|
||||
this.emptyTimeout
|
||||
|
||||
|
||||
)
|
||||
|
||||
.sort(
|
||||
|
||||
(a,b)=>
|
||||
|
||||
(a.emptySince ?? 0) -
|
||||
(b.emptySince ?? 0)
|
||||
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
for(const server of removableServers){
|
||||
|
||||
|
||||
|
||||
const activeCount =
|
||||
|
||||
this.serverRegistry
|
||||
.getAll()
|
||||
.filter(
|
||||
|
||||
s =>
|
||||
s.status === "RUNNING" ||
|
||||
s.status === "STARTING"
|
||||
|
||||
)
|
||||
.length;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if(
|
||||
activeCount <=
|
||||
this.desiredServers
|
||||
){
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
console.log(
|
||||
|
||||
"Stoppe leeren Server:",
|
||||
|
||||
server.name
|
||||
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
this.serverManager.stopServer(
|
||||
|
||||
server.id
|
||||
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private startServers(count:number){
|
||||
|
||||
|
||||
|
||||
console.log(
|
||||
|
||||
"Starte Server:",
|
||||
|
||||
count
|
||||
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
for(
|
||||
|
||||
let i = 0;
|
||||
|
||||
i < count;
|
||||
|
||||
i++
|
||||
|
||||
){
|
||||
|
||||
|
||||
|
||||
this.creatingServers++;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
this.serverManager
|
||||
.createServer()
|
||||
|
||||
.then(()=>{
|
||||
|
||||
|
||||
this.creatingServers--;
|
||||
|
||||
|
||||
})
|
||||
|
||||
|
||||
.catch(err=>{
|
||||
|
||||
|
||||
|
||||
this.creatingServers--;
|
||||
|
||||
|
||||
|
||||
console.error(
|
||||
|
||||
"Server Erstellung fehlgeschlagen:",
|
||||
|
||||
err
|
||||
|
||||
);
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
import { ServerRegistry } from "./serverRegistry";
|
||||
import { Rabbit } from "./rabbit";
|
||||
|
||||
|
||||
export class ServerLifecycleManager {
|
||||
|
||||
|
||||
constructor(
|
||||
|
||||
private serverRegistry: ServerRegistry,
|
||||
|
||||
private rabbit: Rabbit
|
||||
|
||||
){}
|
||||
|
||||
|
||||
|
||||
start(){
|
||||
|
||||
|
||||
console.log(
|
||||
"Server Lifecycle Manager gestartet"
|
||||
);
|
||||
|
||||
|
||||
setInterval(()=>{
|
||||
|
||||
|
||||
this.checkServers();
|
||||
|
||||
|
||||
},5000);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
checkServers(){
|
||||
|
||||
|
||||
const servers =
|
||||
this.serverRegistry.getAll();
|
||||
|
||||
|
||||
|
||||
for(const server of servers){
|
||||
|
||||
|
||||
|
||||
switch(server.status){
|
||||
|
||||
|
||||
|
||||
case "STARTING":
|
||||
|
||||
|
||||
this.checkStarting(
|
||||
server
|
||||
);
|
||||
|
||||
break;
|
||||
|
||||
|
||||
|
||||
case "RUNNING":
|
||||
|
||||
|
||||
this.checkRunning(
|
||||
server
|
||||
);
|
||||
|
||||
break;
|
||||
|
||||
|
||||
|
||||
case "CRASHED":
|
||||
|
||||
|
||||
this.checkOffline(
|
||||
server
|
||||
);
|
||||
|
||||
break;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
checkStarting(server:any){
|
||||
|
||||
|
||||
// kommt später
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
checkRunning(server:any){
|
||||
|
||||
|
||||
// kommt später
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
checkOffline(server:any){
|
||||
|
||||
|
||||
// kommt später
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,355 @@
|
||||
import { Rabbit } from "./rabbit";
|
||||
import { WrapperRegistry } from "./wrapperRegistry";
|
||||
import { ServerRegistry, ServerInfo } from "./serverRegistry";
|
||||
import { randomUUID } from "crypto";
|
||||
import { generateServerName } from "./nameGenerator";
|
||||
import { PortManager } from "./portManager";
|
||||
|
||||
|
||||
export class ServerManager {
|
||||
|
||||
|
||||
constructor(
|
||||
|
||||
private rabbit:Rabbit,
|
||||
|
||||
private wrapperRegistry:WrapperRegistry,
|
||||
|
||||
private serverRegistry:ServerRegistry,
|
||||
|
||||
private portManager:PortManager
|
||||
|
||||
){}
|
||||
|
||||
|
||||
|
||||
async createServer(){
|
||||
|
||||
|
||||
/*
|
||||
freien Wrapper suchen
|
||||
*/
|
||||
|
||||
|
||||
const wrapper =
|
||||
this.findBestWrapper();
|
||||
|
||||
|
||||
|
||||
if(!wrapper){
|
||||
|
||||
console.log(
|
||||
"Kein Wrapper verfügbar"
|
||||
);
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const serverId =
|
||||
"srv-" +
|
||||
randomUUID()
|
||||
.slice(0,8);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const serverName =
|
||||
generateServerName(
|
||||
|
||||
this.serverRegistry
|
||||
.getAll()
|
||||
.map(server => server.name)
|
||||
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Ports reservieren
|
||||
*/
|
||||
|
||||
|
||||
const ports =
|
||||
this.portManager.allocate();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const server:ServerInfo = {
|
||||
|
||||
|
||||
id:
|
||||
serverId,
|
||||
|
||||
|
||||
name:
|
||||
serverName,
|
||||
|
||||
|
||||
wrapperId:
|
||||
wrapper.id,
|
||||
|
||||
|
||||
status:
|
||||
"STARTING",
|
||||
|
||||
|
||||
|
||||
port:
|
||||
ports.port,
|
||||
|
||||
|
||||
|
||||
queryPort:
|
||||
ports.queryPort,
|
||||
|
||||
|
||||
|
||||
players:
|
||||
0,
|
||||
|
||||
|
||||
maxPlayers:
|
||||
21,
|
||||
|
||||
created:
|
||||
Date.now(),
|
||||
|
||||
lastSeen:
|
||||
Date.now()
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Registry Eintrag
|
||||
*/
|
||||
|
||||
|
||||
this.serverRegistry.add(
|
||||
server
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Command an Wrapper
|
||||
*/
|
||||
|
||||
|
||||
const command = {
|
||||
|
||||
|
||||
type:
|
||||
"server.start",
|
||||
|
||||
|
||||
|
||||
serverId,
|
||||
|
||||
|
||||
name:
|
||||
serverName,
|
||||
|
||||
|
||||
|
||||
port:
|
||||
ports.port,
|
||||
|
||||
|
||||
|
||||
queryPort:
|
||||
ports.queryPort
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const sent =
|
||||
this.rabbit.channel.sendToQueue(
|
||||
|
||||
wrapper.commandQueue,
|
||||
|
||||
Buffer.from(
|
||||
JSON.stringify(command)
|
||||
)
|
||||
|
||||
);
|
||||
|
||||
|
||||
if(!sent){
|
||||
|
||||
throw new Error(
|
||||
"Server Start Command konnte nicht gesendet werden"
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
console.log(
|
||||
|
||||
"Server Start gesendet:",
|
||||
|
||||
server
|
||||
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
async stopServer(serverId:string){
|
||||
|
||||
|
||||
const server =
|
||||
this.serverRegistry.get(
|
||||
serverId
|
||||
);
|
||||
|
||||
|
||||
if(!server){
|
||||
|
||||
console.log(
|
||||
"Server nicht gefunden:",
|
||||
serverId
|
||||
);
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
const wrapper =
|
||||
this.wrapperRegistry.get(
|
||||
server.wrapperId
|
||||
);
|
||||
|
||||
|
||||
if(!wrapper){
|
||||
|
||||
console.log(
|
||||
"Wrapper nicht gefunden:",
|
||||
server.wrapperId
|
||||
);
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
const command = {
|
||||
|
||||
|
||||
type:
|
||||
"server.stop",
|
||||
|
||||
|
||||
serverId
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
this.rabbit.channel.sendToQueue(
|
||||
|
||||
wrapper.commandQueue,
|
||||
|
||||
Buffer.from(
|
||||
JSON.stringify(command)
|
||||
)
|
||||
|
||||
);
|
||||
|
||||
|
||||
|
||||
server.status =
|
||||
"STOPPING";
|
||||
|
||||
|
||||
this.serverRegistry.update(
|
||||
server
|
||||
);
|
||||
|
||||
|
||||
|
||||
console.log(
|
||||
"Server Stop gesendet:",
|
||||
server.name
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
findBestWrapper(){
|
||||
|
||||
|
||||
|
||||
const wrappers =
|
||||
this.wrapperRegistry.getAll()
|
||||
.filter(
|
||||
w=>w.online
|
||||
);
|
||||
|
||||
|
||||
|
||||
if(wrappers.length===0)
|
||||
return undefined;
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Einfachste Version:
|
||||
|
||||
wenigste Server
|
||||
*/
|
||||
|
||||
|
||||
wrappers.sort(
|
||||
|
||||
(a,b)=>
|
||||
|
||||
a.servers.length -
|
||||
b.servers.length
|
||||
|
||||
);
|
||||
|
||||
|
||||
|
||||
return wrappers[0];
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,378 @@
|
||||
export interface ServerInfo {
|
||||
|
||||
|
||||
id:string;
|
||||
|
||||
name:string;
|
||||
|
||||
wrapperId:string;
|
||||
|
||||
|
||||
status:
|
||||
| "STARTING"
|
||||
| "RUNNING"
|
||||
| "STOPPING"
|
||||
| "CRASHED";
|
||||
|
||||
|
||||
port:number;
|
||||
|
||||
queryPort:number;
|
||||
|
||||
|
||||
pid?:number;
|
||||
|
||||
|
||||
players:number;
|
||||
|
||||
|
||||
maxPlayers:number;
|
||||
|
||||
|
||||
emptySince?:number;
|
||||
|
||||
|
||||
created:number;
|
||||
|
||||
|
||||
lastSeen:number;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
export class ServerRegistry {
|
||||
|
||||
|
||||
|
||||
private servers =
|
||||
new Map<string, ServerInfo>();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
add(server:ServerInfo){
|
||||
|
||||
|
||||
this.servers.set(
|
||||
|
||||
server.id,
|
||||
|
||||
server
|
||||
|
||||
);
|
||||
|
||||
|
||||
console.log(
|
||||
"Server registriert:",
|
||||
server
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
get(id:string){
|
||||
|
||||
|
||||
return this.servers.get(
|
||||
id
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
getAll(){
|
||||
|
||||
|
||||
return Array.from(
|
||||
|
||||
this.servers.values()
|
||||
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
update(server:ServerInfo){
|
||||
|
||||
|
||||
this.servers.set(
|
||||
|
||||
server.id,
|
||||
|
||||
server
|
||||
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
remove(id:string){
|
||||
|
||||
|
||||
this.servers.delete(
|
||||
id
|
||||
);
|
||||
|
||||
|
||||
console.log(
|
||||
"Server entfernt:",
|
||||
id
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
list(){
|
||||
|
||||
|
||||
console.log(
|
||||
"\n=== SERVER ÜBERSICHT ==="
|
||||
);
|
||||
|
||||
|
||||
|
||||
const servers =
|
||||
this.getAll();
|
||||
|
||||
|
||||
|
||||
|
||||
if(servers.length === 0){
|
||||
|
||||
|
||||
console.log(
|
||||
"Keine Server vorhanden"
|
||||
);
|
||||
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
for(const server of servers){
|
||||
|
||||
|
||||
console.log({
|
||||
|
||||
|
||||
id:
|
||||
server.id,
|
||||
|
||||
|
||||
name:
|
||||
server.name,
|
||||
|
||||
|
||||
wrapper:
|
||||
server.wrapperId,
|
||||
|
||||
|
||||
status:
|
||||
server.status,
|
||||
|
||||
|
||||
port:
|
||||
server.port,
|
||||
|
||||
|
||||
queryPort:
|
||||
server.queryPort,
|
||||
|
||||
|
||||
players:
|
||||
`${server.players}/${server.maxPlayers}`
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
syncWrapperStatus(data:any){
|
||||
|
||||
if(!data.servers)
|
||||
return;
|
||||
|
||||
|
||||
|
||||
const runningServerIds =
|
||||
new Set(
|
||||
data.servers.map(
|
||||
(srv:any)=>srv.id
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
|
||||
for(const server of this.servers.values()){
|
||||
|
||||
|
||||
if(
|
||||
server.wrapperId === data.wrapperId &&
|
||||
!runningServerIds.has(server.id)
|
||||
){
|
||||
|
||||
|
||||
if(server.status === "STOPPING"){
|
||||
|
||||
|
||||
console.log(
|
||||
"Server sauber beendet:",
|
||||
server.name
|
||||
);
|
||||
|
||||
|
||||
this.remove(
|
||||
server.id
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
else if(server.status === "RUNNING"){
|
||||
|
||||
|
||||
server.status =
|
||||
"CRASHED";
|
||||
|
||||
|
||||
this.servers.set(
|
||||
server.id,
|
||||
server
|
||||
);
|
||||
|
||||
|
||||
console.log(
|
||||
"Server abgestürzt:",
|
||||
server.name
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
for(const srv of data.servers){
|
||||
const server =
|
||||
this.servers.get(
|
||||
srv.id
|
||||
);
|
||||
|
||||
|
||||
if(!server)
|
||||
continue;
|
||||
|
||||
|
||||
|
||||
server.pid =
|
||||
srv.pid;
|
||||
|
||||
server.players =
|
||||
srv.players ?? 0;
|
||||
|
||||
|
||||
server.maxPlayers =
|
||||
srv.maxPlayers ?? 0;
|
||||
|
||||
|
||||
server.lastSeen =
|
||||
Date.now();
|
||||
|
||||
|
||||
|
||||
const oldStatus =
|
||||
server.status;
|
||||
|
||||
|
||||
|
||||
if(srv.alive){
|
||||
|
||||
server.status =
|
||||
"RUNNING";
|
||||
|
||||
}
|
||||
else if(
|
||||
server.status === "RUNNING"
|
||||
){
|
||||
|
||||
server.status =
|
||||
"CRASHED";
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
if(oldStatus !== server.status){
|
||||
|
||||
console.log(
|
||||
"Status Änderung:",
|
||||
server.name,
|
||||
oldStatus,
|
||||
"->",
|
||||
server.status
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
this.servers.set(
|
||||
server.id,
|
||||
server
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,238 @@
|
||||
import { Rabbit } from "./rabbit";
|
||||
import { ServerRegistry } from "./serverRegistry";
|
||||
|
||||
|
||||
|
||||
export class ServerStatusManager {
|
||||
|
||||
|
||||
|
||||
constructor(
|
||||
|
||||
private rabbit:Rabbit,
|
||||
|
||||
private registry:ServerRegistry
|
||||
|
||||
){}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
async start(){
|
||||
|
||||
|
||||
|
||||
const queue =
|
||||
"server.status";
|
||||
|
||||
|
||||
|
||||
await this.rabbit.channel.assertQueue(
|
||||
queue
|
||||
);
|
||||
|
||||
|
||||
|
||||
console.log(
|
||||
"Warte auf Server Status..."
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
this.rabbit.channel.consume(
|
||||
|
||||
queue,
|
||||
|
||||
|
||||
msg=>{
|
||||
|
||||
|
||||
if(!msg)
|
||||
return;
|
||||
|
||||
|
||||
|
||||
|
||||
const data =
|
||||
JSON.parse(
|
||||
msg.content.toString()
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
switch(data.type){
|
||||
|
||||
|
||||
|
||||
case "server.started":
|
||||
|
||||
|
||||
|
||||
this.serverStarted(
|
||||
data
|
||||
);
|
||||
|
||||
|
||||
break;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
case "server.stopped":
|
||||
|
||||
|
||||
|
||||
this.serverStopped(
|
||||
data
|
||||
);
|
||||
|
||||
|
||||
break;
|
||||
|
||||
|
||||
|
||||
|
||||
default:
|
||||
|
||||
|
||||
console.log(
|
||||
"Unbekannter Server Status:",
|
||||
data
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
this.rabbit.channel.ack(
|
||||
msg
|
||||
);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
serverStarted(data:any){
|
||||
|
||||
|
||||
|
||||
const server =
|
||||
this.registry.get(
|
||||
data.serverId
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
if(!server)
|
||||
return;
|
||||
|
||||
|
||||
|
||||
|
||||
server.status =
|
||||
"RUNNING";
|
||||
|
||||
|
||||
|
||||
|
||||
this.registry.update(
|
||||
server
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
console.log(
|
||||
|
||||
"Server läuft:",
|
||||
|
||||
server.name
|
||||
|
||||
);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
serverStopped(data:any){
|
||||
|
||||
|
||||
|
||||
const server =
|
||||
this.registry.get(
|
||||
data.serverId
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if(!server)
|
||||
return;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
server.status =
|
||||
"CRASHED";
|
||||
|
||||
|
||||
|
||||
|
||||
this.registry.update(
|
||||
server
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
console.log(
|
||||
|
||||
"Server crashed:",
|
||||
|
||||
server.name
|
||||
|
||||
);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
export interface Wrapper {
|
||||
|
||||
id: string;
|
||||
|
||||
hostname: string;
|
||||
|
||||
cpu: number;
|
||||
|
||||
ram: number;
|
||||
|
||||
lastHeartbeat: number;
|
||||
|
||||
commandQueue: string;
|
||||
|
||||
}
|
||||
|
||||
|
||||
export interface Message {
|
||||
|
||||
type: string;
|
||||
|
||||
[key:string]: any;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,615 @@
|
||||
import { Rabbit } from "./rabbit";
|
||||
import { randomUUID } from "crypto";
|
||||
import { WrapperRegistry, WrapperInfo } from "./wrapperRegistry";
|
||||
import { ServerRegistry } from "./serverRegistry";
|
||||
|
||||
|
||||
export class WrapperManager {
|
||||
|
||||
|
||||
constructor(
|
||||
private rabbit: Rabbit,
|
||||
private registry: WrapperRegistry,
|
||||
private serverRegistry: ServerRegistry
|
||||
){
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
async start(){
|
||||
|
||||
|
||||
const queue =
|
||||
"wrapper.register";
|
||||
|
||||
|
||||
|
||||
await this.rabbit.channel.assertQueue(
|
||||
queue
|
||||
);
|
||||
|
||||
|
||||
|
||||
console.log(
|
||||
"Warte auf Wrapper..."
|
||||
);
|
||||
|
||||
|
||||
|
||||
this.rabbit.channel.consume(
|
||||
|
||||
queue,
|
||||
|
||||
async msg => {
|
||||
|
||||
|
||||
if(!msg)
|
||||
return;
|
||||
|
||||
|
||||
|
||||
const data =
|
||||
JSON.parse(
|
||||
msg.content.toString()
|
||||
);
|
||||
|
||||
|
||||
|
||||
switch(data.type){
|
||||
|
||||
|
||||
|
||||
case "wrapper.register":
|
||||
|
||||
|
||||
await this.register(
|
||||
data
|
||||
);
|
||||
|
||||
|
||||
break;
|
||||
|
||||
|
||||
|
||||
case "wrapper.heartbeat":
|
||||
|
||||
|
||||
this.heartbeat(
|
||||
data
|
||||
);
|
||||
|
||||
|
||||
break;
|
||||
|
||||
|
||||
|
||||
case "wrapper.status":
|
||||
|
||||
|
||||
this.registry.updateStatus(
|
||||
data
|
||||
);
|
||||
|
||||
this.serverRegistry.syncWrapperStatus(
|
||||
data
|
||||
);
|
||||
|
||||
|
||||
break;
|
||||
|
||||
|
||||
|
||||
default:
|
||||
|
||||
|
||||
console.log(
|
||||
"Unbekannte Nachricht:",
|
||||
data
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
this.rabbit.channel.ack(
|
||||
msg
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Offline Check
|
||||
|
||||
setInterval(()=>{
|
||||
|
||||
|
||||
this.registry.checkOffline(
|
||||
30000
|
||||
);
|
||||
|
||||
|
||||
},5000);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
async register(data:any){
|
||||
|
||||
|
||||
|
||||
let id =
|
||||
data.wrapperId;
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Neue Wrapper bekommen eine ID
|
||||
*/
|
||||
|
||||
if(!id){
|
||||
|
||||
|
||||
id =
|
||||
"wrp-" +
|
||||
randomUUID()
|
||||
.slice(0,8);
|
||||
|
||||
|
||||
|
||||
console.log(
|
||||
"Neue Wrapper ID:",
|
||||
id
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const existing =
|
||||
this.registry.get(
|
||||
id
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Wrapper war schon bekannt
|
||||
*/
|
||||
|
||||
if(existing){
|
||||
|
||||
|
||||
existing.online = true;
|
||||
|
||||
|
||||
existing.lastHeartbeat =
|
||||
Date.now();
|
||||
|
||||
|
||||
|
||||
existing.hostname =
|
||||
data.hostname;
|
||||
|
||||
|
||||
|
||||
existing.cpu =
|
||||
data.cpu;
|
||||
|
||||
|
||||
|
||||
existing.ram =
|
||||
data.ram;
|
||||
|
||||
|
||||
|
||||
existing.ramFree =
|
||||
data.ramFree ?? data.ram;
|
||||
|
||||
|
||||
|
||||
existing.servers =
|
||||
data.servers ?? existing.servers;
|
||||
|
||||
|
||||
|
||||
this.registry.update(existing);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Neuer Wrapper
|
||||
*/
|
||||
|
||||
else {
|
||||
|
||||
|
||||
|
||||
const commandQueue =
|
||||
`wrapper.${id}.commands`;
|
||||
|
||||
|
||||
|
||||
|
||||
await this.rabbit.channel.assertQueue(
|
||||
commandQueue
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const wrapper:WrapperInfo = {
|
||||
|
||||
|
||||
id,
|
||||
|
||||
|
||||
hostname:
|
||||
data.hostname,
|
||||
|
||||
|
||||
|
||||
commandQueue,
|
||||
|
||||
|
||||
|
||||
cpu:
|
||||
data.cpu,
|
||||
|
||||
|
||||
|
||||
ram:
|
||||
data.ram,
|
||||
|
||||
|
||||
|
||||
ramFree:
|
||||
data.ram,
|
||||
|
||||
|
||||
|
||||
servers:
|
||||
[],
|
||||
|
||||
|
||||
|
||||
online:true,
|
||||
|
||||
|
||||
|
||||
lastHeartbeat:
|
||||
Date.now()
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
this.registry.add(
|
||||
wrapper
|
||||
);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Antwort zurück an Wrapper
|
||||
*/
|
||||
|
||||
|
||||
const wrapper =
|
||||
this.registry.get(
|
||||
id
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
if(data.replyQueue){
|
||||
|
||||
|
||||
this.rabbit.channel.sendToQueue(
|
||||
|
||||
|
||||
data.replyQueue,
|
||||
|
||||
|
||||
Buffer.from(
|
||||
|
||||
JSON.stringify({
|
||||
|
||||
|
||||
type:
|
||||
"wrapper.registered",
|
||||
|
||||
|
||||
|
||||
wrapperId:
|
||||
id,
|
||||
|
||||
|
||||
|
||||
commandQueue:
|
||||
wrapper?.commandQueue
|
||||
|
||||
|
||||
})
|
||||
|
||||
)
|
||||
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
heartbeat(data:any){
|
||||
|
||||
|
||||
|
||||
this.registry.updateHeartbeat(
|
||||
|
||||
data.wrapperId
|
||||
|
||||
);
|
||||
|
||||
|
||||
|
||||
console.log(
|
||||
|
||||
"Heartbeat:",
|
||||
|
||||
data.wrapperId
|
||||
|
||||
);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
sendCommand(
|
||||
|
||||
wrapperId:string,
|
||||
|
||||
command:any
|
||||
|
||||
){
|
||||
|
||||
|
||||
|
||||
const wrapper =
|
||||
this.registry.get(
|
||||
wrapperId
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
if(!wrapper){
|
||||
|
||||
|
||||
throw new Error(
|
||||
|
||||
"Wrapper nicht gefunden"
|
||||
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
this.rabbit.channel.sendToQueue(
|
||||
|
||||
|
||||
wrapper.commandQueue,
|
||||
|
||||
|
||||
|
||||
Buffer.from(
|
||||
|
||||
JSON.stringify(
|
||||
command
|
||||
)
|
||||
|
||||
)
|
||||
|
||||
);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
getWrappers(){
|
||||
|
||||
|
||||
return this.registry.getAll();
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
listWrappers(){
|
||||
|
||||
|
||||
|
||||
const wrappers =
|
||||
this.registry.getAll();
|
||||
|
||||
|
||||
|
||||
|
||||
console.log(
|
||||
|
||||
"\n======================"
|
||||
|
||||
);
|
||||
|
||||
|
||||
console.log(
|
||||
|
||||
" WRAPPER ÜBERSICHT "
|
||||
|
||||
);
|
||||
|
||||
|
||||
console.log(
|
||||
|
||||
"======================\n"
|
||||
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if(wrappers.length===0){
|
||||
|
||||
|
||||
console.log(
|
||||
|
||||
"Keine Wrapper registriert"
|
||||
|
||||
);
|
||||
|
||||
|
||||
return;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
for(const wrapper of wrappers){
|
||||
|
||||
|
||||
|
||||
console.log(`
|
||||
|
||||
ID:
|
||||
${wrapper.id}
|
||||
|
||||
|
||||
Host:
|
||||
${wrapper.hostname}
|
||||
|
||||
|
||||
CPU:
|
||||
${wrapper.cpu} Cores
|
||||
|
||||
|
||||
RAM:
|
||||
${wrapper.ram} MB
|
||||
|
||||
|
||||
RAM frei:
|
||||
${wrapper.ramFree} MB
|
||||
|
||||
|
||||
Server:
|
||||
${wrapper.servers.length}
|
||||
|
||||
|
||||
Online:
|
||||
${wrapper.online}
|
||||
|
||||
|
||||
Heartbeat:
|
||||
${new Date(
|
||||
wrapper.lastHeartbeat
|
||||
).toLocaleString()}
|
||||
|
||||
|
||||
----------------------
|
||||
|
||||
`);
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
export interface WrapperInfo {
|
||||
|
||||
id:string;
|
||||
|
||||
hostname:string;
|
||||
|
||||
commandQueue:string;
|
||||
|
||||
cpu:number;
|
||||
|
||||
ram:number;
|
||||
|
||||
|
||||
ramFree:number;
|
||||
|
||||
|
||||
online:boolean;
|
||||
|
||||
lastHeartbeat:number;
|
||||
|
||||
|
||||
servers:string[];
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
export class WrapperRegistry {
|
||||
|
||||
|
||||
private wrappers =
|
||||
new Map<string, WrapperInfo>();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
add(wrapper:WrapperInfo){
|
||||
|
||||
|
||||
this.wrappers.set(
|
||||
wrapper.id,
|
||||
wrapper
|
||||
);
|
||||
|
||||
|
||||
console.log(
|
||||
"Wrapper gespeichert:",
|
||||
wrapper.id
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
get(id:string){
|
||||
|
||||
return this.wrappers.get(id);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
getAll(){
|
||||
|
||||
return Array.from(
|
||||
this.wrappers.values()
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
updateHeartbeat(id:string){
|
||||
|
||||
|
||||
const wrapper =
|
||||
this.wrappers.get(id);
|
||||
|
||||
|
||||
|
||||
if(!wrapper)
|
||||
return;
|
||||
|
||||
|
||||
|
||||
wrapper.lastHeartbeat =
|
||||
Date.now();
|
||||
|
||||
|
||||
wrapper.online =
|
||||
true;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
update(wrapper:WrapperInfo){
|
||||
|
||||
|
||||
this.wrappers.set(
|
||||
wrapper.id,
|
||||
wrapper
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
checkOffline(timeout:number){
|
||||
|
||||
|
||||
const now =
|
||||
Date.now();
|
||||
|
||||
|
||||
|
||||
for(const wrapper of this.wrappers.values()){
|
||||
|
||||
|
||||
if(
|
||||
wrapper.online &&
|
||||
now - wrapper.lastHeartbeat > timeout
|
||||
){
|
||||
|
||||
|
||||
wrapper.online = false;
|
||||
|
||||
|
||||
console.log(
|
||||
"Wrapper offline:",
|
||||
wrapper.id
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
updateStatus(data:any){
|
||||
|
||||
|
||||
const wrapper =
|
||||
this.wrappers.get(
|
||||
data.wrapperId
|
||||
);
|
||||
|
||||
|
||||
|
||||
if(!wrapper)
|
||||
return;
|
||||
|
||||
|
||||
|
||||
wrapper.ramFree =
|
||||
data.ramFree;
|
||||
|
||||
|
||||
|
||||
wrapper.servers =
|
||||
data.servers;
|
||||
|
||||
|
||||
|
||||
wrapper.lastHeartbeat =
|
||||
Date.now();
|
||||
|
||||
|
||||
|
||||
wrapper.online =
|
||||
true;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user