PHP Redis Session不保存

时间:2011-12-23 18:52:51

标签: debugging redis php

修改 我尝试使用xdebug和netbeans进行调试。如果我输入一些断点,那么在调试会话期间导出将起作用是很奇怪的。但是,没有断点,更现实的环境,出口不起作用。

我尝试在代码的某些部分添加sleep。

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


我正在制作报告工具。

这是基本问题。

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

这是一个更详细的版本。

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

  $_SESSION['report_name_unixtimestamp'] = gzcompress( serialize( $reportObject ) );

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

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

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

现在发生的事情如下:

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

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

如果我们检查redis服务器上是否有更新版本的报告,那么它就不存在了,但旧的时间戳仍然存在。

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

有没有人有任何想法?

以下是一些细节:

  1. Redis的内存不足。我们已经多次检查过了。
  2. 我们已经知道要在会话中反序列化报表对象,必须已包含报表类。 (记住,第一次出口工作正常,但之后的任何事情都失败了)
  3. 如果我们在运行报告的请求期间检查php会话对象,它将包含较新的报告,但它永远不会进入Redis。
  4. 以下是与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 )
            {
    
    
    
            }
        }
    

1 个答案:

答案 0 :(得分:2)

这个问题已经解决,而且比我想象的要多得多。

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

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

我得到了我的一位同事的帮助。啊!这让我很头疼,我很高兴能够结束。