PHP Redis 会话不保存

Jer*_*via 6 php debugging redis

编辑 我尝试使用 xdebug 和 netbeans 进行调试。奇怪的是,如果我放入一些断点,导出将在调试会话期间工作。然而,在没有断点、更现实的环境的情况下,导出不起作用。

我尝试将睡眠添加到代码的某些部分。

我认为也许 PHP 在 Redis 提交完成之前就结束了。也许Redis连接是异步完成的,但我检查了PRedis,默认是同步连接。


我正在开发一个报告工具。

这是基本问题。

我们将报告存储到会话对象中,但在以后的请求中,当我们尝试访问会话对象中的报告时,它就消失了。

这是更详细的版本。

我将“报告”对象存储到会话中,如下所示

  $_SESSION['report_name_unixtimestamp'] = gzcompress( serialize( $reportObject ) );
Run Code Online (Sandbox Code Playgroud)

用户可以看到某种表格形式的报告,然后如果他们愿意,可以将其导出。报告可能会发生变化,因此将其存储在会话中的想法是,当用户将其导出为 PDF、Excel 等时,他们将获得一份与他们正在查看的报告相同的报告。

用户单击导出按钮,在 PHP 端,它将进入会话,通过作为 get 参数提供的密钥获取报告(解压缩和反序列化),创建导出并将其发送给用户下载。

直到我们尝试引入 Redis 缓存服务器作为更好的会话管理工具之前,这种方法一直运行良好。

现在发生的情况如下:

我们第一次运行报告时,它将存储到缓存中,导出将成功。

我们将在同一会话中使用相同的用户帐户再次运行该报告。这会更改 unixtimestamp,因此 .txt 文件中应该有两个条目$_SESSION。($_SESSION['report_name_oldertimetamp']$_SESSION['report_name_newertimestamp'])。当我们再次单击导出按钮时,我们收到一条错误消息,指出该文件不存在(因为服务器尚未发送该文件)。

如果我们检查 Redis 服务器中是否有较新版本的报告,则该报告不存在,但旧时间戳仍然存在。

现在,这适用于文件会话管理,但不适用于 Redis。我们尝试了 php 的 redis 模块以及纯 php 客户端 Predis。

有人有什么想法吗?

以下是更多细节:

  1. Redis 尚未耗尽内存。我们已经检查过很多次了。
  2. 我们已经知道,要在会话中反序列化报表对象,必须已经包含报表类。(记住,第一次导出工作正常,但之后的任何操作都会失败)
  3. 如果我们在报告运行的请求期间检查 php 会话对象,它将包含较新的报告,但它永远不会发送到 Redis。

下面是与 Predis 一起使用的保存处理程序。redis_session_init 是我在 session_start() 之前调用的函数,以便它被注册。我不确定 redis_session_write 函数是如何工作的,所以也许有人可以帮助我。

    <?php
    namespace RedisSession
    {

        $redisTargetPrefix = "PHPREDIS_SESSION:";
        $unpackItems = array( );
        $redisServer = "tcp://cache.emcweb.com";

        function redis_session_init( $unpack = null, $server = null, $prefix = null )
        {
            global $unpackItems, $redisServer, $redisTargetPrefix;

            if( $unpack !== null )
            {
                $unpackItems = $unpack;
            }

            if( $server !== null )
            {
                $redisServer = $server;
            }

            if( $prefix !== null )
            {
                $redisTargetPrefix = $prefix;
            }

            session_set_save_handler( 'RedisSession\redis_session_open', 'RedisSession\redis_session_close', 'RedisSession\redis_session_read', 'RedisSession\redis_session_write', 'RedisSession\redis_session_destroy', 'RedisSession\redis_session_gc' );
        }

        function redis_session_read( $id )
        {
            global $redisServer, $redisTargetPrefix;

            $redisConnection = new \Predis\Client( $redisServer );
            return base64_decode( $redisConnection->get( $redisTargetPrefix . $id ) );
        }

        function redis_session_write( $id, $data )
        {
            global $unpackItems, $redisServer, $redisTargetPrefix;

            $redisConnection = new \Predis\Client( $redisServer );
            $ttl = ini_get( "session.gc_maxlifetime" );

            $redisConnection->pipeline( function ($r) use (&$id, &$data, &$redisTargetPrefix, &$ttl, &$unpackItems)
        {
            $r->setex( $redisTargetPrefix . $id, $ttl, base64_encode( $data ) );

            foreach( $unpackItems as $item )
            {
                $keyname = $redisTargetPrefix . $id . ":" . $item;

                if( isset( $_SESSION[ $item ] ) )
                {
                    $r->setex( $keyname, $ttl, $_SESSION[ $item ] );
                }
                else
                {
                    $r->del( $keyname );
                }
            }
        } );
        }

        function redis_session_destroy( $id )
        {
            global $redisServer, $redisTargetPrefix;

            $redisConnection = new \Predis\Client( $redisServer );
            $redisConnection->del( $redisTargetPrefix . $id );

            $unpacked = $redisConnection->keys( $redisTargetPrefix . $id . ":*" );

            foreach( $unpacked as $unp )
            {
                $redisConnection->del( $unp );
            }
        }

        // These functions are all noops for various reasons... opening has no practical meaning in
        // terms of non-shared Redis connections, the same for closing. Garbage collection is handled by
        // Redis anyway.
        function redis_session_open( $path, $name )
        {

        }

        function redis_session_close()
        {

        }

        function redis_session_gc( $age )
        {



        }
    }
Run Code Online (Sandbox Code Playgroud)

Jer*_*via 4

问题解决了,但比我想象的要愚蠢得多。

保存处理程序不以任何方式实现锁定。在报告页面上,通过 ajax 等向服务器发出多个请求。在将报表保存到会话空间之前,将启动一个 ajax 请求。因此,它读取会话,然后在最后写入会话。

由于报告每次执行速度都更快,因此报告将被缓存到 Redis 中的会话中,但随后会被具有旧版本 sessien 的其他脚本覆盖。

我得到了一位同事的帮助。啊! 这是一个令人头疼的问题,我很高兴结束了。