Heim  >  Artikel  >  Backend-Entwicklung  >  Analyse der Verarbeitungsschritte mit hoher Parallelität beim Lesen und Schreiben von PHP-Dateien

Analyse der Verarbeitungsschritte mit hoher Parallelität beim Lesen und Schreiben von PHP-Dateien

php中世界最好的语言
php中世界最好的语言Original
2018-05-17 13:49:232301Durchsuche

Dieses Mal werde ich Ihnen eine Analyse der Betriebsschritte der hohen gleichzeitigen Verarbeitung von PHP-Lese- und Schreibdateien geben. Was sind die Vorsichtsmaßnahmen für die hohe Parallelität der Verarbeitung von PHP-Lese- und Schreibdateien? Das Folgende ist ein praktischer Fall.

Seit kurzem muss die Spieleentwicklung des Unternehmens die Abwanderungsrate beim Laden von Spielen kennen. Weil wir Webspiele machen. Jeder, der schon einmal Webspiele gespielt hat, weiß, dass man einige Ressourcen laden muss, bevor man mit dem Spiel beginnen kann. Schließlich gelangen Sie zur Spieloberfläche zum Erstellen eines Charakters. Eine unserer Anforderungen besteht darin, die Anzahl der Benutzer zu zählen, die verloren gehen, bevor während des Ladevorgangs die Benutzeroberfläche zur Charaktererstellung erreicht wird.

Wir zählen die Anzahl der Personen zu Beginn des Ladevorgangs und erfassen dann die Anzahl der Personen, nachdem der Ladevorgang abgeschlossen ist. Auf diese Weise wird die Anzahl der Personen nach erfolgreicher Verladung von der Anzahl der Personen vor der Verladung abgezogen. Kennen Sie einfach die Abwanderungsrate beim Laden. Sie können erkennen, ob das Spiel den Ladevorgang weiter optimieren und die Benutzerladerate des Spiels reduzieren muss.

Weil unser Traffic aus den meisten Mainstream-Kooperationsmedien importiert wird. Daher ist die Parallelität sehr hoch und sollte nach groben Berechnungen etwa 1.000 Parallelitäten pro Sekunde erreichen können.

Die Anzahl der Personen vor dem Laden sollte ursprünglich auf der Caching-Plattform im Spiel platziert werden. Die Kollegen im Spiele-Backend befürchten jedoch, dass die Parallelität zu hoch ist und unnötige Ressourcenverschwendung entsteht. Weil auf die Speicherfreigabe nicht in Echtzeit reagiert wird. Legen Sie daher die gezählte Personenzahl auf einen anderen Server: den Statistikserver.

Die Lösung, die ich gerade verwendet habe, ist wie folgt:

Durch PHPs <a href="http://www.php.cn/wiki/1311.html" target=" _blank">file_get_contents<p style="text-align: left;">()</p></a> und <a href="http://www.php.cn/wiki/1312.html" target="_blank">file_put_contents<code><a href="http://www.php.cn/wiki/1311.html" target="_blank">file_get_contents</a>()( ) Lesen und schreiben. Schreiben Sie beim ersten Lesen und Schreiben 1 in die Datei, fügen Sie beim zweiten Laden 1 zum Original hinzu und so weiter. Mit dieser Ideenfolge gibt es kein Problem. Das Problem liegt darin, dass unser Server nicht sequentiell sein kann. <a href="http://www.php.cn/wiki/1312.html" target="_blank">file_put_contents</a>()Um genau zu sein, ist der gleichzeitige Zugriff nicht sequentiell. Wenn Spieler A das Spiel lädt und die Zahl 100 in der Datei liest (vorausgesetzt, es ist zu diesem Zeitpunkt 100), liest Spieler B auch 100. Zu diesem Zeitpunkt addiert der Thread verarbeitende Spieler A 1 zu 100, um 101 zu erhalten. wird 101 schreiben zur Datei.

Der Thread-Verarbeitungsspieler B erhält ebenfalls das gleiche Ergebnis und schreibt 101 in die Datei. Zu diesem Zeitpunkt tritt das Problem auf? Spieler B hat das Spiel nach Spieler A geladen, daher sollte er ein Berechnungsergebnis von 102 erhalten.

Dies ist das Problem, das durch Parallelität verursacht wird. Zu diesem Zeitpunkt dachte ich darüber nach, die Datei mit

