{"id":1168,"date":"2025-07-12T17:19:15","date_gmt":"2025-07-12T09:19:15","guid":{"rendered":"https:\/\/www.zhaozhao123.cn\/php\/?p=1168"},"modified":"2025-07-12T17:19:15","modified_gmt":"2025-07-12T09:19:15","slug":"php%e6%95%b0%e6%8d%ae%e5%ba%93%e6%b6%88%e6%81%af%e9%98%9f%e5%88%97%e7%9a%84%e5%b9%b6%e5%8f%91%e5%ae%9e%e7%8e%b0","status":"publish","type":"post","link":"https:\/\/www.zhaozhao123.cn\/php\/post\/1168.html","title":{"rendered":"PHP\u6570\u636e\u5e93\u6d88\u606f\u961f\u5217\u7684\u5e76\u53d1\u5b9e\u73b0"},"content":{"rendered":"\n<h1 class=\"wp-block-heading\">\u6570\u636e\u5e93\u6d88\u606f\u961f\u5217\u7684\u5e76\u53d1\u5b9e\u73b0<\/h1>\n\n\n\n<p>\u5728\u57fa\u4e8e\u6570\u636e\u5e93\u7684\u6d88\u606f\u961f\u5217\u4e2d\u5b9e\u73b0\u5e76\u53d1\u5904\u7406\u9700\u8981\u89e3\u51b3\u4e24\u4e2a\u6838\u5fc3\u95ee\u9898\uff1a<strong>\u5b89\u5168\u5730\u5206\u914d\u4efb\u52a1<\/strong>\u548c<strong>\u9ad8\u6548\u5730\u5904\u7406\u4efb\u52a1<\/strong>\u3002\u4e0b\u9762\u6211\u5c06\u8be6\u7ec6\u4ecb\u7ecd\u5b9e\u73b0\u65b9\u6848\u5e76\u63d0\u4f9b\u4e00\u4e2a\u5b8c\u6574\u7684\u53ef\u89c6\u5316\u793a\u4f8b\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\u5b9e\u73b0\u65b9\u6848<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">1. \u5e76\u53d1\u5904\u7406\u7684\u6838\u5fc3\u673a\u5236<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>graph TD\n    A&#91;\u591a\u4e2aWorker\u8fdb\u7a0b] --&gt; B&#91;\u6570\u636e\u5e93\u961f\u5217]\n    B --&gt; C&#91;\u4f7f\u7528FOR UPDATE SKIP LOCKED]\n    C --&gt; D&#91;\u5b89\u5168\u83b7\u53d6\u4efb\u52a1]\n    D --&gt; E&#91;\u5904\u7406\u4efb\u52a1]\n    E --&gt; F&#91;\u66f4\u65b0\u4efb\u52a1\u72b6\u6001]<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">2. \u5173\u952e\u6280\u672f\u70b9<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong><code>SELECT ... FOR UPDATE SKIP LOCKED<\/code><\/strong><\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u5141\u8bb8\u5e76\u53d1worker\u5b89\u5168\u5730\u83b7\u53d6\u4e0d\u540c\u4efb\u52a1<\/li>\n\n\n\n<li>MySQL 8.0+\u548cPostgreSQL 9.5+\u652f\u6301<\/li>\n\n\n\n<li>\u907f\u514d\u9501\u7ade\u4e89\uff0c\u63d0\u9ad8\u5e76\u53d1\u6027\u80fd<\/li>\n<\/ul>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u539f\u5b50\u72b6\u6001\u66f4\u65b0<\/strong><\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u4f7f\u7528\u4e8b\u52a1\u786e\u4fdd\u4efb\u52a1\u72b6\u6001\u53d8\u66f4\u7684\u539f\u5b50\u6027<\/li>\n\n\n\n<li>\u9632\u6b62\u591a\u4e2aworker\u5904\u7406\u540c\u4e00\u4efb\u52a1<\/li>\n<\/ul>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u8fdb\u7a0b\u7ba1\u7406<\/strong><\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u4f7f\u7528Supervisor\u7ba1\u7406\u591a\u4e2aworker\u8fdb\u7a0b<\/li>\n\n\n\n<li>\u81ea\u52a8\u91cd\u542f\u5931\u8d25\u7684\u8fdb\u7a0b<\/li>\n<\/ul>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u8d85\u65f6\u5904\u7406<\/strong><\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u5904\u7406\u4e2d\u72b6\u6001\u7684\u4efb\u52a1\u8bbe\u7f6e\u8d85\u65f6\u65f6\u95f4<\/li>\n\n\n\n<li>\u5b9a\u671f\u91cd\u7f6e\u8d85\u65f6\u4efb\u52a1\u4e3a\u5f85\u5904\u7406\u72b6\u6001<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">\u5b8c\u6574\u5b9e\u73b0\u793a\u4f8b<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;?php\n\/\/ \u6570\u636e\u5e93\u914d\u7f6e\ndefine('DB_HOST', 'localhost');\ndefine('DB_NAME', 'message_queue_db');\ndefine('DB_USER', 'root');\ndefine('DB_PASS', '');\ndefine('DB_CHARSET', 'utf8mb4');\n\nclass DBQueue {\n    private $pdo;\n\n    public function __construct() {\n        $dsn = \"mysql:host=\".DB_HOST.\";dbname=\".DB_NAME.\";charset=\".DB_CHARSET;\n        $options = &#91;\n            PDO::ATTR_ERRMODE =&gt; PDO::ERRMODE_EXCEPTION,\n            PDO::ATTR_DEFAULT_FETCH_MODE =&gt; PDO::FETCH_ASSOC,\n            PDO::ATTR_EMULATE_PREPARES =&gt; false,\n        ];\n\n        try {\n            $this-&gt;pdo = new PDO($dsn, DB_USER, DB_PASS, $options);\n        } catch (PDOException $e) {\n            die(\"\u6570\u636e\u5e93\u8fde\u63a5\u5931\u8d25: \" . $e-&gt;getMessage());\n        }\n    }\n\n    \/\/ \u6dfb\u52a0\u4efb\u52a1\u5230\u961f\u5217\n    public function addTask($queueName, $payload) {\n        $sql = \"INSERT INTO message_queue (queue_name, payload) VALUES (?, ?)\";\n        $stmt = $this-&gt;pdo-&gt;prepare($sql);\n        $stmt-&gt;execute(&#91;$queueName, json_encode($payload)]);\n        return $this-&gt;pdo-&gt;lastInsertId();\n    }\n\n    \/\/ \u83b7\u53d6\u5e76\u9501\u5b9a\u4e00\u4e2a\u4efb\u52a1\n    public function getTask($queueName) {\n        $this-&gt;pdo-&gt;beginTransaction();\n\n        try {\n            \/\/ \u4f7f\u7528SKIP LOCKED\u907f\u514d\u9501\u7ade\u4e89\n            $sql = \"SELECT * FROM message_queue \n                    WHERE queue_name = ? AND status = 0\n                    ORDER BY created_at ASC \n                    LIMIT 1 \n                    FOR UPDATE SKIP LOCKED\";\n\n            $stmt = $this-&gt;pdo-&gt;prepare($sql);\n            $stmt-&gt;execute(&#91;$queueName]);\n            $task = $stmt-&gt;fetch();\n\n            if (!$task) {\n                $this-&gt;pdo-&gt;rollBack();\n                return null;\n            }\n\n            \/\/ \u66f4\u65b0\u4efb\u52a1\u72b6\u6001\u4e3a\u5904\u7406\u4e2d\n            $updateSql = \"UPDATE message_queue \n                         SET status = 1, started_at = NOW() \n                         WHERE id = ?\";\n            $updateStmt = $this-&gt;pdo-&gt;prepare($updateSql);\n            $updateStmt-&gt;execute(&#91;$task&#91;'id']]);\n\n            $this-&gt;pdo-&gt;commit();\n            return $task;\n        } catch (Exception $e) {\n            $this-&gt;pdo-&gt;rollBack();\n            error_log(\"\u83b7\u53d6\u4efb\u52a1\u5931\u8d25: \" . $e-&gt;getMessage());\n            return null;\n        }\n    }\n\n    \/\/ \u6807\u8bb0\u4efb\u52a1\u4e3a\u5b8c\u6210\n    public function completeTask($taskId) {\n        $sql = \"UPDATE message_queue \n               SET status = 2, completed_at = NOW() \n               WHERE id = ?\";\n        $stmt = $this-&gt;pdo-&gt;prepare($sql);\n        return $stmt-&gt;execute(&#91;$taskId]);\n    }\n\n    \/\/ \u6807\u8bb0\u4efb\u52a1\u4e3a\u5931\u8d25\n    public function failTask($taskId, $error) {\n        $sql = \"UPDATE message_queue \n               SET status = 3, error_message = ?, retry_count = retry_count + 1 \n               WHERE id = ?\";\n        $stmt = $this-&gt;pdo-&gt;prepare($sql);\n        return $stmt-&gt;execute(&#91;$error, $taskId]);\n    }\n\n    \/\/ \u91cd\u7f6e\u8d85\u65f6\u4efb\u52a1\n    public function resetTimedOutTasks($timeoutMinutes = 5) {\n        $sql = \"UPDATE message_queue \n               SET status = 0, started_at = NULL \n               WHERE status = 1 \n               AND started_at &lt; NOW() - INTERVAL ? MINUTE\";\n        $stmt = $this-&gt;pdo-&gt;prepare($sql);\n        return $stmt-&gt;execute(&#91;$timeoutMinutes]);\n    }\n}\n\n\/\/ \u521d\u59cb\u5316\u6570\u636e\u5e93\u8868\nfunction initializeDatabase() {\n    $pdo = new PDO(\n        \"mysql:host=\".DB_HOST.\";charset=\".DB_CHARSET, \n        DB_USER, \n        DB_PASS\n    );\n\n    $pdo-&gt;exec(\"CREATE DATABASE IF NOT EXISTS \".DB_NAME);\n    $pdo-&gt;exec(\"USE \".DB_NAME);\n\n    $pdo-&gt;exec(\"CREATE TABLE IF NOT EXISTS message_queue (\n        id INT AUTO_INCREMENT PRIMARY KEY,\n        queue_name VARCHAR(50) NOT NULL DEFAULT 'default',\n        payload TEXT NOT NULL,\n        status TINYINT NOT NULL DEFAULT 0 COMMENT '0=\u7b49\u5f85, 1=\u5904\u7406\u4e2d, 2=\u5b8c\u6210, 3=\u5931\u8d25',\n        retry_count INT NOT NULL DEFAULT 0,\n        error_message TEXT,\n        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n        started_at TIMESTAMP NULL,\n        completed_at TIMESTAMP NULL\n    )\");\n\n    $pdo-&gt;exec(\"CREATE INDEX idx_status ON message_queue (status)\");\n    $pdo-&gt;exec(\"CREATE INDEX idx_queue_name ON message_queue (queue_name)\");\n}\n\n\/\/ \u793a\u4f8b\u4efb\u52a1\u5904\u7406\u5668\nclass TaskProcessor {\n    private $queue;\n\n    public function __construct() {\n        $this-&gt;queue = new DBQueue();\n    }\n\n    public function processTask($task) {\n        $payload = json_decode($task&#91;'payload'], true);\n\n        try {\n            \/\/ \u6a21\u62df\u4e0d\u540c\u7c7b\u578b\u7684\u4efb\u52a1\u5904\u7406\n            switch ($payload&#91;'type']) {\n                case 'email':\n                    $this-&gt;sendEmail($payload);\n                    break;\n                case 'report':\n                    $this-&gt;generateReport($payload);\n                    break;\n                case 'cleanup':\n                    $this-&gt;cleanupData($payload);\n                    break;\n                default:\n                    throw new Exception(\"\u672a\u77e5\u4efb\u52a1\u7c7b\u578b: \".$payload&#91;'type']);\n            }\n\n            \/\/ \u6807\u8bb0\u4efb\u52a1\u5b8c\u6210\n            $this-&gt;queue-&gt;completeTask($task&#91;'id']);\n            return true;\n        } catch (Exception $e) {\n            \/\/ \u6807\u8bb0\u4efb\u52a1\u5931\u8d25\n            $this-&gt;queue-&gt;failTask($task&#91;'id'], $e-&gt;getMessage());\n            return false;\n        }\n    }\n\n    private function sendEmail($data) {\n        \/\/ \u6a21\u62df\u90ae\u4ef6\u53d1\u9001\n        sleep(rand(1, 3)); \/\/ \u968f\u673a\u5904\u7406\u65f6\u95f4\n        if (rand(1, 10) === 1) { \/\/ \u6a21\u62df10%\u7684\u5931\u8d25\u7387\n            throw new Exception(\"\u90ae\u4ef6\u53d1\u9001\u5931\u8d25: SMTP\u9519\u8bef\");\n        }\n        echo \"\u53d1\u9001\u90ae\u4ef6\u81f3: {$data&#91;'to']}\\n\";\n    }\n\n    private function generateReport($data) {\n        \/\/ \u6a21\u62df\u62a5\u544a\u751f\u6210\n        sleep(rand(2, 5));\n        echo \"\u751f\u6210\u62a5\u544a: {$data&#91;'report_name']}\\n\";\n    }\n\n    private function cleanupData($data) {\n        \/\/ \u6a21\u62df\u6570\u636e\u6e05\u7406\n        sleep(rand(1, 4));\n        echo \"\u6e05\u7406\u6570\u636e\u8868: {$data&#91;'table_name']}\\n\";\n    }\n}\n\n\/\/ Worker\u8fdb\u7a0b\nfunction runWorker($queueName) {\n    $queue = new DBQueue();\n    $processor = new TaskProcessor();\n\n    echo \"Worker\u542f\u52a8\uff0c\u76d1\u542c\u961f\u5217: $queueName\\n\";\n\n    while (true) {\n        $task = $queue-&gt;getTask($queueName);\n\n        if ($task) {\n            echo \"\u5f00\u59cb\u5904\u7406\u4efb\u52a1 ID: {$task&#91;'id']}\\n\";\n            $processor-&gt;processTask($task);\n        } else {\n            \/\/ \u6ca1\u6709\u4efb\u52a1\uff0c\u77ed\u6682\u4f11\u7720\n            sleep(1);\n        }\n    }\n}\n\n\/\/ \u5b9a\u65f6\u4efb\u52a1\uff1a\u91cd\u7f6e\u8d85\u65f6\u4efb\u52a1\nfunction runTimeoutReset() {\n    $queue = new DBQueue();\n    while (true) {\n        $resetCount = $queue-&gt;resetTimedOutTasks(5); \/\/ 5\u5206\u949f\u8d85\u65f6\n        if ($resetCount &gt; 0) {\n            echo \"\u5df2\u91cd\u7f6e {$resetCount} \u4e2a\u8d85\u65f6\u4efb\u52a1\\n\";\n        }\n        sleep(60); \/\/ \u6bcf\u5206\u949f\u68c0\u67e5\u4e00\u6b21\n    }\n}\n\n\/\/ \u547d\u4ee4\u884c\u53c2\u6570\u5904\u7406\nif (php_sapi_name() === 'cli') {\n    initializeDatabase();\n\n    $command = $argv&#91;1] ?? null;\n\n    switch ($command) {\n        case 'worker':\n            $queueName = $argv&#91;2] ?? 'default';\n            runWorker($queueName);\n            break;\n\n        case 'timeout-reset':\n            runTimeoutReset();\n            break;\n\n        case 'add-task':\n            $type = $argv&#91;2] ?? 'email';\n            $queue = new DBQueue();\n\n            $payload = &#91;'type' =&gt; $type];\n\n            \/\/ \u6839\u636e\u7c7b\u578b\u6dfb\u52a0\u4e0d\u540c\u53c2\u6570\n            switch ($type) {\n                case 'email':\n                    $payload&#91;'to'] = 'user'.rand(1,100).'@example.com';\n                    $payload&#91;'subject'] = '\u6d4b\u8bd5\u90ae\u4ef6';\n                    break;\n                case 'report':\n                    $payload&#91;'report_name'] = '\u65e5\u62a5_'.date('Ymd');\n                    break;\n                case 'cleanup':\n                    $payload&#91;'table_name'] = 'logs_'.date('Ym');\n                    break;\n            }\n\n            $taskId = $queue-&gt;addTask('default', $payload);\n            echo \"\u6dfb\u52a0\u4efb\u52a1\u6210\u529f\uff0cID: $taskId\\n\";\n            break;\n\n        default:\n            echo \"\u53ef\u7528\u547d\u4ee4:\\n\";\n            echo \"  php queue.php worker &#91;\u961f\u5217\u540d\u79f0] - \u542f\u52a8worker\u8fdb\u7a0b\\n\";\n            echo \"  php queue.php timeout-reset - \u542f\u52a8\u8d85\u65f6\u91cd\u7f6e\u8fdb\u7a0b\\n\";\n            echo \"  php queue.php add-task &#91;\u4efb\u52a1\u7c7b\u578b] - \u6dfb\u52a0\u65b0\u4efb\u52a1\\n\";\n            break;\n    }\n} else {\n    \/\/ Web\u8bbf\u95ee\u663e\u793a\u961f\u5217\u72b6\u6001\n    displayQueueStatus();\n}\n\n\/\/ \u663e\u793a\u961f\u5217\u72b6\u6001\uff08Web\u8bbf\u95ee\uff09\nfunction displayQueueStatus() {\n    $queue = new DBQueue();\n    $pdo = $queue-&gt;getPDO();\n\n    \/\/ \u83b7\u53d6\u961f\u5217\u7edf\u8ba1\u4fe1\u606f\n    $stats = $pdo-&gt;query(\"\n        SELECT \n            queue_name,\n            SUM(CASE WHEN status = 0 THEN 1 ELSE 0 END) as pending,\n            SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END) as processing,\n            SUM(CASE WHEN status = 2 THEN 1 ELSE 0 END) as completed,\n            SUM(CASE WHEN status = 3 THEN 1 ELSE 0 END) as failed,\n            COUNT(*) as total\n        FROM message_queue\n        GROUP BY queue_name\n    \")-&gt;fetchAll(PDO::FETCH_ASSOC);\n\n    \/\/ \u83b7\u53d6\u6700\u8fd1\u4efb\u52a1\n    $recentTasks = $pdo-&gt;query(\"\n        SELECT * FROM message_queue \n        ORDER BY created_at DESC \n        LIMIT 10\n    \")-&gt;fetchAll(PDO::FETCH_ASSOC);\n\n    \/\/ \u6e32\u67d3HTML\u754c\u9762\n    echo '&lt;!DOCTYPE html&gt;\n    &lt;html lang=\"zh-CN\"&gt;\n    &lt;head&gt;\n        &lt;meta charset=\"UTF-8\"&gt;\n        &lt;meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"&gt;\n        &lt;title&gt;\u6570\u636e\u5e93\u6d88\u606f\u961f\u5217\u76d1\u63a7&lt;\/title&gt;\n        &lt;style&gt;\n            body { font-family: Arial, sans-serif; margin: 20px; }\n            .container { max-width: 1200px; margin: 0 auto; }\n            .stats { display: flex; flex-wrap: wrap; gap: 20px; margin-bottom: 30px; }\n            .stat-card { background: #f8f9fa; border: 1px solid #dee2e6; border-radius: 5px; padding: 15px; flex: 1; min-width: 200px; }\n            .stat-card h3 { margin-top: 0; }\n            .queue-name { font-weight: bold; color: #0d6efd; }\n            table { width: 100%; border-collapse: collapse; margin-bottom: 20px; }\n            th, td { border: 1px solid #dee2e6; padding: 8px; text-align: left; }\n            th { background-color: #f8f9fa; }\n            .status-0 { background-color: #fff3cd; }\n            .status-1 { background-color: #cff4fc; }\n            .status-2 { background-color: #d1e7dd; }\n            .status-3 { background-color: #f8d7da; }\n        &lt;\/style&gt;\n    &lt;\/head&gt;\n    &lt;body&gt;\n        &lt;div class=\"container\"&gt;\n            &lt;h1&gt;\u6570\u636e\u5e93\u6d88\u606f\u961f\u5217\u76d1\u63a7&lt;\/h1&gt;\n\n            &lt;div class=\"stats\"&gt;';\n\n    foreach ($stats as $stat) {\n        echo '&lt;div class=\"stat-card\"&gt;\n                &lt;h3&gt;\u961f\u5217: &lt;span class=\"queue-name\"&gt;'.$stat&#91;'queue_name'].'&lt;\/span&gt;&lt;\/h3&gt;\n                &lt;p&gt;\u5f85\u5904\u7406: '.$stat&#91;'pending'].'&lt;\/p&gt;\n                &lt;p&gt;\u5904\u7406\u4e2d: '.$stat&#91;'processing'].'&lt;\/p&gt;\n                &lt;p&gt;\u5df2\u5b8c\u6210: '.$stat&#91;'completed'].'&lt;\/p&gt;\n                &lt;p&gt;\u5931\u8d25: '.$stat&#91;'failed'].'&lt;\/p&gt;\n                &lt;p&gt;&lt;strong&gt;\u603b\u8ba1: '.$stat&#91;'total'].'&lt;\/strong&gt;&lt;\/p&gt;\n            &lt;\/div&gt;';\n    }\n\n    echo '&lt;\/div&gt;\n\n        &lt;h2&gt;\u6700\u8fd1\u4efb\u52a1&lt;\/h2&gt;\n        &lt;table&gt;\n            &lt;thead&gt;\n                &lt;tr&gt;\n                    &lt;th&gt;ID&lt;\/th&gt;\n                    &lt;th&gt;\u961f\u5217&lt;\/th&gt;\n                    &lt;th&gt;\u7c7b\u578b&lt;\/th&gt;\n                    &lt;th&gt;\u72b6\u6001&lt;\/th&gt;\n                    &lt;th&gt;\u521b\u5efa\u65f6\u95f4&lt;\/th&gt;\n                    &lt;th&gt;\u5f00\u59cb\u65f6\u95f4&lt;\/th&gt;\n                    &lt;th&gt;\u5b8c\u6210\u65f6\u95f4&lt;\/th&gt;\n                    &lt;th&gt;\u91cd\u8bd5\u6b21\u6570&lt;\/th&gt;\n                &lt;\/tr&gt;\n            &lt;\/thead&gt;\n            &lt;tbody&gt;';\n\n    foreach ($recentTasks as $task) {\n        $payload = json_decode($task&#91;'payload'], true);\n        $statusText = &#91;'\u7b49\u5f85', '\u5904\u7406\u4e2d', '\u5b8c\u6210', '\u5931\u8d25']&#91;$task&#91;'status']];\n\n        echo '&lt;tr class=\"status-'.$task&#91;'status'].'\"&gt;\n                &lt;td&gt;'.$task&#91;'id'].'&lt;\/td&gt;\n                &lt;td&gt;'.$task&#91;'queue_name'].'&lt;\/td&gt;\n                &lt;td&gt;'.($payload&#91;'type'] ?? '').'&lt;\/td&gt;\n                &lt;td&gt;'.$statusText.'&lt;\/td&gt;\n                &lt;td&gt;'.$task&#91;'created_at'].'&lt;\/td&gt;\n                &lt;td&gt;'.$task&#91;'started_at'].'&lt;\/td&gt;\n                &lt;td&gt;'.$task&#91;'completed_at'].'&lt;\/td&gt;\n                &lt;td&gt;'.$task&#91;'retry_count'].'&lt;\/td&gt;\n            &lt;\/tr&gt;';\n    }\n\n    echo '&lt;\/tbody&gt;\n        &lt;\/table&gt;\n    &lt;\/div&gt;\n    &lt;\/body&gt;\n    &lt;\/html&gt;';\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">\u5e76\u53d1\u7ba1\u7406\u6700\u4f73\u5b9e\u8df5<\/h2>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u4f7f\u7528Supervisor\u7ba1\u7406\u8fdb\u7a0b<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code>   ; \/etc\/supervisor\/conf.d\/queue_worker.conf<\/code><\/pre>\n\n\n<p>[program:queue_worker]<\/p>\n\n\n\n<p>command=php \/path\/to\/queue.php worker default process_name=%(program_name)s_%(process_num)02d numprocs=4 ; \u542f\u52a84\u4e2aworker\u8fdb\u7a0b autostart=true autorestart=true user=www-data redirect_stderr=true stdout_logfile=\/var\/log\/queue_worker.log<\/p>\n\n\n<p>[program:queue_timeout_reset]<\/p>\n\n\n\n<p>command=php \/path\/to\/queue.php timeout-reset autostart=true autorestart=true user=www-data redirect_stderr=true stdout_logfile=\/var\/log\/queue_timeout_reset.log<\/p>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u6570\u636e\u5e93\u4f18\u5316\u6280\u5de7<\/strong><\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u4e3a<code>status<\/code>\u548c<code>queue_name<\/code>\u5b57\u6bb5\u521b\u5efa\u7d22\u5f15<\/li>\n\n\n\n<li>\u5b9a\u671f\u5f52\u6863\u5df2\u5b8c\u6210\u7684\u4efb\u52a1<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>   -- \u521b\u5efa\u7d22\u5f15\n   CREATE INDEX idx_status ON message_queue (status);\n   CREATE INDEX idx_queue_name ON message_queue (queue_name);\n\n   -- \u5f52\u6863\u65e7\u4efb\u52a1\n   CREATE TABLE message_queue_archive LIKE message_queue;\n   INSERT INTO message_queue_archive \n   SELECT * FROM message_queue \n   WHERE status = 2 AND completed_at &lt; NOW() - INTERVAL 30 DAY;\n\n   DELETE FROM message_queue \n   WHERE status = 2 AND completed_at &lt; NOW() - INTERVAL 30 DAY;<\/code><\/pre>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li><strong>\u5904\u7406\u6b7b\u9501\u548c\u91cd\u8bd5<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code>   public function getTask($queueName) {\n       $maxRetries = 3;\n       $retryCount = 0;\n\n       while ($retryCount &lt; $maxRetries) {\n           try {\n               \/\/ \u5c1d\u8bd5\u83b7\u53d6\u4efb\u52a1...\n               return $task;\n           } catch (PDOException $e) {\n               \/\/ \u68c0\u67e5\u662f\u5426\u4e3a\u6b7b\u9501\u9519\u8bef\n               if ($e-&gt;errorInfo&#91;1] == 1213) { \/\/ MySQL\u6b7b\u9501\u9519\u8bef\u4ee3\u7801\n                   $retryCount++;\n                   usleep(100000); \/\/ \u7b49\u5f85100ms\u540e\u91cd\u8bd5\n                   continue;\n               }\n               throw $e;\n           }\n       }\n       return null;\n   }<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">\u6027\u80fd\u4f18\u5316\u5efa\u8bae<\/h2>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u5206\u8868\u5206\u533a<\/strong><\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u6309\u961f\u5217\u540d\u79f0\u5206\u8868\uff1a<code>queue_emails<\/code>, <code>queue_reports<\/code><\/li>\n\n\n\n<li>\u6309\u65f6\u95f4\u5206\u533a\uff1a\u6bcf\u6708\u4e00\u4e2a\u5206\u533a<\/li>\n<\/ul>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u8fde\u63a5\u6c60<\/strong><\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u4f7f\u7528\u8fde\u63a5\u6c60\u51cf\u5c11\u6570\u636e\u5e93\u8fde\u63a5\u5f00\u9500<\/li>\n\n\n\n<li>\u63a8\u8350\u5de5\u5177\uff1a<code>Swoole\\Database\\PDOPool<\/code><\/li>\n<\/ul>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u6279\u91cf\u5904\u7406<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code>   \/\/ \u4e00\u6b21\u83b7\u53d6\u591a\u4e2a\u4efb\u52a1\n   $sql = \"SELECT * FROM message_queue \n          WHERE queue_name = ? AND status = 0\n          ORDER BY created_at ASC \n          LIMIT 10 \n          FOR UPDATE SKIP LOCKED\";<\/code><\/pre>\n\n\n\n<ol start=\"4\" class=\"wp-block-list\">\n<li><strong>\u8bfb\u5199\u5206\u79bb<\/strong><\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u5199\u64cd\u4f5c\u4f7f\u7528\u4e3b\u5e93<\/li>\n\n\n\n<li>\u8bfb\u64cd\u4f5c\u4f7f\u7528\u4ece\u5e93<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">\u603b\u7ed3<\/h2>\n\n\n\n<p>\u901a\u8fc7\u5408\u7406\u4f7f\u7528\u6570\u636e\u5e93\u7684<code>SKIP LOCKED<\/code>\u7279\u6027\u3001\u8fdb\u7a0b\u7ba1\u7406\u548c\u76d1\u63a7\u673a\u5236\uff0c\u53ef\u4ee5\u5728\u57fa\u4e8e\u6570\u636e\u5e93\u7684\u6d88\u606f\u961f\u5217\u4e2d\u5b9e\u73b0\u5b89\u5168\u9ad8\u6548\u7684\u5e76\u53d1\u5904\u7406\u3002\u5173\u952e\u70b9\u5305\u62ec\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u4f7f\u7528<code>SELECT ... FOR UPDATE SKIP LOCKED<\/code>\u5b89\u5168\u83b7\u53d6\u4efb\u52a1<\/li>\n\n\n\n<li>\u4f7f\u7528Supervisor\u7ba1\u7406\u591a\u4e2aworker\u8fdb\u7a0b<\/li>\n\n\n\n<li>\u5b9e\u73b0\u4efb\u52a1\u8d85\u65f6\u91cd\u7f6e\u673a\u5236<\/li>\n\n\n\n<li>\u6dfb\u52a0\u9002\u5f53\u7684\u6570\u636e\u5e93\u7d22\u5f15<\/li>\n\n\n\n<li>\u63d0\u4f9b\u76d1\u63a7\u754c\u9762\u53ef\u89c6\u5316\u961f\u5217\u72b6\u6001<\/li>\n<\/ol>\n\n\n\n<p>\u5bf9\u4e8e\u66f4\u9ad8\u541e\u5410\u91cf\u7684\u573a\u666f\uff08&gt;100 TPS\uff09\uff0c\u5efa\u8bae\u8003\u8651\u8fc1\u79fb\u5230Redis\u6216\u4e13\u4e1a\u6d88\u606f\u961f\u5217\u7cfb\u7edf\uff08\u5982RabbitMQ\u3001Kafka\uff09\u3002\u4f46\u5bf9\u4e8e\u4e2d\u5c0f\u578b\u5e94\u7528\uff0c\u6570\u636e\u5e93\u961f\u5217\u4ecd\u7136\u662f\u4e00\u4e2a\u7b80\u5355\u53ef\u9760\u7684\u89e3\u51b3\u65b9\u6848\u3002<\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u6570\u636e\u5e93\u6d88\u606f\u961f\u5217\u7684\u5e76\u53d1\u5b9e\u73b0 \u5728\u57fa\u4e8e\u6570\u636e\u5e93\u7684\u6d88\u606f\u961f\u5217\u4e2d\u5b9e\u73b0\u5e76\u53d1\u5904\u7406\u9700\u8981\u89e3\u51b3\u4e24\u4e2a\u6838\u5fc3\u95ee\u9898\uff1a\u5b89\u5168\u5730\u5206\u914d\u4efb\u52a1\u548c\u9ad8\u6548\u5730\u5904\u7406\u4efb\u52a1\u3002\u4e0b\u9762\u6211\u5c06\u8be6\u7ec6\u4ecb\u7ecd\u5b9e\u73b0\u65b9\u6848\u5e76\u63d0\u4f9b\u4e00\u4e2a\u5b8c\u6574\u7684\u53ef\u89c6\u5316\u793a\u4f8b\u3002 \u5b9e\u73b0\u65b9\u6848 1. \u5e76\u53d1\u5904\u7406\u7684\u6838\u5fc3\u673a\u5236 2. \u5173\u952e\u6280\u672f\u70b9 \u5b8c\u6574\u5b9e\u73b0\u793a\u4f8b \u5e76\u53d1\u7ba1\u7406\u6700\u4f73\u5b9e\u8df5 command=php \/path\/to\/queue.php w..<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[],"tuisongtax":[],"class_list":["post-1168","post","type-post","status-publish","format-standard","hentry","category-jswz"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.zhaozhao123.cn\/php\/wp-json\/wp\/v2\/posts\/1168","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.zhaozhao123.cn\/php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.zhaozhao123.cn\/php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.zhaozhao123.cn\/php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.zhaozhao123.cn\/php\/wp-json\/wp\/v2\/comments?post=1168"}],"version-history":[{"count":0,"href":"https:\/\/www.zhaozhao123.cn\/php\/wp-json\/wp\/v2\/posts\/1168\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.zhaozhao123.cn\/php\/wp-json\/wp\/v2\/media?parent=1168"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.zhaozhao123.cn\/php\/wp-json\/wp\/v2\/categories?post=1168"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.zhaozhao123.cn\/php\/wp-json\/wp\/v2\/tags?post=1168"},{"taxonomy":"tuisongtax","embeddable":true,"href":"https:\/\/www.zhaozhao123.cn\/php\/wp-json\/wp\/v2\/tuisongtax?post=1168"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}