编辑 我尝试使用xdebug和netbeans进行调试。奇怪的是,如果我加入一些断点,导出操作将在调试会话期间起作用。但是,在没有断点的情况下,更真实的环境下,导出操作不起作用。
我尝试在代码的某些部分添加等待时间。
我认为可能是PHP在Redis提交完成之前就结束了。也许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。
有人有什么想法吗?
以下是使用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 )
{
}
}