zu öffnen und mit fopen() eine Schreibsperre hinzuzufügen. Jeder wird definitiv denken, dass es keine Probleme verursachen wird, wenn diese Methode gesperrt ist. Tatsächlich ist es auch falsch. flock()

Weil unser Problem nicht das Schreiben ist. Stattdessen führt es dazu, dass die Daten beim Lesen nicht synchron sind. OK. Zum jetzigen Zeitpunkt komme ich mit Baidu und Google wirklich nicht klar.

Als meine Hoffnungen auf die PHP-Funktion selbst gesetzt waren und meine Träume zerplatzten, konnte ich nur einen anderen Weg finden. Raus damit. Also dachte ich an den Map-Mapping-Mechanismus der *-Sprache. Ähnlich wie bei unserem PHP-Array füge ich dem Array bei jedem Laden ein Element hinzu. Auf diese Weise muss ich am Ende nur

auf das Array klicken, um zu erfahren, wie viele Spieler das Spiel geladen haben. count()

Allerdings gibt es ein Problem bei der Verwendung von Arrays. Sogar

PHP-Variablen sind weiterhin Konstanten und werden nach der Ausführung des Skripts gelöscht. Also habe ich darüber nachgedacht, wie man Dateien speichert.

Die endgültige mögliche Lösung lautet wie folgt:

Verwenden Sie fopen, um eine Datei im schreibgeschützten Modus zu öffnen. Dann Schreibsperre. Jedes Mal, wenn ein Spieler lädt, schreibe ich eine Zahl 1 in die Datei. Der endgültige Dateiinhalt wird sofort über

ausgelesen und dann wird die Länge mit file_get_contents() berechnet, um zu erfahren, wie viele Spieler das Spiel geladen haben. strlen()

Ich habe gehört, dass die Funktion

gesperrt wird, was dazu führt, dass die Systemressourcen für lange Zeit ansteigen. Daher habe ich die von allen verwendete Methode übernommen und die Mikrosekunden-Timeout-Technologie verwendet, um dieses Problem zu lösen. Wenn ich aus dieser Zeit herauskomme, werde ich es wegwerfen. Der spezifische Code lautet wie folgt: flock()

// loadcount.func.php 函数文件。
/**
 * 获取某来源和某服务器ID的游戏加载次数。
 *
 * @param string $fromid 来源标识。
 * @param int $serverid 服务器ID编号。
 *
 * @return int
 */
function getLoadCount($fromid, $serverid)
{
    global $g_global;
    $serverid = (int) $serverid;
    $fromid  = md5($fromid);
    $filename = $fromid . $serverid . &#39;.txt&#39;;
    $data = file_get_contents($filename);
    return strlen($data);
}
/**
 * 获取某来源所有服务器的游戏加载次数。
 *
 * @param string $fromid 来源标识。
 *
 * @return int
 */
function getAllLoadCount($fromid)
{
    global $g_global;
    $fromid  = md5($fromid);
    $count = 0;
    foreach (glob("{$fromid}*.txt") as $filename)
    {
        $file_content = file_get_contents($filename);
        $count += strlen($file_content);
    }
    return $count;
}
/**
 * 清空所有的加载数据。
 *
 * @return void
 */
function clearLoadCount()
{
    foreach (glob("*.txt") as $filename) {
      unlink($filename);
    }
    return true;
}
/**
 * 延迟更新游戏加载次数中间件。
 *
 * 使用此函数来延迟更新数据,原理:当不足1000次的时候,不更新数据库,超过1000就更新到数据库里面去。
 *
 * @param string $fromid 来源标识。
 * @param int $serverid 服务器ID编号。
 */
