From 2b21d62682b1897582b5bd31675c42cdd63eb168 Mon Sep 17 00:00:00 2001 From: Gregory Gauthier Date: Thu, 26 Mar 2026 10:15:52 +0000 Subject: [PATCH] add metrics example from grok --- src/metric_client.py | 114 ++++++++++++++++++++++++++++++++++++++++++ src/metric_server.py | 116 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 230 insertions(+) create mode 100644 src/metric_client.py create mode 100644 src/metric_server.py diff --git a/src/metric_client.py b/src/metric_client.py new file mode 100644 index 0000000..f9e448b --- /dev/null +++ b/src/metric_client.py @@ -0,0 +1,114 @@ +#!/usr/bin/env python3 +""" +Minimal metric client with custom binary protocol. +""" + +import socket +import struct +import sys +import threading + +# Same constants as server +MSG_PING = 0x01 +MSG_PONG = 0x02 +MSG_METRIC_REQ = 0x03 +MSG_METRIC_RESP = 0x04 +MSG_ERROR = 0xFF + +HEADER_FMT = "!BH" +HEADER_SIZE = struct.calcsize(HEADER_FMT) + + +def send_message(sock: socket.socket, msg_type: int, payload: bytes = b"") -> None: + header = struct.pack(HEADER_FMT, msg_type, len(payload)) + sock.sendall(header + payload) + + +def recv_exact(sock: socket.socket, n: int) -> bytes: + buf = bytearray() + while len(buf) < n: + chunk = sock.recv(n - len(buf)) + if not chunk: + return b"" + buf.extend(chunk) + return bytes(buf) + + +def recv_message(sock: socket.socket) -> tuple[int, bytes]: + header = recv_exact(sock, HEADER_SIZE) + if not header: + raise ConnectionError("Server disconnected") + msg_type, length = struct.unpack(HEADER_FMT, header) + payload = recv_exact(sock, length) if length > 0 else b"" + return msg_type, payload + + +def listener_thread(sock: socket.socket, running: threading.Event): + while running.is_set(): + try: + msg_type, payload = recv_message(sock) + if msg_type == MSG_PONG: + print("\n [PONG] server alive") + elif msg_type == MSG_METRIC_RESP: + value = struct.unpack("!d", payload)[0] + print(f"\n [METRIC] {value:.2f}") + elif msg_type == MSG_ERROR: + print(f"\n [ERROR] {payload.decode('utf-8')}") + else: + print(f"\n [???] Unknown type 0x{msg_type:02X}") + print("> ", end="", flush=True) + except ConnectionError: + if running.is_set(): + print("\nServer disconnected.") + break + + +def show_wire_comparison(): + our_size = HEADER_SIZE + len(b"cpu") + http_req = b"GET /metrics/cpu HTTP/1.1\r\nHost: localhost:9999\r\nAccept: application/json\r\n\r\n" + print(f"\n--- Wire overhead comparison ---") + print(f"Our protocol (METRIC_REQ 'cpu'): {our_size} bytes") + print(f"Equivalent HTTP GET: {len(http_req)} bytes") + print(f"HTTP is ~{len(http_req) / our_size:.0f}x larger\n") + + +def main(): + host = sys.argv[1] if len(sys.argv) > 1 else "127.0.0.1" + port = int(sys.argv[2]) if len(sys.argv) > 2 else 9999 + + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.connect((host, port)) + print(f"Connected to {host}:{port}") + show_wire_comparison() + + running = threading.Event() + running.set() + threading.Thread(target=listener_thread, args=(sock, running), daemon=True).start() + + print("Commands: ping | get | quit") + print("Metrics: cpu, memory, disk, loadavg, uptime\n") + + try: + while True: + line = input("> ").strip() + if not line: + continue + parts = line.split() + cmd = parts[0].lower() + + if cmd == "quit": + break + elif cmd == "ping": + send_message(sock, MSG_PING) + elif cmd == "get" and len(parts) == 2: + send_message(sock, MSG_METRIC_REQ, parts[1].encode("utf-8")) + else: + print(" Unknown command.") + finally: + running.clear() + sock.close() + print("Disconnected.") + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/src/metric_server.py b/src/metric_server.py new file mode 100644 index 0000000..d022abf --- /dev/null +++ b/src/metric_server.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python3 +""" +Minimal metric server with custom binary protocol (cannibalised from Claude). +Demonstrates low-overhead sockets vs HTTP/REST. +""" + +import socket +import struct +import sys +import time +import os +import random +import threading + +# Protocol constants (shared with client) +MSG_PING = 0x01 +MSG_PONG = 0x02 +MSG_METRIC_REQ = 0x03 +MSG_METRIC_RESP = 0x04 +MSG_ERROR = 0xFF + +HEADER_FMT = "!BH" # 1 byte type + 2 bytes length (big-endian) +HEADER_SIZE = struct.calcsize(HEADER_FMT) + + +def get_metric(name: str) -> float: + metrics = { + "cpu": lambda: random.uniform(5.0, 95.0), + "memory": lambda: random.uniform(30.0, 80.0), + "disk": lambda: random.uniform(40.0, 90.0), + "loadavg": lambda: os.getloadavg()[0] if hasattr(os, "getloadavg") else random.uniform(0.5, 4.0), + "uptime": lambda: float(int(time.time()) % 100000), + } + fn = metrics.get(name) + if fn is None: + raise KeyError(f"Unknown metric: {name}") + return fn() + + +def send_message(sock: socket.socket, msg_type: int, payload: bytes = b"") -> None: + header = struct.pack(HEADER_FMT, msg_type, len(payload)) + sock.sendall(header + payload) + + +def recv_exact(sock: socket.socket, n: int) -> bytes: + buf = bytearray() + while len(buf) < n: + chunk = sock.recv(n - len(buf)) + if not chunk: + return b"" + buf.extend(chunk) + return bytes(buf) + + +def recv_message(sock: socket.socket) -> tuple[int, bytes]: + header = recv_exact(sock, HEADER_SIZE) + if not header: + raise ConnectionError("Client disconnected") + msg_type, length = struct.unpack(HEADER_FMT, header) + payload = recv_exact(sock, length) if length > 0 else b"" + return msg_type, payload + + +def handle_client(conn: socket.socket, addr: tuple) -> None: + print(f"[+] Connected: {addr[0]}:{addr[1]}") + try: + while True: + msg_type, payload = recv_message(conn) + + if msg_type == MSG_PING: + print(f" <- PING from {addr[0]}") + send_message(conn, MSG_PONG) + + elif msg_type == MSG_METRIC_REQ: + name = payload.decode("utf-8") + print(f" <- METRIC_REQ: {name}") + try: + value = get_metric(name) + send_message(conn, MSG_METRIC_RESP, struct.pack("!d", value)) + except KeyError as e: + send_message(conn, MSG_ERROR, str(e).encode("utf-8")) + + else: + send_message(conn, MSG_ERROR, f"Unknown type 0x{msg_type:02X}".encode("utf-8")) + + except ConnectionError: + pass + finally: + conn.close() + print(f"[-] Disconnected: {addr[0]}:{addr[1]}") + + +def main(): + host = sys.argv[1] if len(sys.argv) > 1 else "127.0.0.1" + port = int(sys.argv[2]) if len(sys.argv) > 2 else 9999 + + srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + srv.bind((host, port)) + srv.listen(5) + + print(f"Metric server listening on {host}:{port}") + print("Available metrics: cpu, memory, disk, loadavg, uptime\n") + + try: + while True: + conn, addr = srv.accept() + threading.Thread(target=handle_client, args=(conn, addr), daemon=True).start() + except KeyboardInterrupt: + print("\nShutting down.") + finally: + srv.close() + + +if __name__ == "__main__": + main() \ No newline at end of file