РЕДАКТИРОВАТЬ Я попытался отладить это с помощью xdebug и netbeans.Странно, что экспорт будет работать во время сеанса отладки, если я установлю несколько точек останова.Однако без точек разрыва, более реалистичной среды экспорт не работает.
Я пытался добавить сны в некоторые части кода.
Я думаю, что, возможно, PHP заканчивается до того, как завершится коммит Redis.Возможно, соединения Redis выполняются асинхронно, но я проверил PRedis, и по умолчанию используется синхронное соединение.
Я работаю над инструментом отчетности.
Вот основная проблема.
Мы сохраняем отчет в объекте сеанса, но при последующих запросах, когда мы пытаемся добраться до отчета в объекте сеанса, он исчезает.
Вот более подробная версия.
Я сохраняю объект «отчет» в сеансе следующим образом:
$_SESSION['report_name_unixtimestamp'] = gzcompress( serialize( $reportObject ) );
Пользователь видит отчет в некоторой табличной форме, а затем, если он хочет, онможет экспортировать это.Отчет может измениться, поэтому идея его сохранения в сеансе заключается в том, что когда пользователь экспортирует его в PDF, Excel и т. Д., Он получит отчет, идентичный тому, который он просматривает.
Пользователь нажимает кнопку экспорта, и на стороне PHP он входит в сеанс, получает отчет с помощью ключа, предоставленного в качестве параметра get (распаковывает и десериализует его), создает экспорт и отправляет его впользователь для загрузки.
Это работало до тех пор, пока мы не попытались представить сервер кэширования Redis как инструмент для лучшего управления сессиями.
Теперь происходит следующее:
При первом запуске отчета он сохраняется в кеше, и экспорт будет успешно работать.
Мы запустимповторите отчет с той же учетной записью пользователя в том же сеансе.Это изменяет метку unixtime, поэтому в $_SESSION
должно быть две записи.($_SESSION['report_name_oldertimetamp']
и $_SESSION['report_name_newertimestamp']
).Когда мы снова нажимаем кнопку экспорта, мы получаем сообщение о том, что файл не существует (поскольку он не был отправлен сервером).
Если мы проверяем сервер redis на наличие более новой версиив отчете его нет, но старая временная метка все еще там.
Теперь это работало с управлением файловыми сессиями, но не с Redis.мы попробовали модуль redis для php, а также чистый клиент php Predis.
У кого-нибудь есть идеи?
Вот еще несколько подробностей:
- У Redis не хватает памяти.Мы проверяли это много раз.
- Мы уже знаем, что для десериализации объекта отчета в сеансе класс отчета уже должен быть включен.(помните, что первый экспорт работает нормально, но после этого ничего не получается)
- Если мы проверим объект сеанса 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 )
{
}
}