function delayAddLoadCount($fromid, $serverid)
{
    // 使用MD5生成文件名记录缓存次数。
    $fromid  = md5($fromid);
    $filename = $fromid . $serverid . &#39;.txt&#39;;
    if($fp = fopen($filename, &#39;a&#39;))
    {
        $startTime = microtime();
        do {
            $canWrite = flock($fp, LOCK_EX);
            if(!$canWrite)
            {
                usleep(round(mt_rand(0, 100)*1000));
            }
        }
        while ( ( !$canWrite ) && ( ( microtime()- $startTime ) < 1000 ) );
        if ($canWrite)
        {
            fwrite($fp, "1");
        }
        fclose($fp);
    }
    return true;
}

Das Folgende ist die Datei, in der ich die obige Methode aufgerufen habe:

< ?php
/**
 * @describe 平台用户加载游戏次数统计接口入口。
 * @date 2012.12.17
 */
include_once &#39;./loadcount.func.php&#39;;
// 测试用。
// $_GET[&#39;fromid&#39;]  = &#39;4399&#39;;
// $_GET[&#39;serverid&#39;] = mt_rand(0, 5);
// 添加加载次数。
if ( $_GET[&#39;action&#39;] == &#39;addcount&#39; )
{
    $fromid  = $_GET[&#39;fromid&#39;];  // 来源标识。
    $serverid = $_GET[&#39;serverid&#39;]; // 服务器ID编号。
    $return = delayAddLoadCount($fromid, $serverid);
    $return = $return ? 1 : 0;
    ob_clean();
    echo json_encode($return);
    exit;
}
// 取加载次数。
elseif ( $_GET[&#39;action&#39;] == &#39;getcount&#39; )
{
    $fromid = $_GET[&#39;fromid&#39;];  // 来源标识。
    if ( !isset( $_GET[&#39;serverid&#39;] ) ) // 有服务器编号 ID则取来源对应的服务器加载次数。
    {
        $count = getAllLoadCount($fromid);
    }
    else // 加载对应来源的次数。
    {
        $serverid = $_GET[&#39;serverid&#39;]; // 服务器ID编号。
        $count = getLoadCount($fromid, $serverid);
    }
    ob_clean();
    header(&#39;Content-Type:text/html;charset=UTF-8&#39;);
    $serverid = strlen($serverid) ? $serverid : &#39;无&#39;;
    echo "来源:{$fromid},服务器ID:{$serverid},游戏加载次数:" . $count;
    exit;
}
// 清除加载次数。
elseif ( $_GET[&#39;action&#39;] == &#39;clearcount&#39; )
{
    header(&#39;Content-Type:text/html;charset=UTF-8&#39;);
    $return = clearLoadCount();
    if ($return)
    {
        echo "清除成功!";
    }
    else
    {
        echo "清除失败!";
    }
}

Das ist eine verdammte Lektion, also muss ich sie aufzeichnen. Damit andere in Zukunft daraus lernen können.

Bei diesem Artikel handelt es sich um den Code, den der Autor Han Bing vor einem Jahr geschrieben hat, als er bei 4399 Game Studio für die Datenanalyse verantwortlich war. Ich hoffe, es hilft allen.

PHP数据库操作之高并发实例

高并发下PHP写文件日志丢失

<?php
/**
 * Created by PhpStorm.
 * User: andyfeng
 * Date: 2015/6/24
 * Time: 13:31
 */
class LogFileUtil {
  public static $fileHandlerCache;
  private static $initFlag = false;
  private static $MAX_LOOP_COUNT = 3;
  private static function init() {
    self::$initFlag = true;
    register_shutdown_function(array("LogFileUtil", "shutdown_func"));
  }
  /**
   * 输出到文件日志
   * @param $filePath 文件路径
   * @param $msg 日志信息
   * @return int
   */
  public static function out($filePath, $msg) {
    if (!self::$initFlag) {
      self::init();
    }
    return self::internalOut($filePath, $msg);
  }
  /**
   * @param $filePath
   * @param $msg
   * @param $loop
   * @return int
   */
  private static function internalOut($filePath, $msg, $loop = 0) {
    //以防一直添加失败造成死循环
    if ($loop > self::$MAX_LOOP_COUNT) {
      $result = 0;
    } else {
      $loop++;
      $fp = self::$fileHandlerCache["$filePath"];
      if (empty($fp)) {
        $fp = fopen($filePath, "a+");
        self::$fileHandlerCache[$filePath] = $fp;
      }
      if (flock($fp, LOCK_EX)) {
        $result = fwrite($fp, $msg);
        flock($fp, LOCK_UN);
      } else {
        $result = self::internalOut($filePath, $msg, $loop);
      }
    }
    return $result;
  }
  function shutdown_func() {
    if (!empty(LogFileUtil::$fileHandlerCache)) {
      if (is_array(LogFileUtil::$fileHandlerCache)) {
        foreach (LogFileUtil::$fileHandlerCache as $k => $v) {
          if (is_resource($v))
            //file_put_contents("close.txt",$k);
            fclose($v);
        }
      }
    }
  }
}

相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!

推荐阅读:

PHP动态创建XML文档步骤详解

php怎样快速生成无限级分类(附代码)

Das obige ist der detaillierte Inhalt vonAnalyse der Verarbeitungsschritte mit hoher Parallelität beim Lesen und Schreiben von PHP-Dateien. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn