濮阳杆衣贸易有限公司

主頁 > 知識庫 > PHP讀寫文件高并發(fā)處理操作實例詳解

PHP讀寫文件高并發(fā)處理操作實例詳解

熱門標(biāo)簽:四川保險智能外呼系統(tǒng)供應(yīng)商 地圖標(biāo)注專員怎么樣 遼寧ai電銷機(jī)器人價格 電話機(jī)器人銷售主要負(fù)責(zé)什么 福建銀行智能外呼系統(tǒng)價格 上海做外呼線路的通信公司 長沙做地圖標(biāo)注公司 寧波外呼營銷系統(tǒng) 房產(chǎn)中介用的是什么外呼系統(tǒng)

本文實例講述了PHP讀寫文件高并發(fā)處理操作。分享給大家供大家參考,具體如下:

背景:

最近公司游戲開發(fā)需要知道游戲加載的流失率。因為,我們做的是網(wǎng)頁游戲。玩過網(wǎng)頁游戲的人都知道,進(jìn)入游戲前要加載一些資源。最后才能到達(dá)創(chuàng)建角色的游戲界面。我們有一個需求就是要統(tǒng)計在加載過程中還未到達(dá)角色創(chuàng)建界面而流失的用戶數(shù)量。

我們在加載開始就進(jìn)行統(tǒng)計人數(shù),加載完成之后再記錄人數(shù)。這樣,通過用加載前的人數(shù)減去成功加載后的人數(shù)。就知道了加載的流失率。就可以知道游戲是否還要繼續(xù)優(yōu)化加載過程,降低用戶加載游戲率。

由于,我們的量都是從*主流的合作媒體進(jìn)行導(dǎo)量過來。所以,并發(fā)非常高,據(jù)粗略計算應(yīng)該能達(dá)到每秒1000左右的并發(fā)數(shù)量。

加載前的人數(shù)本來想放到游戲內(nèi)部的緩存平臺。但是,游戲后端的同事?lián)牟l(fā)太高,導(dǎo)致資源無故浪費(fèi)。因為,內(nèi)存的釋放并不是實時響應(yīng)的。所以,將統(tǒng)計的人數(shù)放到在另外一臺服務(wù)器:統(tǒng)計服務(wù)器。

我剛開始采用的方案如下:

通過php的file_get_contents()file_put_contents()進(jìn)行讀取與寫入。第一次讀寫就向文件寫入1,第二次加載就在原來的基礎(chǔ)上加1.以此類推.這種順序的思想完全不存在任何問題。問題就出在,我們的服務(wù)器不可能是順序形式的。

準(zhǔn)確的說,并發(fā)的訪問不是順序的。當(dāng)A玩家加載游戲讀取到文件里面的數(shù)字100(假如這時是100),B玩家讀取到的也是100,這時,處理A玩家的線程就是在100的基礎(chǔ)上加1,得到101,就會向文件寫入101。

處理B玩家的線程也得到相同的結(jié)果,將101寫入文件。這時,問題就出現(xiàn)了?B玩家是在A玩家之后加載游戲的,理應(yīng)得到102的計算結(jié)果。

這就是并發(fā)導(dǎo)致的問題。這個時候,我想到了采用fopen()打開文件,并用flock()加一個寫入鎖。大家一定會認(rèn)為,這種方式有了鎖定,那么就不會造成問題了。其實,也是錯的。

因為,我們的問題不是出在寫入上面。而是讀取的時候造成數(shù)據(jù)的不同步。OK。到這里,我實在百度谷歌都搞不定了。

當(dāng)希望寄托在PHP函數(shù)本身而夢碎的時候,我只能另尋它法。脫離它。于是,我想到了*語言的Map映射的機(jī)制。類似于我們的PHP數(shù)組,每加載一次就我往數(shù)組添加一個元素。這樣,到最后我只需要count()一下數(shù)組就知道了有多少玩家加載了游戲。

但是,用數(shù)組的話,也存在一個問題。就是PHP的變量還是常量,在腳本執(zhí)行完畢之后都會自己清掉。于是,我想到了文件保存的方式。

最終的可行方案思路如下:

用fopen打開一個文件,以只寫的方式。然后寫鎖定。玩家每加載一次我就向文件里面寫入一個數(shù)字1,最后得到的文件內(nèi)容通過file_get_contents()一次性讀取出來,再用strlen()計算一下長度即知道了有多少玩家加載了游戲。

聽聞flock()函數(shù)會鎖定會造成系統(tǒng)資源在很多時間升高。所以,我采用大家所使用的方式,用微秒超時的技術(shù)解決這個問題。如果,走出這個時間我就*掉它。具體的代碼如下:

// loadcount.func.php 函數(shù)文件。
/**
 * 獲取某來源和某服務(wù)器ID的游戲加載次數(shù)。
 *
 * @param string $fromid 來源標(biāo)識。
 * @param int $serverid 服務(wù)器ID編號。
 *
 * @return int
 */
function getLoadCount($fromid, $serverid)
{
    global $g_global;
    $serverid = (int) $serverid;
    $fromid  = md5($fromid);
    $filename = $fromid . $serverid . '.txt';
    $data = file_get_contents($filename);
    return strlen($data);
}
/**
 * 獲取某來源所有服務(wù)器的游戲加載次數(shù)。
 *
 * @param string $fromid 來源標(biāo)識。
 *
 * @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;
}
/**
 * 清空所有的加載數(shù)據(jù)。
 *
 * @return void
 */
