基于Node.js的python-shell心跳监控实现方案问询
Got it, let’s walk through how to implement heartbeat monitoring for your long-running Python scripts (executed via python-shell) and display their active/inactive status in an HTML table. I’ve built similar setups before, so here’s a practical, step-by-step approach:
1. Implement Heartbeat Signals in Python Scripts
First, modify your persistent Python scripts to send regular heartbeat messages to Node.js. We’ll use stdout to send a uniquely formatted signal that Node.js can easily identify.
import time import sys import threading def send_heartbeat(): # Send a heartbeat every 10 seconds (adjust interval as needed) while True: print("HEARTBEAT:ACTIVE", flush=True) # Flush ensures immediate delivery time.sleep(10) # Start heartbeat as a daemon thread (dies when main script exits) heartbeat_thread = threading.Thread(target=send_heartbeat, daemon=True) heartbeat_thread.start() # Your existing script logic goes here...
2. Track Script Status in Node.js
Create a state store to track each running script’s heartbeat timestamp and current status. Use python-shell event listeners to update this state when heartbeats are received, or when the script exits/errors.
const { PythonShell } = require('python-shell'); const scriptStatuses = new Map(); // Key: unique script ID, Value: status object function startMonitoredScript(scriptId, scriptPath, options = {}) { const initialHeartbeat = Date.now(); scriptStatuses.set(scriptId, { lastHeartbeat: initialHeartbeat, status: 'active', shellInstance: null }); const shell = new PythonShell(scriptPath, options); scriptStatuses.get(scriptId).shellInstance = shell; // Listen for heartbeat messages shell.on('message', (message) => { if (message.trim() === 'HEARTBEAT:ACTIVE') { const status = scriptStatuses.get(scriptId); status.lastHeartbeat = Date.now(); status.status = 'active'; } }); // Handle script exit/error shell.on('close', (exitCode) => { scriptStatuses.get(scriptId).status = 'inactive'; console.log(`Script ${scriptId} exited with code ${exitCode}`); }); shell.on('error', (err) => { scriptStatuses.get(scriptId).status = 'inactive'; console.error(`Script ${scriptId} encountered an error:`, err); }); } // Example: Start a script with ID "data-processor" startMonitoredScript("data-processor", "./scripts/data-processor.py");
3. Add Heartbeat Timeout Checks
Set up a recurring check to mark scripts as inactive if they stop sending heartbeats for longer than a threshold (give a small buffer over your Python heartbeat interval).
const HEARTBEAT_TIMEOUT = 20000; // 20 seconds (longer than Python's 10s interval) // Check status every 15 seconds setInterval(() => { const currentTime = Date.now(); scriptStatuses.forEach((status, scriptId) => { if (currentTime - status.lastHeartbeat > HEARTBEAT_TIMEOUT) { status.status = 'inactive'; // Optional: Auto-restart the script if needed // restartScript(scriptId); } }); }, 15000);
4. Expose a Status API for the Frontend
Use a framework like Express to create an endpoint that returns the current script statuses to your HTML page.
const express = require('express'); const app = express(); const port = 3000; // Serve your HTML file app.use(express.static('public')); // API endpoint to get script statuses app.get('/api/script-statuses', (req, res) => { const statusList = Array.from(scriptStatuses.entries()).map(([id, status]) => ({ scriptId: id, status: status.status, lastHeartbeat: new Date(status.lastHeartbeat).toLocaleString() })); res.json(statusList); }); app.listen(port, () => { console.log(`Monitor server running on http://localhost:${port}`); });
5. Build the HTML Status Table
Create an HTML page that fetches status data periodically and updates the table dynamically.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Python Script Monitor</title> <style> table { border-collapse: collapse; width: 80%; margin: 2rem auto; } th, td { border: 1px solid #ddd; padding: 0.8rem; text-align: left; } th { background-color: #f5f5f5; } .active { color: #2ecc71; font-weight: bold; } .inactive { color: #e74c3c; font-weight: bold; } </style> </head> <body> <h1 style="text-align: center;">Python Script Status Monitor</h1> <table id="statusTable"> <thead> <tr> <th>Script ID</th> <th>Status</th> <th>Last Heartbeat</th> </tr> </thead> <tbody></tbody> </table> <script> function updateStatusTable() { fetch('/api/script-statuses') .then(res => res.json()) .then(statuses => { const tbody = document.querySelector('#statusTable tbody'); tbody.innerHTML = ''; statuses.forEach(script => { const row = document.createElement('tr'); row.innerHTML = ` <td>${script.scriptId}</td> <td class="${script.status}">${script.status.toUpperCase()}</td> <td>${script.lastHeartbeat}</td> `; tbody.appendChild(row); }); }) .catch(err => console.error('Failed to load statuses:', err)); } // Update table every 10 seconds setInterval(updateStatusTable, 10000); // Load initial status on page load updateStatusTable(); </script> </body> </html>
Bonus: Real-Time Updates with WebSockets
For more immediate status updates (instead of polling), use Socket.io to push status changes to the frontend automatically:
- Add Socket.io to your Node.js server
- Emit a
status-updateevent whenever a script’s status changes - Modify the frontend to listen for these events and update the table instantly
内容的提问来源于stack exchange,提问作者Harsh Goyal

