Electron中Python服务器启动后轮询检测机制未停止问题排查
问题:轮询机制未检测到已启动的Python服务器
我在Electron应用中实现轮询机制,等待Python服务器启动完成后再创建浏览器窗口——因为若服务器启动过慢,我需要刷新应用才能从服务器获取数据。
我设置了10秒的重试时长,使用electron-forge start启动应用时,服务器约1秒即可启动,控制台可见启动日志,且我能通过Chrome浏览器成功访问/health-check健康检查接口(控制台中的INFO行是我刷新浏览器验证连接的记录)。但我的轮询机制会持续检测直到10秒超时,随后应用退出。
代码与控制台输出
main.ts代码
// Electron modules import { app, BrowserWindow, ipcMain } from 'electron'; // Built-in modules import { spawn, exec } from 'child_process'; import path from 'path'; // Extra modules import getPort, { portNumbers } from 'get-port'; import get from 'axios'; // Function to check if server is running async function isServerRunning(serverUrl: string) { try { const response = await get(serverUrl); console.log(response); return response.status === 200; } catch (error) { return false; } } // Function to wait for the server to start async function waitForServer(serverUrl: string) { const maxRetries = 10; // Set max retries let attempts = 0; while (attempts < maxRetries) { console.log(`Attempt #${attempts}`); const serverIsRunning = await isServerRunning(serverUrl); console.log(serverIsRunning); if (serverIsRunning) { return true; // Server is up } attempts++; await new Promise((resolve) => setTimeout(resolve, 1000)); // Wait 1 second before retrying } throw new Error('Waiting for server to start.'); } const createWindow = (port: number) => { const mainWindow = new BrowserWindow({ frame: false, width: 800, height: 600, webPreferences: { preload: path.join(__dirname, 'preload.js'), nodeIntegration: true, contextIsolation: false, }, }); // and load the index.html of the app. if (MAIN_WINDOW_VITE_DEV_SERVER_URL) { mainWindow.loadURL(MAIN_WINDOW_VITE_DEV_SERVER_URL); } else { mainWindow.loadFile( path.join(__dirname, `../renderer/${MAIN_WINDOW_VITE_NAME}/index.html`), ); } }; app.whenReady().then(async () => { const port = 8036; // Start the flask server in development or production if (MAIN_WINDOW_VITE_DEV_SERVER_URL) { spawn(`python backend/main.py ${port}`, { detached: true, shell: true, stdio: 'inherit', }); } else { ... } try { await waitForServer(`http://localhost:${port}/health-check`); createWindow(port); } catch (error) { console.error(error); app.quit(); } app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) createWindow(port); }); app.on('window-all-closed', () => { if (process.platform !== 'darwin') { app.quit(); } }); });
控制台输出
✔ Checking your system ✔ Locating application ✔ Loading configuration ✔ Preparing native dependencies [0.2s] ✔ Running generateAssets hook ⠙ [plugin-vite] Launching dev servers for renderer process code ◼ [plugin-vite] Compiling main process code ✔ [plugin-vite] Launching dev servers for renderer process code [0.1s] ⠸ [plugin-vite] Compiling main process code vite v4.4.9 building for development... watching for file changes... vite v4.4.9 building for development... watching for file changes... build started... build started... ✓ 1 modules transformed. Generated an empty chunk: "preload". ✔ [plugin-vite] Launching dev servers for renderer process code [0.1s] ✔ [plugin-vite] Launching dev servers for renderer process code [0.1s] ⠧ [plugin-vite] Compiling main process code ✔ [plugin-vite] Launching dev servers for renderer process code [0.1s] ✔ [plugin-vite] Compiling main process code [0.8s] 2024-09-26 22:54:06.322 Electron[22787:10970072] WARNING: Secure coding is not enabled for restorable state! Enable secure coding by implementing NSApplicationDelegate.applicationSupportsSecureRestorableState: and returning YES. Attempt #0 false INFO: Started server process [22790] INFO: Waiting for application startup. INFO: Application startup complete. INFO: Uvicorn running on http://0.0.0.0:8036 (Press CTRL+C to quit) Attempt #1 false Attempt #2 false INFO: 127.0.0.1:53635 - "GET /health-check HTTP/1.1" 200 OK Attempt #3 false Attempt #4 false Attempt #5 false INFO: 127.0.0.1:53635 - "GET /health-check HTTP/1.1" 200 OK Attempt #6 false Attempt #7 false Attempt #8 false Attempt #9 false Error: Waiting for server to start. at Oo (/repopath/.vite/build/main.js:21:7777) at async /repopath/.vite/build/main.js:21:8504
问题原因与修复方案
核心原因
你的Python服务器绑定的是0.0.0.0:8036(仅监听IPv4地址),但Electron中轮询请求用的是http://localhost:8036/health-check。部分环境下Electron会优先将localhost解析为IPv6地址[::1],而服务器未监听IPv6,导致请求失败,轮询无法检测到服务器已启动。
另外,当前isServerRunning函数未打印错误详情,无法直观排查请求失败原因。
修复步骤
- 修改轮询地址:将
waitForServer的参数从http://localhost:${port}/health-check改为http://127.0.0.1:${port}/health-check,强制使用IPv4地址请求,避免解析歧义。 - 添加错误日志:在
isServerRunning的catch块中增加错误打印,方便后续排查:catch (error) { console.error(`健康检查请求失败:`, error.message); return false; } - 可选:兼容IPv6:如果需要支持IPv6,可以修改Python服务器启动参数,让Uvicorn同时监听IPv4和IPv6,例如启动命令加上
--host ::。
验证效果
修改地址后,轮询请求将直接访问IPv4的127.0.0.1,与Chrome测试的地址一致,就能正确检测到服务器状态,不会再超时退出。
内容的提问来源于stack exchange,提问作者Simon1
相关产品推荐
相关产品推荐

