核心業務需同時寫入「線上正式庫」與「開發測試庫」。測試庫結構頻繁變更、不穩定,若同步寫入導致測試庫錯誤拋出異常,將回滾正式庫事務,造成嚴重可用性損失。
a.php) 處理完核心邏輯,寫入正式庫並回應用戶。b.php。b.php 接收陣列,獨立連線寫入測試庫,失敗僅記錄日誌,不影響主流程。<?php
// ... 核心業務邏輯,寫入正式 DB ...
$data = [
'username' => '張三',
'password' => 'secret',
'ip' => $_SERVER['REMOTE_ADDR'],
'action' => 'create_order',
'payload' => $orderDetails
];
// 關閉函數:確保即使用戶關閉瀏覽器也會執行
register_shutdown_function('asyncPost', $data);
function asyncPost($data) {
$curl = new Curl_Class();
// 目標 b.php 內網地址,超時 3 秒,失敗不拋出
$curl->post('https://10.0.0.5/b.php', $data, '', '', '', '', 3);
}
class Curl_Class {
public function post($url, $fields, $userAgent='', $headers='', $user='', $pass='', $timeout=3) {
return $this->execute('POST', $url, $fields, $userAgent, $headers, $user, $pass, $timeout);
}
private function execute($method, $url, $fields, $ua, $headers, $user, $pass, $timeout) {
$ch = curl_init();
if (!$ch) return false;
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HEADER => false,
CURLOPT_TIMEOUT => $timeout,
CURLOPT_CONNECTTIMEOUT => 2,
CURLOPT_NOSIGNAL => 1, // 多執行緒環境下避免信號衝突
CURLOPT_FORBID_REUSE => true, // 短連線,用完即棄
CURLOPT_FRESH_CONNECT => true,
]);
if ($user) curl_setopt($ch, CURLOPT_USERPWD, "$user:$pass");
if ($ua) curl_setopt($ch, CURLOPT_USERAGENT, $ua);
if ($headers) curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
if (strtoupper($method) === 'POST') {
curl_setopt($ch, CURLOPT_POST, true);
if (is_array($fields)) {
$postFields = [];
foreach ($fields as $k => $v) $postFields[] = "$k=" . urlencode($v);
$fields = implode('&', $postFields);
}
curl_setopt($ch, CURLOPT_POSTFIELDS, $fields);
}
$ret = curl_exec($ch);
if (curl_errno($ch)) {
error_log("Curl asyncPost failed: " . curl_error($ch));
curl_close($ch);
return false;
}
curl_close($ch);
return $ret;
}
}
?>
<?php
ignore_user_abort(true); // 即使發起端斷開,仍完成處理
set_time_limit(30);
$raw = file_get_contents('php://input'); // 獲取原始 POST body
parse_str($raw, $data); // 還原為關聯陣列
// 現在 $data 結構與 a.php 完全一致,可直接寫入測試庫
// try { $testDb->insert('log', $data); } catch (Exception $e) { error_log($e->getMessage()); }
?>
CURLOPT_TIMEOUT=3 確保主程式不被掛起。php://input + parse_str() 完美還原一維陣列;多維陣列建議 json_encode/decode。