Chinaunix首页 | 论坛 | 博客
  • 博客访问: 181973
  • 博文数量: 36
  • 博客积分: 2078
  • 博客等级: 大尉
  • 技术积分: 330
  • 用 户 组: 普通用户
  • 注册时间: 2009-04-09 17:13
文章分类

全部博文(36)

文章存档

2012年(1)

2011年(5)

2010年(9)

2009年(21)

我的朋友

分类:

2009-04-09 17:54:49

接上一篇:还是郁字符长度的限制。
早期版本的PHPWIND论坛的cache机制是很差的,虽然它很快,但是很脆弱,一旦cache文件损坏或丢失,它不会自己去创建它,而是直接导致程序无法运行,这种只能叫做临时文件,而不能叫cache。我不知道现在的PHPWIND什么样,因为我一直没兴趣去看它……
   
    下面的部分是mSession的实现,它只是模拟了session的存取过程,并对系统session进行了改进。它用了Hash目录。它的缺点是在程序结束部分还要Rewrite一下,把数据更新到session文件里,当然这个很容易被改进。

 

<?php
class BsmSession
{
    var $sid;
    var $sess_file;
    
    function mSession_Start ()
    {
        // Special Function...session_start()

        global $cookie_sess_id_varname, $cookie_path, $sess_liftime, $mSession;
        
        $sid = $_COOKIE[$cookie_sess_id_varname] ? $_COOKIE[$cookie_sess_id_varname] : $this->_Gen_Sid();
        setcookie ($cookie_sess_id_varname, $sid, $sess_liftime, $cookie_path);
        
        $sess_file = $this->_Hash_Dir($sid) . 'sess_' . $sid;
        
        if (file_exists ($sess_file)) {
            if (!@$fp = fopen ($sess_file, 'rb')) {
                // Debug Info...No Log.

                fatal_error ('Session Error...');
            }
            
            if (0 == ($fl = filesize ($sess_file)))
                $sess_content = '';
            
            else
                $sess_content = fread ($fp, $fl);
        }
        else {
            if (!@$fp = fopen ($sess_file, 'wb')) {
                // Debug Info...No Log.

                fatal_error ('Session Error...');
            }
            
            $sess_content = '';
        }
        
        fclose ($fp);
        
        $this->sid = $sid;
        $this->sess_file = $sess_file;
        
        $mSession = unserialize($sess_content) or $mSession = array ();
    }
    
    function mSession_Destroy ()
    {
        global $mSession;
        
        $mSession = array ();
        return @unlink ($this->sess_file);
    }
    
    function mSession_Rewrite ()
    {
        // Restore Session Data into Session File

        global $mSession;
        
        $sess_content = serialize($mSession);
        
        if (!@$fp = fopen ($this->sess_file, 'wb')) {
            // Debug Info...No Log.

            fatal_error ('Session Error...');
        }
        
        fwrite ($fp, $sess_content);
        fclose ($fp);
        
        return;
    }
    
    function _Hash_Dir ($sid)
    {
        // Hash the Session file Dir

        
        global $user_sess_base_dir;
        
        $sess_dir = $user_sess_base_dir . substr ($sid, 0, 1) . '/' . substr ($sid, 16, 1) . '/';
        return $sess_dir;
    }
    
    function _Gen_Sid ()
    {
        // Gen an Unique Session ID

        
        $key_1 = rand (32768, 65535);
        $key_2 = microtime ();
        $key_3 = sha1 (time ());
        
        $sid = md5 ($key_1 . $key_3 . $key_2);
        
        return $sid;
    }
    
