prometheus + load-testing
This commit is contained in:
@@ -14,6 +14,7 @@ import cookieParser from "cookie-parser";
|
|||||||
import cors from "cors";
|
import cors from "cors";
|
||||||
import { authMiddleware } from "modules/expressMiddleware";
|
import { authMiddleware } from "modules/expressMiddleware";
|
||||||
import "modules/chron";
|
import "modules/chron";
|
||||||
|
import { socketConnections } from "modules/prometheus";
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
const server = createServer(app);
|
const server = createServer(app);
|
||||||
@@ -25,7 +26,10 @@ export const io = new Server(server, {
|
|||||||
io.use(jwtMiddleware);
|
io.use(jwtMiddleware);
|
||||||
|
|
||||||
io.on("connection", (socket) => {
|
io.on("connection", (socket) => {
|
||||||
console.log("New socket connection", socket.id);
|
socketConnections.inc();
|
||||||
|
socket.on("disconnect", () => {
|
||||||
|
socketConnections.dec();
|
||||||
|
});
|
||||||
socket.on("connect-dispatch", handleConnectDispatch(socket, io));
|
socket.on("connect-dispatch", handleConnectDispatch(socket, io));
|
||||||
socket.on("connect-pilot", handleConnectPilot(socket, io));
|
socket.on("connect-pilot", handleConnectPilot(socket, io));
|
||||||
socket.on("connect-desktop", handleConnectDesktop(socket, io));
|
socket.on("connect-desktop", handleConnectDesktop(socket, io));
|
||||||
|
|||||||
50
apps/dispatch-server/modules/prometheus.ts
Normal file
50
apps/dispatch-server/modules/prometheus.ts
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import { prisma } from "@repo/db";
|
||||||
|
import promClient from "prom-client";
|
||||||
|
|
||||||
|
export const promRegister = new promClient.Registry();
|
||||||
|
promClient.collectDefaultMetrics({ register: promRegister });
|
||||||
|
|
||||||
|
export const socketConnections = new promClient.Gauge({
|
||||||
|
name: "socket_connections",
|
||||||
|
help: "Number of active socket connections",
|
||||||
|
registers: [promRegister],
|
||||||
|
});
|
||||||
|
|
||||||
|
export const aircraftPatches = new promClient.Counter({
|
||||||
|
name: "aircraft_patches",
|
||||||
|
help: "Counts patch requests for aircrafts",
|
||||||
|
registers: [promRegister],
|
||||||
|
});
|
||||||
|
|
||||||
|
export const connectedPilots = new promClient.Gauge({
|
||||||
|
name: "connected_pilots",
|
||||||
|
help: "Counts connected pilots",
|
||||||
|
registers: [promRegister],
|
||||||
|
collect: async () => {
|
||||||
|
const count = await prisma.connectedAircraft.count({
|
||||||
|
where: {
|
||||||
|
logoutTime: null,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
connectedPilots.set(count);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const connectedDispatcher = new promClient.Gauge({
|
||||||
|
name: "connected_dispatcher",
|
||||||
|
help: "Counts connected dispatchers",
|
||||||
|
registers: [promRegister],
|
||||||
|
collect: async () => {
|
||||||
|
const count = await prisma.connectedDispatcher.count({
|
||||||
|
where: {
|
||||||
|
logoutTime: null,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
connectedDispatcher.set(count);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
promRegister.registerMetric(socketConnections);
|
||||||
|
promRegister.registerMetric(aircraftPatches);
|
||||||
|
promRegister.registerMetric(connectedPilots);
|
||||||
|
promRegister.registerMetric(connectedDispatcher);
|
||||||
@@ -5,7 +5,7 @@ import jwt from "jsonwebtoken";
|
|||||||
|
|
||||||
export const jwtMiddleware = async (socket: Socket, next: (err?: ExtendedError) => void) => {
|
export const jwtMiddleware = async (socket: Socket, next: (err?: ExtendedError) => void) => {
|
||||||
try {
|
try {
|
||||||
const { uid } = socket.handshake.auth;
|
const uid = socket.handshake.auth.uid || socket.handshake.query.uid;
|
||||||
if (!uid) return new Error("Authentication error");
|
if (!uid) return new Error("Authentication error");
|
||||||
/* const token = socket.handshake.auth?.token;
|
/* const token = socket.handshake.auth?.token;
|
||||||
if (!token) return new Error("Authentication error");
|
if (!token) return new Error("Authentication error");
|
||||||
|
|||||||
@@ -11,8 +11,8 @@
|
|||||||
"packageManager": "pnpm@10.11.0",
|
"packageManager": "pnpm@10.11.0",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@repo/db": "workspace:*",
|
"@repo/db": "workspace:*",
|
||||||
"@repo/typescript-config": "workspace:*",
|
|
||||||
"@repo/shared-components": "workspace:*",
|
"@repo/shared-components": "workspace:*",
|
||||||
|
"@repo/typescript-config": "workspace:*",
|
||||||
"@types/cookie-parser": "^1.4.8",
|
"@types/cookie-parser": "^1.4.8",
|
||||||
"@types/cors": "^2.8.18",
|
"@types/cors": "^2.8.18",
|
||||||
"@types/express": "^5.0.2",
|
"@types/express": "^5.0.2",
|
||||||
@@ -37,6 +37,7 @@
|
|||||||
"node-cron": "^4.1.0",
|
"node-cron": "^4.1.0",
|
||||||
"nodemailer": "^7.0.3",
|
"nodemailer": "^7.0.3",
|
||||||
"nodemon": "^3.1.10",
|
"nodemon": "^3.1.10",
|
||||||
|
"prom-client": "^15.1.3",
|
||||||
"react": "^19.1.0",
|
"react": "^19.1.0",
|
||||||
"redis": "^5.1.1",
|
"redis": "^5.1.1",
|
||||||
"socket.io": "^4.8.1",
|
"socket.io": "^4.8.1",
|
||||||
|
|||||||
@@ -1,13 +1,7 @@
|
|||||||
import {
|
import { AdminMessage, getPublicUser, MissionLog, Prisma, prisma } from "@repo/db";
|
||||||
AdminMessage,
|
|
||||||
ConnectedAircraft,
|
|
||||||
getPublicUser,
|
|
||||||
MissionLog,
|
|
||||||
Prisma,
|
|
||||||
prisma,
|
|
||||||
} from "@repo/db";
|
|
||||||
import { Router } from "express";
|
import { Router } from "express";
|
||||||
import { io } from "../index";
|
import { io } from "../index";
|
||||||
|
import { aircraftPatches } from "modules/prometheus";
|
||||||
|
|
||||||
const router: Router = Router();
|
const router: Router = Router();
|
||||||
|
|
||||||
@@ -97,7 +91,7 @@ router.patch("/:id", async (req, res) => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
aircraftPatches.inc();
|
||||||
res.json(updatedConnectedAircraft);
|
res.json(updatedConnectedAircraft);
|
||||||
// When change is only the estimated logout time, we don't need to emit an event
|
// When change is only the estimated logout time, we don't need to emit an event
|
||||||
if (Object.keys(aircraftUpdate).length === 1 && aircraftUpdate.esimatedLogoutTime) return;
|
if (Object.keys(aircraftUpdate).length === 1 && aircraftUpdate.esimatedLogoutTime) return;
|
||||||
|
|||||||
9
apps/dispatch-server/routes/metrics.ts
Normal file
9
apps/dispatch-server/routes/metrics.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { Router } from "express";
|
||||||
|
import { promRegister } from "modules/prometheus";
|
||||||
|
|
||||||
|
export const metricsRouter: Router = Router();
|
||||||
|
|
||||||
|
metricsRouter.get("/", async (req, res) => {
|
||||||
|
res.setHeader("Content-Type", promRegister.contentType);
|
||||||
|
res.end(await promRegister.metrics());
|
||||||
|
});
|
||||||
@@ -4,6 +4,7 @@ import missionRouter from "./mission";
|
|||||||
import statusRouter from "./status";
|
import statusRouter from "./status";
|
||||||
import aircraftsRouter from "./aircraft";
|
import aircraftsRouter from "./aircraft";
|
||||||
import reportRouter from "./report";
|
import reportRouter from "./report";
|
||||||
|
import { metricsRouter } from "routes/metrics";
|
||||||
|
|
||||||
const router: Router = Router();
|
const router: Router = Router();
|
||||||
|
|
||||||
@@ -12,5 +13,6 @@ router.use("/mission", missionRouter);
|
|||||||
router.use("/status", statusRouter);
|
router.use("/status", statusRouter);
|
||||||
router.use("/aircrafts", aircraftsRouter);
|
router.use("/aircrafts", aircraftsRouter);
|
||||||
router.use("/report", reportRouter);
|
router.use("/report", reportRouter);
|
||||||
|
router.use("/metrics", metricsRouter);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ export const handleConnectPilot =
|
|||||||
debug: boolean;
|
debug: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
try {
|
try {
|
||||||
|
console.log("Connecting pilot:", socket.id, "Station ID:", stationId, "Debug mode:", debug);
|
||||||
if (!stationId) return Error("Station ID is required");
|
if (!stationId) return Error("Station ID is required");
|
||||||
const user: User = socket.data.user; // User ID aus dem JWT-Token
|
const user: User = socket.data.user; // User ID aus dem JWT-Token
|
||||||
const userId = socket.data.user.id; // User ID aus dem JWT-Token
|
const userId = socket.data.user.id; // User ID aus dem JWT-Token
|
||||||
@@ -43,7 +44,7 @@ export const handleConnectPilot =
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (existingConnection) {
|
if (existingConnection && !debug) {
|
||||||
await io.to(`user:${user.id}`).emit("force-disconnect", "double-connection");
|
await io.to(`user:${user.id}`).emit("force-disconnect", "double-connection");
|
||||||
await prisma.connectedAircraft.updateMany({
|
await prisma.connectedAircraft.updateMany({
|
||||||
where: {
|
where: {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import { redirect } from "next/navigation";
|
|||||||
import { ListInput } from "_components/ui/List";
|
import { ListInput } from "_components/ui/List";
|
||||||
|
|
||||||
export const KeywordForm = ({ keyword }: { keyword?: Keyword }) => {
|
export const KeywordForm = ({ keyword }: { keyword?: Keyword }) => {
|
||||||
const form = useForm<z.infer<typeof KeywordOptionalDefaultsSchema>>({
|
const form = useForm({
|
||||||
resolver: zodResolver(KeywordOptionalDefaultsSchema),
|
resolver: zodResolver(KeywordOptionalDefaultsSchema),
|
||||||
defaultValues: keyword,
|
defaultValues: keyword,
|
||||||
});
|
});
|
||||||
@@ -36,9 +36,7 @@ export const KeywordForm = ({ keyword }: { keyword?: Keyword }) => {
|
|||||||
<FileText className="w-5 h-5" /> Allgemeines
|
<FileText className="w-5 h-5" /> Allgemeines
|
||||||
</h2>
|
</h2>
|
||||||
<label className="form-control w-full ">
|
<label className="form-control w-full ">
|
||||||
<span className="label-text text-lg flex items-center gap-2">
|
<span className="label-text text-lg flex items-center gap-2">Kategorie</span>
|
||||||
Kategorie
|
|
||||||
</span>
|
|
||||||
<select
|
<select
|
||||||
className="input-sm select select-bordered select-sm w-full"
|
className="input-sm select select-bordered select-sm w-full"
|
||||||
{...form.register("category")}
|
{...form.register("category")}
|
||||||
@@ -50,12 +48,7 @@ export const KeywordForm = ({ keyword }: { keyword?: Keyword }) => {
|
|||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
<Input
|
<Input form={form} label="Abkürzung" name="abreviation" className="input-sm" />
|
||||||
form={form}
|
|
||||||
label="Abkürzung"
|
|
||||||
name="abreviation"
|
|
||||||
className="input-sm"
|
|
||||||
/>
|
|
||||||
<Input
|
<Input
|
||||||
form={form}
|
form={form}
|
||||||
label="Name"
|
label="Name"
|
||||||
@@ -82,11 +75,7 @@ export const KeywordForm = ({ keyword }: { keyword?: Keyword }) => {
|
|||||||
<div className="card bg-base-200 shadow-xl col-span-6">
|
<div className="card bg-base-200 shadow-xl col-span-6">
|
||||||
<div className="card-body ">
|
<div className="card-body ">
|
||||||
<div className="flex w-full gap-4">
|
<div className="flex w-full gap-4">
|
||||||
<Button
|
<Button isLoading={loading} type="submit" className="btn btn-primary flex-1">
|
||||||
isLoading={loading}
|
|
||||||
type="submit"
|
|
||||||
className="btn btn-primary flex-1"
|
|
||||||
>
|
|
||||||
Speichern
|
Speichern
|
||||||
</Button>
|
</Button>
|
||||||
{keyword && (
|
{keyword && (
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import { Button } from "../../../../_components/ui/Button";
|
|||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
|
|
||||||
export const StationForm = ({ station }: { station?: Station }) => {
|
export const StationForm = ({ station }: { station?: Station }) => {
|
||||||
const form = useForm<z.infer<typeof StationOptionalDefaultsSchema>>({
|
const form = useForm({
|
||||||
resolver: zodResolver(StationOptionalDefaultsSchema),
|
resolver: zodResolver(StationOptionalDefaultsSchema),
|
||||||
defaultValues: station,
|
defaultValues: station,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -10,50 +10,50 @@
|
|||||||
"lint": "next lint"
|
"lint": "next lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint/eslintrc": "^3",
|
"@eslint/eslintrc": "^3.3.1",
|
||||||
"@hookform/resolvers": "^5.0.1",
|
"@hookform/resolvers": "^5.1.1",
|
||||||
"@next-auth/prisma-adapter": "^1.0.7",
|
"@next-auth/prisma-adapter": "^1.0.7",
|
||||||
"@radix-ui/react-icons": "^1.3.2",
|
"@radix-ui/react-icons": "^1.3.2",
|
||||||
"@repo/db": "workspace:*",
|
"@repo/db": "workspace:*",
|
||||||
"@repo/eslint-config": "workspace:*",
|
"@repo/eslint-config": "workspace:*",
|
||||||
"@repo/shared-components": "workspace:*",
|
"@repo/shared-components": "workspace:*",
|
||||||
"@repo/typescript-config": "workspace:*",
|
"@repo/typescript-config": "workspace:*",
|
||||||
"@tailwindcss/postcss": "^4.1.8",
|
"@tailwindcss/postcss": "^4.1.11",
|
||||||
"@tanstack/react-query": "^5.79.2",
|
"@tanstack/react-query": "^5.81.5",
|
||||||
"@tanstack/react-table": "^8.21.3",
|
"@tanstack/react-table": "^8.21.3",
|
||||||
"@types/bcryptjs": "^3.0.0",
|
"@types/jsonwebtoken": "^9.0.10",
|
||||||
"@types/jsonwebtoken": "^9.0.9",
|
"@types/node": "^22.15.34",
|
||||||
"@types/node": "^22.15.29",
|
"@types/react": "^19.1.8",
|
||||||
"@types/react": "^19.1.6",
|
"@types/react-dom": "^19.1.6",
|
||||||
"@types/react-dom": "^19.1.5",
|
|
||||||
"@uiw/react-md-editor": "^4.0.7",
|
"@uiw/react-md-editor": "^4.0.7",
|
||||||
"axios": "^1.9.0",
|
"axios": "^1.10.0",
|
||||||
"bcryptjs": "^3.0.2",
|
"bcryptjs": "^3.0.2",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"daisyui": "^5.0.43",
|
"daisyui": "^5.0.43",
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
"eslint": "^9.15.0",
|
"eslint": "^9.30.0",
|
||||||
"eslint-config-next": "^15.3.3",
|
"eslint-config-next": "^15.3.4",
|
||||||
"i": "^0.3.7",
|
"i": "^0.3.7",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"lucide-react": "^0.511.0",
|
"lucide-react": "^0.511.0",
|
||||||
"next": "^15.3.3",
|
"next": "^15.3.4",
|
||||||
"next-auth": "^4.24.11",
|
"next-auth": "^4.24.11",
|
||||||
"next-remove-imports": "^1.0.12",
|
"next-remove-imports": "^1.0.12",
|
||||||
"npm": "^11.4.1",
|
"npm": "^11.4.2",
|
||||||
"postcss": "^8.5.4",
|
"postcss": "^8.5.6",
|
||||||
"react": "^19.1.0",
|
"react": "^19.1.0",
|
||||||
"react-datepicker": "^8.4.0",
|
"react-datepicker": "^8.4.0",
|
||||||
"react-day-picker": "^9.7.0",
|
"react-day-picker": "^9.7.0",
|
||||||
"react-dom": "^19.1.0",
|
"react-dom": "^19.1.0",
|
||||||
"react-error-boundary": "^6.0.0",
|
"react-error-boundary": "^6.0.0",
|
||||||
"react-hook-form": "^7.56.4",
|
"react-hook-form": "^7.59.0",
|
||||||
"react-hot-toast": "^2.5.2",
|
"react-hot-toast": "^2.5.2",
|
||||||
"react-select": "^5.10.1",
|
"react-select": "^5.10.1",
|
||||||
"tailwind-merge": "^3.3.0",
|
"tailwind-merge": "^3.3.1",
|
||||||
"tailwindcss": "^4.1.8",
|
"tailwindcss": "^4.1.11",
|
||||||
"typescript": "^5.8.3",
|
"typescript": "^5.8.3",
|
||||||
"zod": "^3.25.46"
|
"zod": "^3.25.67",
|
||||||
|
"zustand": "^5.0.6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,16 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- postgres-data:/var/lib/postgresql/data
|
- postgres-data:/var/lib/postgresql/data
|
||||||
|
|
||||||
|
prometheus:
|
||||||
|
image: prom/prometheus:latest
|
||||||
|
container_name: prometheus
|
||||||
|
ports:
|
||||||
|
- "9090:9090"
|
||||||
|
volumes:
|
||||||
|
- "./packages/prometheus/prometheus.dev.yml:/etc/prometheus/prometheus.yml"
|
||||||
|
command:
|
||||||
|
- "--config.file=/etc/prometheus/prometheus.yml"
|
||||||
|
|
||||||
redis:
|
redis:
|
||||||
container_name: redis
|
container_name: redis
|
||||||
image: redis/redis-stack:latest
|
image: redis/redis-stack:latest
|
||||||
|
|||||||
@@ -108,6 +108,8 @@ services:
|
|||||||
- "traefik.http.services.dispatch-server.loadBalancer.sticky.cookie.name=server_id"
|
- "traefik.http.services.dispatch-server.loadBalancer.sticky.cookie.name=server_id"
|
||||||
- "traefik.http.services.dispatch-server.loadBalancer.sticky.cookie.httpOnly=true"
|
- "traefik.http.services.dispatch-server.loadBalancer.sticky.cookie.httpOnly=true"
|
||||||
- "traefik.http.services.dispatch-server.loadbalancer.server.port=3000"
|
- "traefik.http.services.dispatch-server.loadbalancer.server.port=3000"
|
||||||
|
- "prometheus.scrape=true"
|
||||||
|
- "prometheus.port=3000"
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
- discord_network
|
- discord_network
|
||||||
@@ -119,6 +121,20 @@ services:
|
|||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
redis:
|
redis:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
|
||||||
|
prometheus:
|
||||||
|
image: prom/prometheus:latest
|
||||||
|
container_name: prometheus
|
||||||
|
ports:
|
||||||
|
- "9090:9090"
|
||||||
|
volumes:
|
||||||
|
- "./packages/prometheus/prometheus.prod.yml:/etc/prometheus/prometheus.yml"
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
command:
|
||||||
|
- "--config.file=/etc/prometheus/prometheus.yml"
|
||||||
|
networks:
|
||||||
|
- traefik
|
||||||
|
|
||||||
discord-server:
|
discord-server:
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
@@ -146,6 +162,7 @@ services:
|
|||||||
|
|
||||||
networks:
|
networks:
|
||||||
- traefik
|
- traefik
|
||||||
|
|
||||||
postgres:
|
postgres:
|
||||||
image: postgres:13
|
image: postgres:13
|
||||||
container_name: postgres
|
container_name: postgres
|
||||||
@@ -191,7 +208,7 @@ services:
|
|||||||
depends_on:
|
depends_on:
|
||||||
- redis
|
- redis
|
||||||
volumes:
|
volumes:
|
||||||
- ./livekit.yaml:/etc/livekit.yaml
|
- ./packages/livekit/livekit.yaml:/etc/livekit.yaml
|
||||||
labels:
|
labels:
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.http.routers.livekit.rule=Host(`livekit.premiumag.de`)"
|
- "traefik.http.routers.livekit.rule=Host(`livekit.premiumag.de`)"
|
||||||
|
|||||||
13
packages/prometheus/prometheus.dev.yml
Normal file
13
packages/prometheus/prometheus.dev.yml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
global:
|
||||||
|
scrape_interval: 40s
|
||||||
|
|
||||||
|
remote_write:
|
||||||
|
- url: https://prometheus-prod-36-prod-us-west-0.grafana.net/api/prom/push
|
||||||
|
basic_auth:
|
||||||
|
username: 2527367
|
||||||
|
password: glc_eyJvIjoiMTMzOTM4MiIsIm4iOiJzdGFjay0xMzAxNTY2LWFsbG95LWxvY2FsLWRldiIsImsiOiI1YkM0SkFvODU3NjJCaTFlQnkwY0xySjEiLCJtIjp7InIiOiJwcm9kLXVzLXdlc3QtMCJ9fQ==
|
||||||
|
|
||||||
|
scrape_configs:
|
||||||
|
- job_name: dispatch-server
|
||||||
|
static_configs:
|
||||||
|
- targets: ["host.docker.internal:3002"]
|
||||||
24
packages/prometheus/prometheus.prod.yml
Normal file
24
packages/prometheus/prometheus.prod.yml
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
global:
|
||||||
|
scrape_interval: 40s
|
||||||
|
|
||||||
|
remote_write:
|
||||||
|
- url: https://prometheus-prod-36-prod-us-west-0.grafana.net/api/prom/push
|
||||||
|
basic_auth:
|
||||||
|
username: 2527367
|
||||||
|
password: glc_eyJvIjoiMTMzOTM4MiIsIm4iOiJzdGFjay0xMzAxNTY2LWFsbG95LWxvY2FsLWRldiIsImsiOiI1YkM0SkFvODU3NjJCaTFlQnkwY0xySjEiLCJtIjp7InIiOiJwcm9kLXVzLXdlc3QtMCJ9fQ==
|
||||||
|
|
||||||
|
scrape_configs:
|
||||||
|
- job_name: "docker-services"
|
||||||
|
docker_sd_configs:
|
||||||
|
- host: unix:///var/run/docker.sock
|
||||||
|
refresh_interval: 30s
|
||||||
|
|
||||||
|
relabel_configs:
|
||||||
|
- source_labels: [__meta_docker_container_label_prometheus_scrape]
|
||||||
|
action: keep
|
||||||
|
regex: true
|
||||||
|
|
||||||
|
- source_labels: [__meta_docker_container_label_prometheus_port]
|
||||||
|
target_label: __address__
|
||||||
|
regex: (.+)
|
||||||
|
replacement: $1
|
||||||
8283
pnpm-lock.yaml
generated
8283
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
24
tests/aircrafts.js
Normal file
24
tests/aircrafts.js
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import http from "k6/http";
|
||||||
|
import { check, sleep } from "k6";
|
||||||
|
|
||||||
|
export const options = {
|
||||||
|
vus: 10, // virtuelle Nutzer gleichzeitig
|
||||||
|
duration: "30s", // Testdauer
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function () {
|
||||||
|
const res = http.get("https://dispatch.premiumag.de/api/aircrafts");
|
||||||
|
const res2 = http.get("https://dispatch.premiumag.de/api/dispatcher");
|
||||||
|
|
||||||
|
check(res, {
|
||||||
|
"Status Piloten ist 200": (r) => r.status === 200,
|
||||||
|
"Antwort enthält Daten": (r) => r.body && r.body.length > 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
check(res2, {
|
||||||
|
"Status Dispatcher ist 200": (r) => r.status === 200,
|
||||||
|
"Antwort enthält Daten": (r) => r.body && r.body.length > 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
sleep(1); // optional, Wartezeit zwischen Requests
|
||||||
|
}
|
||||||
16
tests/package.json
Normal file
16
tests/package.json
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"name": "tests",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"packageManager": "pnpm@10.12.1",
|
||||||
|
"devDependencies": {
|
||||||
|
"artillery": "^2.0.23"
|
||||||
|
}
|
||||||
|
}
|
||||||
40
tests/pilotenWS.yml
Normal file
40
tests/pilotenWS.yml
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
config:
|
||||||
|
target: "http://localhost:3002" # URL des Dispatch-Servers
|
||||||
|
phases:
|
||||||
|
- name: "0->50 in 10 min"
|
||||||
|
duration: "10m"
|
||||||
|
arrivalRate: 0
|
||||||
|
rampTo: 50
|
||||||
|
- name: "keep 50 for 20 min"
|
||||||
|
duration: "20m"
|
||||||
|
arrivalRate: 50
|
||||||
|
- name: "50->0 in 10 min"
|
||||||
|
duration: "10m"
|
||||||
|
arrivalRate: 50
|
||||||
|
rampTo: 0
|
||||||
|
engines:
|
||||||
|
socketio: {}
|
||||||
|
socketio:
|
||||||
|
transport: "websocket"
|
||||||
|
query: "uid=c69741db-af39-499c-8367-8d2b38531b9c" # Beispiel UID, kann angepasst werden
|
||||||
|
|
||||||
|
scenarios:
|
||||||
|
- engine: socketio
|
||||||
|
flow:
|
||||||
|
- emit:
|
||||||
|
channel: "connect-pilot"
|
||||||
|
data:
|
||||||
|
logoffTime: "14:00"
|
||||||
|
stationId: "1"
|
||||||
|
debug: true
|
||||||
|
- waitFor:
|
||||||
|
event: "aircraft-update"
|
||||||
|
timeout: 10000
|
||||||
|
assign: "aircraftData"
|
||||||
|
- log: "Empfangenes aircraft-update Event: {{ aircraftData }}"
|
||||||
|
- function: |
|
||||||
|
// In JavaScript kannst du jetzt die id extrahieren und in session speichern
|
||||||
|
const aircraftData = JSON.parse(JSON.stringify(session.vars.aircraftData));
|
||||||
|
session.vars.aircraftId = aircraftData.id;
|
||||||
|
return session;
|
||||||
|
- log: "Aircraft ID gespeichert: {{ aircraftId }}"
|
||||||
Reference in New Issue
Block a user