How to store PHP sessions in APC Cache?

纵然是瞬间 提交于 2019-12-20 09:13:31

问题


Storing sessions in disk very slow and painful for me. I'm having very high traffic. I want to store session in Advanced PHP Cache, how can I do this?


回答1:


I tried to lure better answers by offering 100 points as a bounty, but none of the answers were really satisfying.

I would aggregate the recommended solutions like this:

Using APC as a session storage

APC cannot really be used as a session store, because there is no mechanism available to APC that allows proper locking, But this locking is essential to ensure nobody alters the initially read session data before writing it back.

Bottom line: Avoid it, it won't work.

Alternatives

A number of session handlers might be available. Check the output of phpinfo() at the Session section for "Registered save handlers".

File storage on RAM disk

Works out-of-the-box, but needs a file system mounted as RAM disk for obvious reasons.

Shared memory (mm)

Is available when PHP is compiled with mm enabled. This is builtin on windows.

Memcache(d)

PHP comes with a dedicated session save handler for this. Requires installed memcache server and PHP client. Depending on which of the two memcache extensions is installed, the save handler is either called memcache or memcached.




回答2:


<?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);
    }
}



回答3:


In theory, you ought to be able to write a custom session handler which uses APC to do this transparently for you. However, I haven't actually been able to find anything really promising in a quick five-minute search; most people seem to be using APC for the bytecode cache and putting their sessions in memcached.




回答4:


Simply putting your /tmp disk (or, wherever PHP session files are stored) onto a RAM disk such as tmpfs or ramfs would also have serious performance gains, and would be a much more transparent switch, with zero code changes.

The performance gain may be significantly less, but it will still be significantly faster than on-disk sessions.




回答5:


Store it in cookies (encrypted) or MongoDB. APC isn't really intended for that purpose.




回答6:


You can store your session data within PHP internals shared memory.

session.save_handler = mm

But it needs to be available: http://php.net/manual/en/session.installation.php




回答7:


Another good solution is to store PHP sessions in memcached

session.save_handler = memcache




回答8:


Explicit Session Closing immediately following Session Starting, Opening and Writing should solve the locking problem in Unirgy's Answer(where session access is always cyclic(start/open-write-close). I also Imagine a Second class - APC_journaling or something similar used in conjunction with Sessions would be ultimately better.... A session starts and is written to with a unique external Id assigned to each session, that session is closed, and a journal (array in apc cache via _store & _add) is opened/created for any other writes intended to go to session which can then be read, validated and written to the session(identified by that unique id!) in apc at the next convenient opportunity.

I found a good blog post Explaining that the Locking havoc Sven refers to comes from the Session blocking until it's closed or script execution ends. The session being immediately closed doesn't prevent reading just writing. http://konrness.com/php5/how-to-prevent-blocking-php-requests - link to the blog post. Hope this helps.




回答9:


Caching external data in PHP

Tutorial Link - http://www.gayadesign.com/diy/caching-external-data-in-php/


How to Use APC Caching with PHP

Tutorial Link - http://www.script-tutorials.com/how-to-use-apc-caching-with-php/



来源:https://stackoverflow.com/questions/1724587/how-to-store-php-sessions-in-apc-cache

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!