    function _Get_Sid ()
    {
        // Get Current Session ID

        global $cookie_sess_id_varname;
        
        $sid = $_COOKIE[$cookie_sess_id_varname] ? $_COOKIE[$cookie_sess_id_varname] : FALSE;
        return $sid;
    }
}
?>

    Hash目录是一种优化文件存储性能的方法。无论是Windows还是Linux,无论是NTFS还是ext3,每个目录下所能容纳的项目数是有限的。并不是不能保存,而是当项目数量过大的时候,会降低文件索引速度,所以权衡一个目录下应该保存多少文件是很必要的。保存得多了会影响性能,保存得少了会造成目录太多和空间浪费。所以当保存大批文件的时候,需要有一种算法能将文件比较均匀地“打散”在不同的子目录下以提高每一级的索引速度,这种算法就是 Hash。通常用的MD5、sha1等都可以用来做Hash目录,我的mSession里也同样使用了MD5,取得sessionID的第一位和第九位,这就构成了两级Hash路径,也就是说,系统把所有的Session文件分散到了16×16=256个子目录下。假设Linux每个目录下保存1000个文件可以获得最好的空间性能比,那么系统在理想情况下可以同时有256000个session文件在被使用。
    Hash目录还被广泛应用在备份、图库、电子邮件、静态页生成等文件密集型应用上。
   
    再来点一下我的模板类,我很懒地保留了Discuz模板函数的所有标签。一方面是我确实很懒,另一方面是我曾经试图修改Discuz,把它改成一个专用的版本,不过这是一个类,它的使用方法和Discuz函数没什么两样,都是include一个parse结果返回的文件名。
    所不同的是在处理{template}标签的时候。Discuz的处理方式是把{template}替换成再次调用模板解析函数去解析另一个模板文件,这样,模板函数可能会被调用多次,编译的结果里也会有很多include另一个模板文件Parse结果的地方。这里涉及另一个优化点——尽量少地 include文件。过多地include会带来更多的IO开销和CPU处理开销,所以我把{template}改成直接读入文件内容,然后再 parse。这样一个模板文件即使有1000个{template},编译的结果也只有一个文件。
    这个模板类用起来是如此地简单方便,更重要的是,它确实很快~~呵呵,我从来不否认我有时候也会做一些比较有用的事,哈哈:

<?php

// BSM Template Class v1.03

// By Dr.NP

// Create data: 11-26-2005


class BsmTpl {

    var $classname = 'BsmTpl';
    
    var $tpl_root_dir = 'templates/';
    var $tpl_cache_root_dir = 'sitedata/template/';
    var $tpl_dir = '';
    
    var $tpl_file_ext = 'htm';
    var $tpl_cache_file_ext = 'php';
    var $tpl_lang_file_ext = 'lang';
    var $tpl_static_file_ext = 'html';
    
    var $tpl_name = 'default';
    var $default_tpl_name = 'default';
    
    var $default_db_handle = '$db';
    var $default_lang = 'zh-cn';
    
    function BsmTpl ($root_dir = '', $cache_root_dir = '')
    {
        if ($root_dir != '')
            $this->tpl_root_dir = $root_dir;
        
        if ($cache_root_dir != '')
            $this->tpl_cache_root_dir = $cache_root_dir;
    }
    
