Файл перезаписывается в некоторых сценариях - PullRequest
0 голосов
/ 14 февраля 2019

Я работаю в системе, которая получает push-уведомления и обрабатывает их, анализируя их с помощью шаблона 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 уведомления в одно и то же время, мне нужны все данные без перезаписи.

1 Ответ

0 голосов
/ 14 февраля 2019

Как подсказал 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));
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...