大規模なXMLファイルを使用する場合、単一スレッドの解析は、メモリの過度の使用または過度の実行時間を引き起こす可能性があります。 PHP自体は、「 PthreadsやSwooleなどの拡張機能を使用しない限り」「リアル」マルチスレッドをネイティブにサポートしていませんが、(たとえばProc_openを使用する)ために( Proc_openを使用する)ことにより、大規模なXMLファイルを並行して処理できます。
この記事では、XML_PARSE関数とProc_Openを組み合わせて、大きなXMLファイルの擬似マルチスレッド解析を実装する方法を示します。
XML_PARSEは、PHPの基礎となる解析機能の1つであり、Expatパーサーの一部です。イベントベースの解析をサポートし、大規模なXMLデータストリームのストリーミングに最適です。 XML_PARSEは、ドキュメント全体をメモリにロードするよりも多くのリソースを保存します。
複数のスレッドがXML_PARSERオブジェクトを共有することはできませんが、できます。
大きなXMLファイルをブロックします(ノードで分割) 。
proc_open()またはshell_exec()を使用して、複数のPHP子プロセスを開始します。
各子プロセスは、独自のXMLブロックを解析します。
主なプロセスは結果を収集し、それらをマージします。
次の構造を備えた大きなXMLファイル/Data/huge.xmlがあるとします。
<items>
<item><id>1</id><name>Item 1</name></item>
<item><id>2</id><name>Item 2</name></item>
...
</items>
<?php
$sourceFile = '/data/huge.xml';
$tempDir = '/tmp/xml_chunks/';
$chunkSize = 1000; // 各子供のプロセス解析 1000 個々 <item>
$urls = [];
// 一時的なディレクトリが存在することを確認してください
if (!is_dir($tempDir)) {
mkdir($tempDir, 0777, true);
}
// 分割 XML 書類
$handle = fopen($sourceFile, 'r');
$chunkIndex = 0;
$buffer = '';
$itemCount = 0;
while (($line = fgets($handle)) !== false) {
if (strpos($line, '<item>') !== false) {
$itemCount++;
}
$buffer .= $line;
if ($itemCount >= $chunkSize || feof($handle)) {
$chunkFile = $tempDir . "chunk_{$chunkIndex}.xml";
file_put_contents($chunkFile, "<items>\n" . $buffer . "\n</items>");
$urls[] = "http://m66.net/worker.php?file=" . urlencode($chunkFile);
$chunkIndex++;
$buffer = '';
$itemCount = 0;
}
}
fclose($handle);
// パラレルコール worker パーサー(に変更できます curl_multi_exec 効率を向上させます)
foreach ($urls as $url) {
shell_exec("php worker.php '{$url}' > /dev/null &");
}
echo "始まった " . count($urls) . " 個々解析任务。\n";
<?php
if ($argc < 2) {
exit("渡してください XML 書類路径参数\n");
}
$xmlFile = urldecode($argv[1]);
if (!file_exists($xmlFile)) {
exit("書類不存在: $xmlFile\n");
}
$parser = xml_parser_create();
xml_set_element_handler($parser, "startElement", "endElement");
xml_set_character_data_handler($parser, "characterData");
$currentTag = '';
$currentItem = [];
function startElement($parser, $name, $attrs) {
global $currentTag;
$currentTag = strtolower($name);
}
function endElement($parser, $name) {
global $currentTag, $currentItem;
if (strtolower($name) == 'item') {
// 例:将解析结果保存到書類或数据库
file_put_contents('/tmp/parsed_result.txt', json_encode($currentItem) . PHP_EOL, FILE_APPEND);
$currentItem = [];
}
$currentTag = '';
}
function characterData($parser, $data) {
global $currentTag, $currentItem;
if (trim($data)) {
$currentItem[$currentTag] = trim($data);
}
}
$fp = fopen($xmlFile, 'r');
while ($data = fread($fp, 4096)) {
xml_parse($parser, $data, feof($fp)) or
die(sprintf("XML 間違い: %s", xml_error_string(xml_get_error_code($parser))));
}
fclose($fp);
xml_parser_free($parser);
echo "分析が完了しました: $xmlFile\n";
パフォーマンスの改善:マルチコアCPUでは、各子プロセスは独立して実行され、全体的な解析速度を並行して高速化できます。
メモリコントロール:各子プロセスによって処理されるデータの量は、メモリの破裂を避けるために制御可能です。
セキュリティ:ファイルパスが生産環境のURLパラメーターを直接渡されないことを確認し、ホワイトリストの確認を追加する必要があります。
プロセス管理: PCNTL_FORKまたはSWOOLEを使用して、 Shell_execを置き換えて、より安定した子どものプロセス管理を実現できます。
PHP自体は、 XML_PARSEおよびプロセス制御手法を介して、同時処理に理想的な言語ではありませんが、大規模なXMLファイルを効率的に解析することができます。この方法は、ログ処理やデータのインポートなどの効率を必要とするタスクシナリオに特に適しています。
さらなる改善が必要な場合は、Go/Pythonなどの同時実行に優しい言語で解析モジュールを書き換え、PHPを通じてスケジュールすることをお勧めします。
関連タグ:
xml_parse