簡體   English   中英

如何在APC緩存中存儲PHP會話?

[英]How to store PHP sessions in APC Cache?

將會話存儲在磁盤上對我來說非常緩慢且痛苦。 我的流量很高。 我想將會話存儲在高級PHP緩存中,該怎么做?

<?php

// to enable paste this line right before session_start():
//   new Session_APC;
class Session_APC
{
    protected $_prefix;
    protected $_ttl;
    protected $_lockTimeout = 10; // if empty, no session locking, otherwise seconds to lock timeout

    public function __construct($params=array())
    {
        $def = session_get_cookie_params();
        $this->_ttl = $def['lifetime'];
        if (isset($params['ttl'])) {
            $this->_ttl = $params['ttl'];
        }
        if (isset($params['lock_timeout'])) {
            $this->_lockTimeout = $params['lock_timeout'];
        }

        session_set_save_handler(
            array($this, 'open'), array($this, 'close'),
            array($this, 'read'), array($this, 'write'),
            array($this, 'destroy'), array($this, 'gc')
        );
    }

    public function open($savePath, $sessionName)
    {
        $this->_prefix = 'BSession/'.$sessionName;
        if (!apc_exists($this->_prefix.'/TS')) {
            // creating non-empty array @see http://us.php.net/manual/en/function.apc-store.php#107359
            apc_store($this->_prefix.'/TS', array(''));
            apc_store($this->_prefix.'/LOCK', array(''));
        }
        return true;
    }

    public function close()
    {
        return true;
    }

    public function read($id)
    {
        $key = $this->_prefix.'/'.$id;
        if (!apc_exists($key)) {
            return ''; // no session
        }

        // redundant check for ttl before read
        if ($this->_ttl) {
            $ts = apc_fetch($this->_prefix.'/TS');
            if (empty($ts[$id])) {
                return ''; // no session
            } elseif (!empty($ts[$id]) && $ts[$id] + $this->_ttl < time()) {
                unset($ts[$id]);
                apc_delete($key);
                apc_store($this->_prefix.'/TS', $ts);
                return ''; // session expired
            }
        }

        if (!$this->_lockTimeout) {
            $locks = apc_fetch($this->_prefix.'/LOCK');
            if (!empty($locks[$id])) {
                while (!empty($locks[$id]) && $locks[$id] + $this->_lockTimeout >= time()) {
                    usleep(10000); // sleep 10ms
                    $locks = apc_fetch($this->_prefix.'/LOCK');
                }
            }
            /*
            // by default will overwrite session after lock expired to allow smooth site function
            // alternative handling is to abort current process
            if (!empty($locks[$id])) {
                return false; // abort read of waiting for lock timed out
            }
            */
            $locks[$id] = time(); // set session lock
            apc_store($this->_prefix.'/LOCK', $locks);
        }

        return apc_fetch($key); // if no data returns empty string per doc
    }

    public function write($id, $data)
    {
        $ts = apc_fetch($this->_prefix.'/TS');
        $ts[$id] = time();
        apc_store($this->_prefix.'/TS', $ts);

        $locks = apc_fetch($this->_prefix.'/LOCK');
        unset($locks[$id]);
        apc_store($this->_prefix.'/LOCK', $locks);

        return apc_store($this->_prefix.'/'.$id, $data, $this->_ttl);
    }

    public function destroy($id)
    {
        $ts = apc_fetch($this->_prefix.'/TS');
        unset($ts[$id]);
        apc_store($this->_prefix.'/TS', $ts);

        $locks = apc_fetch($this->_prefix.'/LOCK');
        unset($locks[$id]);
        apc_store($this->_prefix.'/LOCK', $locks);

        return apc_delete($this->_prefix.'/'.$id);
    }

    public function gc($lifetime)
    {
        if ($this->_ttl) {
            $lifetime = min($lifetime, $this->_ttl);
        }
        $ts = apc_fetch($this->_prefix.'/TS');
        foreach ($ts as $id=>$time) {
            if ($time + $lifetime < time()) {
                apc_delete($this->_prefix.'/'.$id);
                unset($ts[$id]);
            }
        }
        return apc_store($this->_prefix.'/TS', $ts);
    }
}

從理論上講,您應該能夠編寫一個使用APC為您透明地執行此操作的自定義會話處理程序 但是,我實際上無法在快速的五分鍾搜索中找到真正有希望的東西。 大多數人似乎將APC用於字節碼緩存,並將其會話放入memcached。

只需將您的/ tmp磁盤(或PHP會話文件的存儲位置)放到諸如tmpfsramfs的RAM磁盤上,也將顯着提高性能,並且將是一個更加透明的開關,零代碼更改。

性能提升可能會大大降低,但仍將比磁盤會話快得多。

我嘗試通過提供100分作為獎勵來吸引更好的答案,但是沒有一個答案真正令人滿意。

我將匯總推薦的解決方案,如下所示:

使用APC作為會話存儲

APC不能真正用作會話存儲,因為APC沒有可用的機制允許適當的鎖定。但是,此鎖定對於確保沒有人在回寫之前讀取的初始會話數據至關重要。

底線:避免它,它將不起作用。

備擇方案

可能有許多會話處理程序。 在“ Session部分檢查phpinfo()的輸出是否有“已注冊的保存處理程序”。

RAM磁盤上的文件存儲

開箱即用,但出於顯而易見的原因,需要將文件系統安裝為RAM磁盤。

共享內存(毫米)

在啟用mm情況下編譯PHP時可用。 這是內置在Windows上的。

記憶快取(d)

PHP為此提供了專用的會話保存處理程序。 需要安裝的Memcache服務器和PHP客戶端。 根據安裝的兩個memcache擴展中的哪個,保存處理程序稱為memcachememcached

將其存儲在cookie(加密)或MongoDB中。 APC並非真正用於此目的。

您可以將會話數據存儲在PHP內部共享內存中。

session.save_handler = mm

但是它需要可用: http : //php.net/manual/en/session.installation.php

另一個好的解決方案是將PHP會話存儲在memcached中

session.save_handler = memcache

在會話開始,打開和寫入之后立即顯式關閉會話應該可以解決Unirgy的Answer中的鎖定問題(會話訪問始終是循環的(開始/打開-寫入-關閉)。我也想像一下第二類-APC_journaling或類似的東西聯合使用使用Sessions最終會更好。...啟動一個會話並使用分配給每個會話的唯一外部ID寫入該會話,然后關閉該會話,並為其打開/創建日志(通過_store和_add在apc緩存中的數組)下一次方便的機會,可以在apc中讀取,驗證並寫入會話的其他任何打算進入會話的寫入(由該唯一ID!標識)。

我找到了一篇很好的博客文章,解釋了Sven所指的Locking havoc Sven來自會話阻塞,直到它關閉或腳本執行結束。 立即關閉的會話並不會阻止您只閱讀寫作。 http://konrness.com/php5/how-to-prevent-blocking-php-requests-鏈接到博客文章。 希望這可以幫助。

用PHP緩存外部數據

教程鏈接-http: //www.gayadesign.com/diy/caching-external-data-in-php/


如何在PHP中使用APC緩存

教程鏈接-http: //www.script-tutorials.com/how-to-use-apc-caching-with-php/

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM