我正在使用一个接收推送通知并通过使用XSL模板对其进行解析并创建(如果不存在)或将XML解析的数据附加到文件中来处理它们的系统。 有时它几乎同时接收到多个通知,因此第一个通知创建了文件,但是第二个通知又创建了该文件,因为在那一刻我认为文件正在写入但尚不存在于文件系统中。因此,结果是第一个xml数据因第二次调用脚本而被覆盖而丢失。
我认为主要问题是在尝试使用scan_dir来检查文件是否已存在时。第一次调用找不到该文件(正确),但是第二次调用找不到该文件,因为第一次调用正在写入该文件。我没有办法确定文件名,所以我必须应用正则表达式模式,因为不知道文件是何时创建的。最初,我使用glob具有相同的结果,因此更改为scan_dir,因为我认为scan_dir更快。
/**
* Main method
* @return string XML resulting
*/
public function run()
{
$xml = simplexml_load_string($this->xml);
$xsl = new \DOMDocument();
$xsl->load(self::XSLPATH);
// Transformer config
$proc = new \XSLTProcessor;
$proc->registerPHPFunctions();
$proc->importStyleSheet($xsl);
$xmlResult = $proc->transformToXML($xml);
// Obtain path to destination folder
$path = self::PATH."/{$this->folder}/";
// Get the file with pattern
$file_list = glob($path."fileoutput*.xml");
// If there exists files matching the pattern, get the first one
if (sizeof($file_list) > 0) {
$this->append($xmlResult, $file_list[0]);
} else {
// ERROR! concurrent calls end here beceause file is not
// in filesystem so scan_dir can't detect it!
$filename = "fileoutput_".date("d-m-Y_H-i-s").".xml";
$this->writeFile(
__DIR__."/../../../output/{$this->folder}/{$filename}",
$xmlResult
);
}
// return the xml string
return $xmlResult;
}
/**
* This method uses flock to gain exclusive acces to resource.
*
* @param string $filepath file path
* @param string $data dat ato be written
* @return void
*/
private function writeFile($filepath, $data)
{
$fh = fopen($filepath, "w");
$tries = 5;
while ($tries > 0) {
$locked = flock($fh, LOCK_EX);
if (! $locked) {
sleep(5);
$tries--;
} else {
$tries = 0;
}
}
if ($locked) {
fwrite($fh, $data);
flock($fh, LOCK_UN);
}
fclose($fh);
}
/**
* Append xml data to existing xml
* @param $xml string xml to append
* @param $file string file where xml will be append
*/
private function append($xml, $filename)
{
$xmlFromFile = simplexml_load_file($filename);
$xmlToAppend = simplexml_load_string($xml);
$nodeToAppend = $xmlToAppend->reserva;
$this->sxml_append($xmlFromFile, $nodeToAppend);
$this->writeFile($filename, $xmlFromFile->asXML());
}
/**
* This method adds a childnode to xml with deep copy
* @param $to SimpleXMLElement xml where childnode is copied
* @param $from SimpleXMLElement xml childnode to copy to
* @return void
*/
private function sxml_append(\SimpleXMLElement $to, \SimpleXMLElement $from)
{
$toDom = dom_import_simplexml($to);
$fromDom = dom_import_simplexml($from);
$toDom->formatOutput = true;
//$toDom->preserveWhiteSpace = false;
$toDom->appendChild($toDom->ownerDocument->createTextNode("\n"));
$toDom->appendChild($toDom->ownerDocument->importNode($fromDom, true));
}
我正在尝试处理所有通知数据,即使它们几乎同时到达我的脚本。如果我几乎同时收到2条通知,则需要所有数据而不会覆盖它们。
答案 0 :(得分:1)
按照RiggsFolly的建议,如果我创建一个 lock.dat 文件并尝试对其进行锁定,则可以编写或以安全的方式获取输出文件,避免数据被覆盖。
我附上了修改后的代码。尝试使用jMeter,成功并发5次并发调用PHP脚本!
/**
* Main method
* @return string XML resulting
*/
public function run()
{
$xml = simplexml_load_string($this->xml);
$xsl = new \DOMDocument();
$xsl->load(self::XSLPATH);
// Transformer config
$proc = new \XSLTProcessor;
$proc->registerPHPFunctions();
$proc->importStyleSheet($xsl);
$xmlResult = $proc->transformToXML($xml);
// Obtain path to destination folder
$path = self::PATH."/{$this->folder}/";
// Get the file with pattern
$file_list = glob($path."fileoutput*.xml");
$lockfile = fopen("lock.dat", "w");
// check if get lock for $lockfile
if($this->getLock($lockfile)) {
$file_list = glob($path."fileoutput*.xml");
// If there exist files matching the pattern, get the first one
if (sizeof($file_list) > 0) {
$this->append($xmlResult, $file_list[0]);
} else {
//NICE! now it is sure we create file only when got lock in $lockfile,
//so it won't be overwriten
$filename = "fileoutput_".date("d-m-Y_H-i-s").".xml";
$this->writeFile(__DIR__."/../../../output/{$this->folder}/{$filename}", $xmlResult);
}
}
// return the xml string
return $xmlResult;
}
/**
* this method tries to get lock on a file
* @param resource $file The lock file
*
* @return boolean true if we get lock, false if not
*/
private function getLock($file)
{
$fh = $file;
$tries = 5;
while ($tries > 0) {
$locked = flock($fh, LOCK_EX);
if (! $locked) {
sleep(5);
$tries--;
} else {
$tries = 0;
}
}
return $locked;
}
/**
* This method uses flock to gain exclusive acces to resource.
*
* @param string $filepath file path
* @param string $data dat ato be written
* @return void
*/
private function writeFile($filepath, $data)
{
$fh = fopen($filepath, "w");
$tries = 5;
while ($tries > 0) {
$locked = flock($fh, LOCK_EX);
if (! $locked) {
sleep(5);
$tries--;
} else {
$tries = 0;
}
}
if ($locked) {
fwrite($fh, $data);
flock($fh, LOCK_UN);
}
fclose($fh);
}
/**
* Append xml data to existing xml
* @param $xml string xml to append
* @param $file string file where xml will be append
*/
private function append($xml, $filename)
{
$xmlFromFile = simplexml_load_file($filename);
$xmlToAppend = simplexml_load_string($xml);
$nodeToAppend = $xmlToAppend->reserva;
$this->sxml_append($xmlFromFile, $nodeToAppend);
$this->writeFile($filename, $xmlFromFile->asXML());
}
/**
* This method adds a childnode to xml with deep copy
* @param $to SimpleXMLElement xml where childnode is copied
* @param $from SimpleXMLElement xml childnode to copy to
* @return void
*/
private function sxml_append(\SimpleXMLElement $to, \SimpleXMLElement $from)
{
$toDom = dom_import_simplexml($to);
$fromDom = dom_import_simplexml($from);
$toDom->formatOutput = true;
//$toDom->preserveWhiteSpace = false;
$toDom->appendChild($toDom->ownerDocument->createTextNode("\n"));
$toDom->appendChild($toDom->ownerDocument->importNode($fromDom, true));
}