    function parse_template ($tplfile, $objfile)
    {
        $nest = 5;
        
        if (!@$fp = fopen ($tplfile, 'r')) {
            die ("Current template file '" . $tplfile. " ' not found or have no access!");
        }
        
        $template = fread ($fp, filesize ($tplfile));
        fclose ($fp);
        
        $var_regexp = "((\$[a-zA-Z_x7f-xff][a-zA-Z0-9_x7f-xff]*)([[a-zA-Z0-9_\"'$x7f-xff]+])*)";
        $const_regexp = "([a-zA-Z_x7f-xff][a-zA-Z0-9_x7f-xff]*)";
        
        $template = preg_replace ("/s*{templates+(.+?)}s*/ies", "file_get_contents('{$this->tpl_dir}\1.{$this->tpl_file_ext}')", $template);
        
        $template = preg_replace ("/([nr]+)t+/s", "\1", $template);
        $template = preg_replace ("//s", "{\1}", $template);
        //$template = preg_replace ("/{langs+(.+?)}/ies", "languagevar('\1')", $template);

        $template = str_replace ("{LF}", "", $template);
        
        $template = preg_replace ("/{(\$[a-zA-Z0-9_[]'\"$x7f-xff]+)}/s", "", $template);
        $template = preg_replace ("/$var_regexp/es", "addquote('')", $template);
        $template = preg_replace ("/?>/es", "addquote('')", $template);
        
        $template = preg_replace ("/s*{evals+(.+?)}s*/ies", "stripvtags('nn', '')", $template);
        $template = preg_replace ("/s*{elseifs+(.+?)}s*/ies", "stripvtags('nn', '')", $template);
        $template = preg_replace ("/s*{else}s*/is", "nn", $template);
        $template = preg_replace ("/s*{dates+(.+?)s+(.+?)}s*/ies", "stripvtags('nn', '')", $template);
        
        for($i = 0; $i < $nest; $i++) {
            $template = preg_replace ("/s*{loops+(S+)s+(S+)}s*(.+?)s*{/loop}s*/ies", "stripvtags('n','n\3nn')", $template);
            $template = preg_replace ("/s*{loops+(S+)s+(S+)s+(S+)}s*(.+?)s*{/loop}s*/ies", "stripvtags('n \3) { ?>','n\4nn')", $template);
            $template = preg_replace ("/s*{ifs+(.+?)}s*(.+?)s*{/if}s*/ies", "stripvtags('n','n\2nn')", $template);
        }
        
        $template = preg_replace ("/{$const_regexp}/s", "", $template);
        $template = preg_replace ("/ ?>[nr]*, " ", $template);
        
        >')/ies", $template, $arr);
        
        for ($i = 0; $i < sizeof ($arr[0]); $i++) {
            $template = str_replace ($arr[0][$i], '
->parse(' . $arr[1][$i] . ')', $template);
        }
        */
        
        $template = str_replace ('
', '<?php echo time(); ?>', $template);
        $template = str_replace ('
', '<?php echo ' . $this->default_db_handle . '->num_queries; ?>', $template);
        
        /*
        if (!@$fp = fopen($objfile, '
w')) {
            die ("Directory '
" . $this->tpl_root . '/' . $this->tpl_cache_root . "' not found or have no access!");
        }
        
        flock ($fp, 3);
        fwrite ($fp, $template);
        fclose ($fp);
        */
        
        return $template;
    }
    
    function parse ($file)
    {
        global $page_time, $page_time_start;
        
        $tplfile = $this->tpl_dir . $file . '
.' . $this->tpl_file_ext;
        $objfile = $this->tpl_cache_root_dir . '
/' . $this->tpl_name . '_' . $file . '.tpl.' . $this->tpl_cache_file_ext;
        
        if (!file_exists($tplfile)) {
            $tplfile = $this->tpl_root_dir . '
/' . $this->default_tpl_name . '/' . $file.'.'.$this->tpl_file_ext;
            $objfile = $this->tpl_cache_root_dir . '
/' . $this->default_tpl_name . '_' . $file . '.tpl.' . $this->tpl_cache_file_ext;
        }
        
        if ((@filemtime($tplfile) > @filemtime($objfile)) || (!@file_exists($objfile))) {
            //$this->parse_template ($tplfile, $objfile);
            if (!@$fp = fopen($objfile, '
w')) {
                die ("Directory '
" . $this->tpl_root . '/' . $this->tpl_cache_root . "' not found or have no access!");
            }
            
            flock ($fp, 3);
            fwrite ($fp, $this->parse_template ($tplfile, $objfile));
            fclose ($fp);
        }
        list ($usec, $sec) = explode (" ", microtime ());
        $page_time_end = $usec + $sec;
        $page_time = sprintf ("%0.6f", $page_time_end - $page_time_start);
        
        return $objfile;
    }
    
    function set_tpl_db_handle ($dh)
    {
        $this->defalt_db_handle = '
$' . $dh;
    }
    
    function set_tpl_name ($name)
    {
        $tpl_dir = $this->tpl_root_dir . '
/' . $name . '/';
        if ($name != '
' && is_dir ($tpl_dir)) {
            $this->tpl_name = $name;
            $this->tpl_dir = str_replace ('
//', '/', $tpl_dir);
        }
        else {
            $this->tpl_name = $this->default_tpl_name;
            $this->tpl_dir = str_replace ('
//', '/', $this->tpl_root_dir . '/' . $this->tpl_name . '/');
        }
        
    }
    
    function set_language ($langMeta)
    {
        $langFile = $this->tpl_dir . $langMeta . '
.lang';
        clearstatcache ();
        
        if (@is_readable ($langFile)) {
            return $langFile;
        }
        
        elseif (@is_readable ($this->tpl_dir . $this->default_lang . '
.' . $this->tpl_lang_file_ext)) {
            $langFile = $this->tpl_dir . $this->default_lang . '
.' . $this->tpl_lang_file_ext;
            $langMeta = $this->default_lang;
            return $langFile;
        }
        
        elseif (@is_readable ($this->tpl_root_dir . '
/' . $this->default_tpl_name . '/' . $langMeta . '.lang')) {
            $langFile = $this->tpl_root_dir . '
/' . $this->default_tpl_name . '/' . $langMeta . '.lang';
            return $langFile;
        }
        
        elseif (@is_readable ($this->tpl_root_dir . '
/' . $this->default_tpl_name . '/' . $this->default_lang . '.' . $this->tpl_lang_file_ext)) {
            $langFile = $this->tpl_root_dir . '
/' . $this->default_tpl_name . '/' . $this->default_lang . '.' . $this->tpl_lang_file_ext;
            $langMeta = $this->default_lang;
            return $langFile;
        }
        
        else
            die ('
Accept Langfile:' . $langFile . ' did not exist or has no access!');
    }
    
    function dsp ()
    {
        global $mSession;
        if ($mSession['
do_gzip']) {
            $gzip_contents = ob_get_contents ();
            ob_end_clean ();
            
            $gzip_size = strlen ($gzip_contents);
            $gzip_crc = crc32 ($gzip_contents);
            
            $gzip_contents = gzcompress ($gzip_contents, 9);
            $gzip_contents = substr ($gzip_contents, 0, strlen ($gzip_contents) - 4);
            
            echo "x1fx8bx08x00x00x00x00x00";
            echo $gzip_contents;
            echo pack ('
V', $gzip_crc);
            echo pack ('
V', $gzip_size);
        }
        else
            ob_end_flush ();
    }
    
    function get_static_html ($file)
    {
        // Just for Test...
        
        $static_file = '
../../data/static/' . $file . '.' . $this->tpl_static_file_ext;
        if (@$fp = fopen ($static_file, '
wb')) {
            fwrite ($fp, ob_get_contents ());
            fclose ($fp);
        }
        
        ob_end_clean ();
        return;
    }
}

function addquote ($var)
{
    return str_replace("\\"", """, preg_replace("/[([a-zA-Z_x7f-xff][a-zA-Z0-9_x7f-xff]*)]/s", "['
\1']", $var));
}
    
function stripvtags ($expr, $statement)
{
    $expr = str_replace("\\"", """, preg_replace("/x7f-xff]*)?>/s", "\1", $expr));
    $statement = str_replace("
\\"", ""

    面附了一个简单的获取静态页的方法,其实也没什么用,大家都有更好的方法来生成静态页。 
   
    主要就是这些东西支撑起一个系统运行的必要部分。我从来不强调MVC层次,也不去讲究OOP,虽然偶尔也写一些很蹩脚的类。多年以来Pascal、C和汇编养成的习惯使我相比注意OO结构之外更注意执行效率。这次只是罗列了一些基于共享内存和tmpfs的优化方法。
    至于把什么样的数据放在tmpfs上,各位自己看着办。我把include文件、session、模板的编译结果、cache文件放在了上面。在提升IO性能的同时,它带来的另一个好处是不需要把这些文件放在web目录里,也提高了不少安全性。即使有一些文件需要放在web目录下,比如程序执行文件(废话……),也不要用奇怪的扩展名。对于config.inc.php这样的文件尤其要注意,不要使用config.inc这种文件名,很有可能你的系统忘了配置对.inc的支持,访问者可以直接在浏览器里访问config.inc就可以把这个文件下载走了,而这个文件里保存着你的数据库密码……
    走到这里,我们已经逐渐地跟上了优化的步伐,在后面的时间里,优化程序结构的同时,已经可以做好更输入地挖掘系统潜力的的准备了。将优化进行到底,挑战一下一台服务器到底能撑住多少个访问者是我近期的变态目标。不过再走下去,可能已经走出了PHP的领地,各位一定要有心理准备,因为我的C程序写得有时候比天书还乱…………hoho
    附上那个压缩/解压的类:

<?php
class BsmPkg
{
    //This class operates with PKG archive format...Haha

    //By Dr.NP 02-15-2006

    
    var $classname = 'BsmPkg';
    var $source_dir = '';
    var $target_dir = '';
    var $filename = '';
    var $max_filesize = 1048576;
    
    var $error_msg = '';
    var $line_ret = "n";
    
    function pack_from_dir ()
    {
        ini_set ('memory_limit', '32M');
        global $content;
        
        $source_dir = $this->source_dir ? $this->source_dir : './';
        
        $content = '<==PACKAGE_START==>' . $this->line_ret;
        $this->_GetDirs ($source_dir);
        $content .= '<==PACKAGE_END==>';
        $zfp = gzopen ($this->filename, 'wb9');
        gzwrite ($zfp, $content);
        gzclose ($zfp);
        return;
    }
    
    function unpack_into_dir ()
    {
        ini_set ('memory_limit', '32M');
        
        $target_dir = $this->target_dir ? $this->target_dir : './';
        $zfp = gzopen ($this->filename, 'rb');
        $content = gzread ($zfp, $this->max_filesize);
        gzclose ($zfp);
        $lines = explode ($this->line_ret, $content);
        while (list ($key, $line) = each ($lines)) {
            if (preg_match ("/<==Directory:([0-7]+)==>(S+)<==/Directory==>/is", $line, $march)) {
                $access_str = $march[1];
                $item_dir = $march[2];
                if (!is_dir ($target_dir . $item_dir)) {
                    mkdir ($target_dir . $item_dir);
                    @chmod ($target_dir . $item_dir, intval ($access_str, 8));
                }
            }
            
            if (preg_match ("/<==File:(d+)-([0-9a-f]+)-([0-7]+)==>(S+)<==/File==>/is", $line, $march)) {
                $target_file = $march[4];
                $access_str = $march[3];
                $target_file_checksum = $march[2];
                $target_filesize = $march[1];
                
                if (!@$fp = fopen ($target_dir . $target_file, 'wb')) {
                    continue;
                }
                
                if (false === (list ($key, $content) = each ($lines))) {
                    continue;
                }
                
                $file_content = base64_decode (trim ($content));
                
                if (!@fwrite ($fp, $file_content)) {
                    continue;
                }
                
                fclose ($fp);
                
                if (!@md5_file ($target_file) == $target_file_checksum) {
                    $this->error_msg = 'File : ' . $target_dir . $target_file . 'CheckSum Failed...';
                }
        
                @chmod ($target_dir . $target_file, intval ($access_str, 8));
            
            }
        }
        return;
    }
    
    function _GetDirs ($dirname)
    {
        global $content;
        
        $dh = opendir ($dirname);
        
        while (false !== ($item = readdir ($dh))) {
            $full_itemname = str_replace ('//', '/', $dirname . '/' . $item);
            if (strpos ($full_itemname, $this->source_dir) === 0)
                $pkg_itemname = substr ($full_itemname, strlen ($this->source_dir));
            else
                continue;
            
            if ($item != '.' && $item != '..' && $item != $this->filename) {
                if (is_dir ($full_itemname)) {
                    $access_str = substr (decoct (fileperms ($full_itemname)), -4);
                    $content .= "<==Directory:{$access_str}==>$pkg_itemname<==/Directory==>{$this->line_ret}";
                    $this->_GetDirs ($full_itemname);
                }
                
                elseif (is_file ($full_itemname) && is_readable ($full_itemname)) {
                    $filesize = filesize ($full_itemname);
                    $checksum = md5_file ($full_itemname);
                    $access_str = substr (decoct (fileperms ($full_itemname)), -4);
                    $content .= "<==File:{$filesize}-{$checksum}-{$access_str}==>$pkg_itemname<==/File==>{$this->line_ret}";
                    
                    @$fp = fopen ($full_itemname, 'rb');
                    if ($filesize > 0)
                        $source_file_str = fread ($fp, $filesize);
                    else
                        $source_file_str = '';
                    $base64_str = base64_encode ($source_file_str);
                    fclose ($fp);
                    
                    $content .= $base64_str . $this->line_ret;
                }
            }
        }
        return;
    }
}
?>

阅读(1386) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~