Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/redis/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Debugging PHP Redis会话未保存_Debugging_Redis_Php - Fatal编程技术网

Debugging PHP Redis会话未保存

Debugging PHP Redis会话未保存,debugging,redis,php,Debugging,Redis,Php,编辑 我试着用xdebug和netbeans调试它。奇怪的是,如果我设置一些断点,那么导出将在调试会话期间工作。但是,由于没有断点,在更现实的环境中,导出不起作用 我尝试在代码的某些部分添加睡眠 我认为PHP可能在Redis提交完成之前就结束了。也许Redis连接是异步完成的,但我检查了PRedis,默认是同步连接 我正在开发一个报告工具 这是基本问题 我们将报告存储到会话对象中,但在以后的请求中,当我们尝试在会话对象中获取报告时,它就消失了 这是一个更详细的版本 我将“report”对象存储

编辑 我试着用xdebug和netbeans调试它。奇怪的是,如果我设置一些断点,那么导出将在调试会话期间工作。但是,由于没有断点,在更现实的环境中,导出不起作用

我尝试在代码的某些部分添加睡眠

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


我正在开发一个报告工具

这是基本问题

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

这是一个更详细的版本

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

  $_SESSION['report_name_unixtimestamp'] = gzcompress( serialize( $reportObject ) );
用户以某种表格形式查看报告,如果需要,可以将其导出。报告可能会发生变化,因此将其存储在会话中的想法是,当用户将其导出为PDF、Excel等时,他们将得到一份与他们正在查看的报告相同的报告

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

在我们尝试引入Redis缓存服务器作为更好的会话管理工具之前,这一直都很有效

现在发生的情况如下:

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

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

如果我们在redis服务器上检查报告的较新版本,它不在那里,但旧的时间戳仍然在那里

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

有人有什么想法吗

以下是更多细节:

  • Redis没有耗尽内存。我们已经检查过很多次了
  • 我们已经知道,要在会话中取消序列化report对象,必须已经包含report类。(请记住,第一次导出工作正常,但之后的任何导出都会失败)
  • 如果我们在运行报告的请求期间检查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 )
            {
    
    
    
            }
        }
    

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

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

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


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

    您能(仅用于诊断)为会话变量使用不同的键吗?例如md5('report_name_unixtimestamp')或'timestamp reportname'?背景:我怀疑钥匙被截断了somwhere@EugenRieck,您的意思是像这样的$会话[md5('report\u name\u 1234')?也许吧,但报告没有解释为什么第一个报告导出有效。报告的密钥长度是相同的,因为它是相同的名称,只是时间戳不同。“不过我会试试的,谢谢你。”尤金瑞克,它没用。我试着按照您的建议执行md5哈希,但它也不起作用。我的意思是:假设密钥在“report_name”的读取路径上被截断,那么客户端执行GETKEYS,然后执行GETKEYS。使用$_SESSION[md5('report_name_1234')]有助于我们排除这种可能性。很抱歉,我当时没有主意。我们最终没有使用redis进行会话管理,现在只使用php会话。但是解决方案是编写一个编码到其中的存储处理程序锁定机制,以便会话的更改被不同的请求捕获。不完全是锁定,而是在每个会话上维护一个校验和,当写入它时,如果校验和已更改,则需要合并两个数组,否则,只需覆盖。在PHP中做起来很简单,但在CPP中却不是这样@Itaymav Malimovka,我可能会使用时间戳,但这只是我的观点。如果保存时的时间戳与读取时的时间戳不同,则将新会话与您的会话合并。如果是相同的时间戳,则写入会话并更新时间戳。典型的乐观锁定。为什么不在PHP中使用内置会话处理程序并将其指向Redis?由于PHP本机支持文件或memcache作为存储,我不明白您为什么不能使用它?Redis与memcache兼容。