2011-12-23 3 views
2

EDIT xdebug 및 netbeans를 사용하여 디버깅을 시도했습니다. 일부 중단 점을 넣으면 디버그 세션 중에 내보내기가 작동한다는 것은 이상한 일입니다. 그러나 브레이크 포인트가 없으면보다 현실적인 환경에서 수출이 효과적이지 않습니다.PHP Redis 세션 저장 안함

코드의 일부분에 잠자기를 추가하려고했습니다.

필자는 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뿐만 아니라 순수 PHP 클라이언트 Predis에 대한 redis 모듈을 시도했습니다.

누구에게 아이디어가 있습니까?

  1. 레디 스 메모리가 부족하지 않은 : 여기

    은 몇 가지 더 자세한 내용입니다. 우리는 이것을 여러 번 확인했습니다.
  2. 세션에서 보고서 개체의 serialize를 해제하려면 보고서 클래스가 이미 포함되어 있어야한다는 것을 이미 알고 있습니다. (기억, 첫 번째 내보내기 잘 작동하지만 그 이후의 모든 실패)
  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) 
     { 



     } 
    } 
+0

진단을 위해서만 세션 변수에 다른 키를 사용할 수 있습니까? md5 ('report_name_unixtimestamp') 또는 'timestamp-reportname'같은가? 배경 : 나는 열쇠가 잘리는 것처럼 보입니다 –

+0

@EugenRieck,이 뜻은 $ _SESSION [md5 ('report_name_1234')]입니까? 아마도, 그러나 첫 번째 보고서 내보내기가 왜 작동하는지 설명하지는 못할 것입니다. 키 길이는 동일한 시간 소인이라는 동일한 이름이므로 보고서에서 동일합니다. 나는 그것을 시도 할 것이다, 고마워. –

+0

@EugenRieck, 작동하지 않았습니다. 내가 제안한대로 md5 해시를 시도했지만 어느 쪽도 작동하지 않습니다. –

답변

2

문제가 해결되어서 생각했던 것보다 훨씬 어리 석다.

저장 핸들러는 어떤 식 으로든 잠금을 구현하지 않습니다. 보고서 페이지에는 ajax 등을 통해 서버에 여러 요청이 있습니다. Ajax 요청 중 하나는 보고서가 세션 공간에 저장되기 전에 시작됩니다. 따라서 세션을 읽은 다음 끝에 세션을 씁니다.

보고서가 매번 더 빠르게 실행되므로 보고서는 Redis의 세션에 캐시되지만 이전 버전의 sessien을 가진 다른 스크립트로 덮어 쓰게됩니다.

나는 동료 중 한 사람에게서 도움을 받았습니다. 아! 이것은 끝내기가 좋았던 두통이었습니다.

+0

그래서 ... 어떻게 그걸 해결 했니? –

+0

우리는 세션 관리를 위해 redis를 사용하지 않고 PHP 세션을 고수했습니다. 그러나 솔루션은 세션에 대한 변경 사항이 다른 요청에 의해 캐치되도록 세션에 코딩 된 저장 핸들러 잠금 메커니즘을 작성하는 것입니다. –

+0

사실 나는 그것에 대해 약간의 생각을했습니다. 정확하게 잠금이 아니라 오히려 각각의 세션에서 체크섬을 유지하고, 그것을 쓸 때 체크섬이 변경되면 두 배열을 모두 병합해야합니다. 그렇지 않으면 덮어 씁니다. CPP에서 PHP로 수행하는 것이 더 간단합니다. –