PHP cURL单次POST请求数据重复存入数据库问题排查
问题描述
我通过PHP cURL调用自定义网站API执行单次POST请求提交数据,流程看似正常,但数据库中却重复存入了相同的数据。我尝试开启/关闭RETURNTRANSFER、FOLLOWLOCATION、VERBOSE参数,也试过ob_start、ob_get_contents、ob_end_clean方法,但重复存储的问题始终存在。
我的PHP cURL代码
$curl = curl_init(); curl_setopt($curl, CURLOPT_URL, $url); curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); curl_setopt($curl, CURLOPT_USERPWD, $username . ':' . $pw); curl_setopt($curl, CURLOPT_HEADER, 1); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); curl_setopt($curl, CURLOPT_MAXREDIRS, 10); curl_setopt($curl, CURLOPT_TIMEOUT, 30); curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false); curl_setopt($curl, CURLOPT_VERBOSE, 0); curl_setopt($curl, CURLOPT_HTTPHEADER, ['Content-type: multipart/form-data']); curl_setopt($curl, CURLOPT_HTTPHEADER, ['Expect:']); curl_setopt($curl, CURLOPT_HTTPHEADER, ['Accept: application/json']); $adat = []; $adat['data[sortOrder]'] = 1; $adat['data[status]'] = 1; $adat['data[productsStatus]'] = 1; $adat['data[groupCode]'] = NULL; $adat['data[parentCategory][id]'] = ''; $adat['data[centralCategory][id]'] = ''; $adat['data[categoryDescriptions][0][name]'] = 'Test cat 1'; $adat['data[categoryDescriptions][0][description]'] = 'Test cat 1 desc'; $adat['data[categoryDescriptions][0][language][id]'] = 'bGFuZ3VhZ2UtbGFuZ3VhZ2VfaWQ9MQ=='; curl_setopt($curl, CURLOPT_POST, true); curl_setopt($curl, CURLOPT_POSTFIELDS, $adat); //ob_start(); $response = curl_exec($curl); //$response = ob_get_contents(); //ob_end_clean(); if ($response === false) { echo curl_error($curl); } $header_size = curl_getinfo($curl, CURLINFO_HEADER_SIZE); $httpReturnCode = curl_getinfo($curl, CURLINFO_HTTP_CODE); $contentType = curl_getinfo($curl, CURLINFO_CONTENT_TYPE); $lastURL = curl_getinfo($curl, CURLINFO_EFFECTIVE_URL); $data = substr($response, curl_getinfo($curl, CURLINFO_HEADER_SIZE)); curl_close($curl); echo '<pre>'; print_r(json_decode($data)); echo '</pre>';
cURL请求日志
* Trying x.x.x.x:80... * Connected to api.url.example.address (x.x.x.x) port 80 (#0) * Server auth using Basic with user 'username' > POST /categoryExtend HTTP/1.1 Host: api.url.example.address Authorization: Basic Accept: application/json Content-Length: 1144 Content-Type: multipart/form-data; boundary=------------------------b05e0dbf7a681e1e * We are completely uploaded and fine * Mark bundle as not supporting multiuse < HTTP/1.1 200 OK < Server: openresty/1.13.6.1 < Date: Sat, 08 Apr 2023 18:36:36 GMT < Content-Type: application/json < Content-Length: 1205 < Connection: keep-alive < Cache-Control: max-age=0, public < Content-Security-Policy-Report-Only: frame-ancestors 'self'; report-uri /csp_logger < Expires: Sat, 08 Apr 2023 18:36:36 GMT < Vary: Accept-Encoding < X-Content-Security-Policy-Report-Only: frame-ancestors 'self'; report-uri /csp_logger < X-UA-Compatible: IE=Edge < X-WebKit-CSP-Report-Only: frame-ancestors 'self'; report-uri /csp_logger < X-LB: api < X-LB: api < * Connection #0 to host api.url.example.address left intact
问题排查与解决方案
从日志和代码来看,cURL仅发起了一次请求,但数据库重复存储,可从以下方向排查修复:
修复HTTP头覆盖问题
你多次调用curl_setopt($curl, CURLOPT_HTTPHEADER, [...]),会覆盖之前的头信息,最终仅Accept: application/json生效,Content-type: multipart/form-data被丢弃。虽然cURL会自动生成该头,但手动设置被覆盖可能导致服务端解析异常,触发重复处理。
修复方式:将所有HTTP头放入一个数组一次性设置:curl_setopt($curl, CURLOPT_HTTPHEADER, [ 'Content-type: multipart/form-data', 'Expect:', 'Accept: application/json' ]);服务端防重复处理
日志显示返回200 OK,但服务端可能因负载均衡(日志出现两次X-LB: api)、网络延迟导致请求被重复转发处理。- 给请求添加唯一标识:在POST数据中加入
request_id字段(用UUID生成),服务端校验该ID,避免重复处理相同请求。 - 检查服务端请求日志,确认是否收到多次相同请求。
- 给请求添加唯一标识:在POST数据中加入
限制cURL隐式重试
部分场景下cURL可能因连接不稳定触发隐式重试,可添加参数限制:curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 10); // 限制连接超时时间 curl_setopt($curl, CURLOPT_NOSIGNAL, true); // 避免信号干扰导致的重试脚本防重复执行
检查PHP脚本是否被意外重复调用(如浏览器刷新、CRON任务重复触发、前端重复提交),可添加文件锁机制:$lockFile = fopen('/tmp/category_post.lock', 'w'); if (!flock($lockFile, LOCK_EX | LOCK_NB)) { exit('请求正在处理中'); } // 执行cURL请求的代码 flock($lockFile, LOCK_UN); fclose($lockFile);
内容的提问来源于stack exchange,提问作者Jani88
相关产品推荐
相关产品推荐