function clearLoadCount()
{
    foreach (glob("*.txt") as $filename) {
      unlink($filename);
    }
    return true;
}
/**
 * 延遲更新游戲加載次數(shù)中間件。
 *
 * 使用此函數(shù)來延遲更新數(shù)據(jù),原理:當(dāng)不足1000次的時候,不更新數(shù)據(jù)庫,超過1000就更新到數(shù)據(jù)庫里面去。
 *
 * @param string $fromid 來源標(biāo)識。
 * @param int $serverid 服務(wù)器ID編號。
 */
function delayAddLoadCount($fromid, $serverid)
{
    // 使用MD5生成文件名記錄緩存次數(shù)。
    $fromid  = md5($fromid);
    $filename = $fromid . $serverid . '.txt';
    if($fp = fopen($filename, 'a'))
    {
        $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;
}

以下是我調(diào)用以上方法的文件:

 ?php
/**
 * @describe 平臺用戶加載游戲次數(shù)統(tǒng)計接口入口。
 * @date 2012.12.17
 */
include_once './loadcount.func.php';
// 測試用。
// $_GET['fromid']  = '4399';
// $_GET['serverid'] = mt_rand(0, 5);
// 添加加載次數(shù)。
if ( $_GET['action'] == 'addcount' )
{
    $fromid  = $_GET['fromid'];  // 來源標(biāo)識。
    $serverid = $_GET['serverid']; // 服務(wù)器ID編號。
    $return = delayAddLoadCount($fromid, $serverid);
    $return = $return ? 1 : 0;
    ob_clean();
    echo json_encode($return);
    exit;
}
// 取加載次數(shù)。
elseif ( $_GET['action'] == 'getcount' )
{
    $fromid = $_GET['fromid'];  // 來源標(biāo)識。
    if ( !isset( $_GET['serverid'] ) ) // 有服務(wù)器編號 ID則取來源對應(yīng)的服務(wù)器加載次數(shù)。
    {
        $count = getAllLoadCount($fromid);
    }
    else // 加載對應(yīng)來源的次數(shù)。
    {
        $serverid = $_GET['serverid']; // 服務(wù)器ID編號。
        $count = getLoadCount($fromid, $serverid);
    }
    ob_clean();
    header('Content-Type:text/html;charset=UTF-8');
    $serverid = strlen($serverid) ? $serverid : '無';
    echo "來源:{$fromid},服務(wù)器ID:{$serverid},游戲加載次數(shù):" . $count;
    exit;
}
// 清除加載次數(shù)。
elseif ( $_GET['action'] == 'clearcount' )
{
    header('Content-Type:text/html;charset=UTF-8');
    $return = clearLoadCount();
    if ($return)
    {
        echo "清除成功!";
    }
    else
    {
        echo "清除失?。?;
    }
}

這是血的教訓(xùn),所以,我不得不將它記錄下來。以備以后讓他人借鑒。

本文是作者寒冰一年前在4399游戲工作室負(fù)責(zé)做數(shù)據(jù)分析的時候?qū)懙拇a。希望對大家有所幫助。

PHP數(shù)據(jù)庫操作之高并發(fā)實例

高并發(fā)下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) {
    //以防一直添加失敗造成死循環(huán)
    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);
        }
      }
    }
  }
}

更多關(guān)于PHP相關(guān)內(nèi)容感興趣的讀者可查看本站專題:《php文件操作總結(jié)》、《php緩存技術(shù)總結(jié)》、《PHP運(yùn)算與運(yùn)算符用法總結(jié)》、《PHP基本語法入門教程》、《php面向?qū)ο蟪绦蛟O(shè)計入門教程》、《php字符串(string)用法總結(jié)》、《php+mysql數(shù)據(jù)庫操作入門教程》及《php常見數(shù)據(jù)庫操作技巧匯總》

希望本文所述對大家PHP程序設(shè)計有所幫助。

您可能感興趣的文章:
  • PHP文件讀寫操作相關(guān)函數(shù)總結(jié)
  • php中讀寫文件與讀寫數(shù)據(jù)庫的效率比較分享
  • php格式文件打開的四種方法
  • php實現(xiàn)以只讀方式打開文件的方法
  • PHP打開和關(guān)閉文件操作函數(shù)總結(jié)
  • php打開文件fopen函數(shù)的使用說明
  • PHP 處理TXT文件(打開/關(guān)閉/檢查/讀取)
  • php文件怎么打開 如何執(zhí)行php文件
  • PHP文件打開、關(guān)閉、寫入的判斷與執(zhí)行代碼
  • PHP文件打開關(guān)閉及讀寫操作示例解析

標(biāo)簽:延安 宿遷 工商登記 深圳 佛山 常德 澳門 宜春

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《PHP讀寫文件高并發(fā)處理操作實例詳解》,本文關(guān)鍵詞  PHP,讀寫,文件,高并發(fā),高,;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問題,煩請?zhí)峁┫嚓P(guān)信息告之我們,我們將及時溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無關(guān)。
  • 相關(guān)文章
  • 下面列出與本文章《PHP讀寫文件高并發(fā)處理操作實例詳解》相關(guān)的同類信息!
  • 本頁收集關(guān)于PHP讀寫文件高并發(fā)處理操作實例詳解的相關(guān)信息資訊供網(wǎng)民參考!
  • 推薦文章
    车险| 瑞安市| 昆山市| 西平县| 田东县| 靖州| 嵩明县| 新化县| 上饶县| 昌平区| 淳化县| 富宁县| 石台县| 西充县| 西贡区| 郎溪县| 沙坪坝区| 成都市| 桂平市| 江城| 喀喇沁旗| 弥渡县| 大宁县| 龙州县| 三穗县| 高尔夫| 秦皇岛市| 兴和县| 五家渠市| 瑞丽市| 巫山县| 南昌市| 绥中县| 区。| 万载县| 武强县| 怀化市| 武乡县| 仙居县| 资兴市| 苏尼特右旗|