Analysis of high concurrency processing operation steps of PHP reading and writing files

php中世界最好的语言
Release: 2023-03-26 10:20:01
Original
2325 people have browsed it

This time I will bring you the high concurrency processing of PHP files. Operation step analysis, what are the precautions for PHP high concurrency processing of reading and writing files, the following is a practical case, Let’s take a look.

Recently, company game development needs to know the churn rate of game loading. Because we are making web games. Anyone who has played web games knows that you need to load some resources before entering the game. Finally, you can reach the game interface for creating a character. One of our needs is to count the number of users who are lost before reaching the character creation interface during the loading process.

We count the number of people at the beginning of loading, and then record the number of people after loading is completed. In this way, the number of people after successful loading is subtracted from the number of people before loading. Just know the loading churn rate. You can know whether the game needs to continue to optimize the loading process and reduce the user loading rate of the game.

Because our content is imported from the most mainstream cooperative media. Therefore, the concurrency is very high. According to rough calculations, it should be able to reach about 1,000 concurrencies per second.

The number of people before loading was originally intended to be placed on the caching platform within the game. However, colleagues at the game backend are worried that the concurrency is too high, resulting in unnecessary waste of resources. Because the release of memory does not respond in real time. Therefore, put the counted number of people on another server: the statistics server.

The solution I just started using is as follows:

Through PHP’s <a href="http://www.php.cn/wiki/1311.html" target="_blank">file_get_contents</a>() and <a href="http://www.php.cn/wiki/1312.html" target="_blank">file_put_contents</a>( ) for reading and writing. The first time you read and write, write 1 to the file, the second time you load, add 1 to the original, and so on. There is no problem with this sequence of ideas. The problem lies in that our server cannot be sequential.

To be precise, concurrent access is not sequential. When player A loads the game and reads the number 100 in the file (assuming it is 100 at this time), player B also reads 100. At this time, the thread processing player A adds 1 to 100 to get 101. will write 101 to the file.

The thread processing player B also gets the same result, writing 101 to the file. At this time, the problem arises? Player B loaded the game after player A, so he should get a calculation result of 102.

This is the problem caused by concurrency. At this time, I thought of using fopen() to open the file and flock() to add a write lock. Everyone will definitely think that if this method is locked, it will not cause problems. In fact, it is also wrong.

Because our problem is not writing. Instead, it causes the data to be out of sync when reading. OK. At this point, I really can’t figure it out with Baidu and Google.

When my hopes were pinned on the PHP function itself and my dreams were shattered, I could only find another way. Get out of it. So, I thought of the Map mapping mechanism of * language. Similar to our PHP array, I add an element to the array every time it is loaded. In this way, in the end I only need to count() to know how many players have loaded the game.

However, there is also a problem when using arrays. That is, PHP variables are still constants and will be cleared after the script is executed. So, I thought of how to save files.

The final feasible solution is as follows:

Use fopen to open a file in write-only mode. Then write lock. Every time the player loads, I write a number 1 into the file. The final file content is read out at once through file_get_contents(), and then strlen() is used to calculate the length. That is, knowing how many players have loaded the game.

Heardflock() The function will be locked, which will cause system resources to increase for a long time. Therefore, I adopted the method used by everyone and used microsecond timeout technology to solve this problem. If I get out of this time, I will throw it away. The specific code is as follows:

// 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;
}
Copy after login

The following is the file in which I called the above method:

< ?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 "清除失败!";
    }
}
Copy after login

This is a bloody lesson, so I have to record it. So that others can learn from it in the future.

This article is the code written by the author Han Bing a year ago when he was responsible for data analysis at 4399 Game Studio. I hope to be helpful.

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);
        }
      }
    }
  }
}
Copy after login

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

推荐阅读:

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

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

The above is the detailed content of Analysis of high concurrency processing operation steps of PHP reading and writing files. For more information, please follow other related articles on the PHP Chinese website!

Related labels:
source:php.cn
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template