如何在 APC 缓存中存储 PHP 会话?

发布于 2024-08-10 13:50:09 字数 61 浏览 3 评论 0原文

将会话存储在磁盘中对我来说非常缓慢且痛苦。我的流量非常高。我想将会话存储在高级 PHP 缓存中,我该怎么做?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

给不了的爱 2024-08-17 13:50:09
<?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);
    }
}
<?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);
    }
}
爱的十字路口 2024-08-17 13:50:09

我试图通过提供 100 分作为赏金来吸引更好的答案,但没有一个答案真正令人满意。

我会汇总这样推荐的解决方案:

使用APC作为会话存储

APC不能真正用作会话存储,因为APC没有可用的机制来允许正确的锁定,但是这种锁定对于确保没有人改变最初读取的数据至关重要写回之前的会话数据。

底线:避免它,它不会起作用。

替代方案

许多会话处理程序可能可用。检查 Session 部分的 phpinfo() 输出中的“Registered save handlers”。

RAM 磁盘上的文件存储

开箱即用,但由于明显的原因需要将文件系统安装为 RAM 磁盘。

共享内存 (mm)

当 PHP 编译时启用 mm 时可用。这是 Windows 上内置的。

Memcache(d)

PHP 为此提供了一个专用的会话保存处理程序。需要安装 memcache 服务器和 PHP 客户端。根据安装的两个 memcache 扩展中的哪一个,保存处理程序称为 memcachememcached

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.

万水千山粽是情ミ 2024-08-17 13:50:09

理论上,您应该能够编写一个 自定义会话处理程序使用 APC 透明地为您完成此操作。然而,在五分钟的快速搜索中,我实际上并没有找到任何真正有希望的东西;大多数人似乎使用 APC 作为字节码缓存,并将会话放入 memcached 中。

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.

硪扪都還晓 2024-08-17 13:50:09

只需将 /tmp 磁盘(或者存储 PHP 会话文件的任何位置)放到 RAM 磁盘(例如 tmpfs 或 ramfs )上也会带来显着的性能提升,并且更透明的切换,零代码更改。

性能提升可能会明显减少,但仍然比磁盘会话快得多。

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.

深居我梦 2024-08-17 13:50:09

将其存储在 cookie(加密)或 MongoDB 中。 APC 并不是真正用于此目的。

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

眼眸 2024-08-17 13:50:09

您可以将会话数据存储在 PHP 内部共享内存中。

session.save_handler = mm

但它需要可用:http://php.net/manual/en/session。安装.php

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

笑红尘 2024-08-17 13:50:09

另一个好的解决方案是将 PHP 会话存储在 memcached

session.save_handler = memcache

Another good solution is to store PHP sessions in memcached

session.save_handler = memcache

赢得她心 2024-08-17 13:50:09

会话开始、打开和写入后立即显式会话关闭应该可以解决 Unirgy 答案中的锁定问题(其中会话访问始终是循环的(开始/打开-写入-关闭)。我还想象第二个类 - APC_journaling 或类似的组合使用使用会话最终会更好......会话启动并使用分配给每个会话的唯一外部 ID 进行写入,该会话关闭,并且打开/创建日志(通过 _store 和 _add 在 apc 缓存中的数组)对于任何其他打算进入会话的写入,然后可以在下次方便的时候在 apc 中读取、验证并写入会话(由该唯一 id 标识!),

我发现了一篇很好的博客文章解释了锁定破坏 Sven 提到的。来自会话阻塞,直到它关闭或脚本执行结束,会话立即关闭并不会阻止读取和写入。
http://konrness.com/php5/how-to-prevent-blocking -php-requests - 链接到博客文章。
希望这有帮助。

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.

少跟Wǒ拽 2024-08-17 13:50:09

在 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/

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/

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文