You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PHP命令行脚本多线程集成咨询:解决Telnet耗时与DB并发写入问题

Hey Michele, let's break down how to tackle your PHP CLI multi-threading issue for those slow Telnet operations—here's a practical approach tailored to your scenario:

Core Problem Recap

You're stuck integrating multi-threading into your CLI script because:

  • Telnet operations are too slow when run sequentially
  • You can't safely use $stmt2 to write to the database concurrently
  • You're currently iterating through devices with $stmt->fetch and need a better task-based approach
Solution: Separate Telnet Work from Database Writes

The key fix here is to decouple the slow Telnet tasks (which benefit from parallelism) from database writes (which need to be handled sequentially to avoid race conditions or connection issues). Here are two reliable methods for PHP CLI:


Method 1: Use Symfony Process (Third-Party, Easier)

This library simplifies spawning and managing child processes without low-level PCNTL calls.

Step 1: Install the Library

composer require symfony/process

Step 2: Main Script Logic

First, fetch all your devices upfront, then spawn a child process for each Telnet task. Collect results in the main process, then write everything to the database at once.

<?php
require 'vendor/autoload.php';

use Symfony\Component\Process\Process;

// 1. Fetch all devices first (avoids holding a DB cursor open during parallel tasks)
$devices = [];
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
    $devices[] = $row;
}

// 2. Spawn processes for Telnet tasks
$processes = [];
foreach ($devices as $index => $device) {
    // Pass device details to a dedicated Telnet handler script
    $process = new Process([
        'php', 
        'telnet_handler.php', 
        $device['ip'], 
        $device['port'],
        $device['id'] // Pass device ID to link results later
    ]);
    $process->start();
    $processes[$index] = ['process' => $process, 'device' => $device];
}

// 3. Wait for all processes to finish and collect results
$telnetResults = [];
foreach ($processes as $procData) {
    $procData['process']->wait();
    
    if ($procData['process']->isSuccessful()) {
        $telnetResults[] = [
            'device_id' => $procData['device']['id'],
            'output' => $procData['process']->getOutput()
        ];
    } else {
        // Log failures instead of crashing
        error_log("Telnet failed for {$procData['device']['ip']}: " . $procData['process']->getErrorOutput());
    }
}

// 4. Write all results to DB sequentially (safe, no concurrency issues)
$stmt2 = $pdo->prepare("INSERT INTO device_telnet_results (device_id, output, created_at) VALUES (:device_id, :output, NOW())");
foreach ($telnetResults as $result) {
    $stmt2->execute($result);
}

Step 3: Telnet Handler Script (telnet_handler.php)

This script runs in isolation per device, handling only the Telnet logic:

<?php
// Get device details from CLI arguments
$ip = $argv[1];
$port = $argv[2];
$deviceId = $argv[3];

// Execute Telnet logic (adjust this to match your actual commands)
$socket = fsockopen($ip, $port, $errno, $errstr, 30);
if (!$socket) {
    echo "Connection failed: $errstr ($errno)";
    exit(1);
}

// Send command and read response
fwrite($socket, "show status\r\n");
$output = fread($socket, 2048);
fclose($socket);

// Output the result (main script will capture this)
echo $output;
exit(0);

Method 2: Use PCNTL (Native PHP Extension)

If you prefer not to use third-party libraries, you can use PHP's built-in PCNTL extension for forking processes.

Example Script

<?php
// Enable PCNTL signals (optional but helpful for cleanup)
pcntl_signal(SIGCHLD, SIG_IGN);

// 1. Fetch all devices
$devices = [];
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
    $devices[] = $row;
}

$tmpDir = sys_get_temp_dir();
$childPids = [];

// 2. Fork child processes for each device
foreach ($devices as $device) {
    $pid = pcntl_fork();
    
    if ($pid === -1) {
        die("Failed to fork process for {$device['ip']}");
    } elseif ($pid === 0) {
        // Child process: handle Telnet and save result to temp file
        $socket = fsockopen($device['ip'], $device['port'], $errno, $errstr, 30);
        $result = $socket ? fread($socket, 2048) : "Error: $errstr ($errno)";
        fclose($socket);
        
        $tmpFile = "$tmpDir/device_{$device['id']}_result.tmp";
        file_put_contents($tmpFile, $result);
        exit(0); // Exit child process to avoid duplication
    } else {
        // Parent process: track child PID and temp file
        $childPids[$pid] = [
            'device_id' => $device['id'],
            'tmp_file' => "$tmpDir/device_{$device['id']}_result.tmp"
        ];
    }
}

// 3. Wait for all children to finish and collect results
$telnetResults = [];
foreach ($childPids as $pid => $data) {
    pcntl_waitpid($pid, $status);
    if (file_exists($data['tmp_file'])) {
        $telnetResults[] = [
            'device_id' => $data['device_id'],
            'output' => file_get_contents($data['tmp_file'])
        ];
        unlink($data['tmp_file']); // Clean up temp file
    }
}

// 4. Write to database sequentially
$stmt2 = $pdo->prepare("INSERT INTO device_telnet_results (device_id, output) VALUES (:device_id, :output)");
foreach ($telnetResults as $result) {
    $stmt2->execute($result);
}

Key Notes to Avoid Headaches
  • Never share DB connections across processes: Child processes inherit the parent's connection, but it will break once the parent or child tries to use it. Always handle DB writes in the main process.
  • Limit concurrency: If you have hundreds of devices, don't spawn a process for all at once—use a queue system to cap parallel processes (e.g., run 10 at a time) to avoid overwhelming your server.
  • Error handling: Always log failures instead of letting the script crash. Telnet connections will fail occasionally, and you need to track which devices had issues.

内容的提问来源于stack exchange,提问作者Michele

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 12:33:58