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

PHP异步SOAP服务器如何先发送自定义ACK再返回响应?

PHP实现SOAP服务先返回自定义确认消息的方案

首先得明确:标准的SOAP/HTTP是单请求单响应的无状态模型,PHP内置的SOAP扩展和Zend SOAP都是基于这个模型设计的,所以直接用它们确实没法实现“先返回确认消息再返回最终响应”的需求——这也是你找不到现成示例的原因。不过我们可以通过两种思路绕开这个限制,下面是具体的实现方案:

方案一:异步处理+主动发送确认与最终响应(推荐)

这是最可靠、兼容性最好的方案,完全符合HTTP和SOAP的设计逻辑。核心思路是把整个流程拆成两个独立的SOAP交互:

  1. 收到客户端的SubmitInv/RequestInv请求后,立即同步返回自定义的确认消息
  2. 将请求放入异步任务队列,后台处理业务逻辑
  3. 处理完成后,主动调用客户端的SOAP接口返回最终的业务响应

具体实现步骤

  1. 禁用输出缓冲并手动发送确认响应
    要绕过标准SOAP库的响应处理,我们需要直接控制HTTP输出:
// 禁用所有输出缓冲
ob_end_clean();
ignore_user_abort(true); // 确保后台处理不受客户端断开影响
set_time_limit(0); // 取消脚本执行时间限制

// 从原始SOAP请求中解析出RqUID和PKey(示例用DOM解析,你可以根据实际请求调整)
$requestXml = file_get_contents('php://input');
$dom = new DOMDocument();
$dom->loadXML($requestXml);
$rqUid = $dom->getElementsByTagName('RqUID')->item(0)->nodeValue;
$pKey = $dom->getElementsByTagName('PKey')->item(0)->nodeValue;

// 构建确认消息的SOAP XML
$ackXml = <<<XML
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
 <soap:Body>
 <PAddRs>
 <RqUID>$rqUid</RqUID>
 <PKey>$pKey</PKey>
 <AppStatus>
 <AppStatusCode>Accept</AppStatusCode>
 </AppStatus>
 </PAddRs>
 </soap:Body>
</soap:Envelope>
XML;

// 设置HTTP响应头并发送确认消息
header('Content-Type: text/xml; charset=utf-8');
header('Content-Length: ' . strlen($ackXml));
echo $ackXml;
flush(); // 强制输出到客户端

// PHP-FPM环境下,立即结束HTTP连接,后续代码后台执行
if (function_exists('fastcgi_finish_request')) {
    fastcgi_finish_request();
}
  1. 异步处理请求
    把请求数据存入任务队列(比如Redis、RabbitMQ,或者简单的数据库队列),然后用后台进程(比如Crontab定时脚本、Supervisor守护进程)处理队列中的任务:
// 将请求数据存入Redis队列
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$requestData = [
    'raw_request' => $requestXml,
    'rq_uid' => $rqUid,
    'client_endpoint' => 'https://partner.example.com/soap-response-endpoint' // 客户端接收最终响应的地址
];
$redis->lPush('soap_task_queue', json_encode($requestData));
  1. 处理完成后主动发送最终响应
    后台进程处理完任务后,调用客户端的SOAP接口返回最终结果:
// 后台处理脚本示例
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);

while ($task = $redis->rPop('soap_task_queue')) {
    $taskData = json_decode($task, true);
    
    // 这里替换成你的业务逻辑处理(比如生成发票、查询数据等)
    $finalResponseXml = $this->generateFinalSoapResponse($taskData['raw_request']);
    
    // 调用客户端SOAP接口发送最终响应
    $ch = curl_init($taskData['client_endpoint']);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $finalResponseXml);
    curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: text/xml; charset=utf-8']);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $response = curl_exec($ch);
    
    // 处理调用结果(比如记录日志、重试失败请求)
    if (curl_errno($ch)) {
        // 可以将失败任务重新放回队列重试
        $redis->lPush('soap_task_queue', $task);
    }
    curl_close($ch);
}

方案二:HTTP分块传输编码(兼容性有限)

如果必须在同一个HTTP请求中返回确认消息和最终响应,可以利用HTTP的分块传输编码(Chunked Transfer Encoding),把两个SOAP消息作为不同的块发送。但注意:大部分标准SOAP客户端不支持这种方式,需要确认对方客户端能正确解析分块中的多个SOAP消息。

具体实现代码

// 禁用输出缓冲
ob_end_clean();

// 设置分块传输的HTTP头
header('Transfer-Encoding: chunked');
header('Content-Type: text/xml; charset=utf-8');

// 输出确认消息的块
$ackXml = <<<XML
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
 <soap:Body>
 <PAddRs>
 <RqUID>f11958c8-3fde-42ca-bd94-94fdfca316ef</RqUID>
 <PKey>46dba062-2105-4851-831f-a1d364741329</PKey>
 <AppStatus>
 <AppStatusCode>Accept</AppStatusCode>
 </AppStatus>
 </PAddRs>
 </soap:Body>
</soap:Envelope>
XML;
printf("%x\r\n%s\r\n", strlen($ackXml), $ackXml);
flush(); // 强制发送第一个块

// 模拟业务处理耗时(替换成你的实际业务逻辑)
sleep(5);

// 输出最终响应的块
$finalResponseXml = $this->generateFinalSoapResponse(); // 生成你的最终SOAP响应
printf("%x\r\n%s\r\n", strlen($finalResponseXml), $finalResponseXml);
flush();

// 发送空块结束响应
echo "0\r\n\r\n";
flush();

关键注意事项

  • 优先选择方案一:方案一完全符合HTTP/SOAP的设计,兼容性和可靠性都远高于方案二,也避免了客户端解析分块响应的问题。
  • 异步任务的可靠性:使用队列时要考虑失败重试机制,比如设置任务重试次数、死信队列,避免任务丢失。
  • SOAP XML的正确性:手动构建SOAP XML时要确保命名空间、标签结构完全符合需求,最好用DOMDocument或XMLWriter生成,避免字符串拼接的错误。
  • 连接管理:在方案一中,使用fastcgi_finish_request()(PHP-FPM环境)确保后台处理不受客户端断开连接的影响。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 06:44:26