在创建电子浏览器窗口之前等待 python 服务器启动的问题

问题描述 投票:0回答:1

我正在尝试在我的电子应用程序中实现一种轮询机制,该机制会等到我的 python 服务器启动后再创建浏览器窗口。我需要这个,因为如果服务器启动太慢,那么我必须刷新应用程序才能从服务器获取数据。

我实现了一个 10 秒计时器。当我使用

electron-forge start
启动应用程序时,服务器将在大约 1 秒后启动。您可以在控制台窗口中看到这一点,并且我能够在 chrome 浏览器中成功到达运行状况检查路由(控制台窗口中的信息行是我刷新浏览器以证明我已连接)。由于某种原因,我的轮询机制的实现会不断检查,直到达到 10 秒,然后应用程序退出。

我确实做了一些谷歌搜索,但找不到答案。

问题:为什么Python服务器启动后该机制没有停止?

这是我的

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
node.js async-await electron
1个回答
0
投票

我认为有两个问题:

  1. 需要一个新的异步函数来启动 python 服务器并且
  2. 不要使用
    http://localhost:xxx
    ,而使用
    http://127.0.0.1:xxx

第一个更改不足以使代码正常工作,但在做了更多研究后,似乎

axios
在使用
localhost
时出现问题,因此将其切换为
127.0.0.1
有效。

main.ts

import { app, BrowserWindow, ipcMain } from 'electron';
import { spawn, exec } from 'child_process';
import path from 'path';
import axios from 'axios';

const PORT = 8036;

// Function to check if python server is running
async function isServerRunning(url: string) {
  try {
    const response = await axios.get(url);
    return response.status === 200;
  } catch (error) {
    return false;
  }
}

// Function to ping server to see if it is running
async function waitForServer(url: string) {
  const maxRetries = 20; // Set max retries
  let attempts = 0;

  while (attempts < maxRetries) {
    console.log(`Attempt #${attempts + 1}`);
    const isRunning = await isServerRunning(url);
    if (isRunning) {
      return true;
    }
    attempts++;
    await new Promise((resolve) => setTimeout(resolve, 1000));
  }
  throw new Error('Unable to connect to server.');
}

// Function to start server in dev or production
async function startServer() {
  if (MAIN_WINDOW_VITE_DEV_SERVER_URL) {
    spawn('python backend/main.py', {
      detached: true,
      shell: true,
      stdio: 'inherit',
    });
  } else {
    ...
  }
}

// Shut down server and electron
const shutdown = () => {
  // Kill app executable if production
  if (!MAIN_WINDOW_VITE_DEV_SERVER_URL) {
    const killcmd = {
      darwin: 'killall App',
      linux: '',
      win32: 'taskkill /f /t /im App.exe',
      aix: undefined,
      android: undefined,
      cygwin: undefined,
      freebsd: undefined,
      haiku: undefined,
      netbsd: undefined,
      openbsd: undefined,
      sunos: undefined,
    }[process.platform];

    exec(killcmd, (err, stdout, stderr) => {
      if (err) {
        console.log(err);
        return;
      }
      console.log(`stdout: ${stdout}`);
      console.log(`stderr: ${stderr}`);
    });
  }

  axios
    .get(`http://127.0.0.1:${PORT}/quit`)
    .then(() => app.quit())
    .catch(() => app.quit());
};

if (require('electron-squirrel-startup')) {
  app.quit();
}

const createWindow = () => {
  // Create the browser window.
  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 () => {
  await startServer();

  try {
    await waitForServer(`http://127.0.0.1:${PORT}/health-check`);
    createWindow();
  } catch (error) {
    console.error(error);
    app.quit();
  }

  app.on('activate', () => {
    if (BrowserWindow.getAllWindows().length === 0) createWindow();
  });

  app.on('window-all-closed', () => {
    if (process.platform !== 'darwin') {
      shutdown();
    }
  });
});
© www.soinside.com 2019 - 2024. All rights reserved.