php redis 연결 끊김, pconnect 연결 실패 문제
소개하다.
swoole, workerman과 같은 CLI 긴 연결 모드에서 Redis가 비정상적으로 끊어졌다가 다시 켜지는 경우 일반적으로 프로그램을 다시 시작해야 정상적으로 사용할 수 있습니다.
본문에서는 서비스를 재개하지 않고 원래의 Redis 단선을 실현하는 방법을 소개합니다.
원리
Redis 연결이 끊겼을 때 호출하기
$Redis->ping()은 Notice 오류를 트리거합니다. Notice: Redis::ping(): send of 14 bytes failed with errno=10054
redis 인스턴스를 가져올 때 ping이 불통되거나 이상이 생기면 다시 연결
실현1
try catch가 notice 이상을 포착하지 못하기 때문에 ping 불통으로 직접 재접속하고 catch가 새로운 연결을 포착한 인스턴스가 연결되지 않아 다음에 ping 트리거를 실행합니다
Redis server went away 이상
public static function getInstance( )
{
try {
if (!self::$_instance) {
new self( )
} else {
if (!self::$_instance->ping())
new self( )
}
} catch (\Exception $e) {
// 끊기고 다시 이어지다.
new self( )
}
return self::$_instance;
}
구현 2
1 ping을 호출하기 전에 notice 이상을 던집니다.
2 ping 호출
3 error_get_last를 사용하여 마지막 오류를 얻고, 오류 메시지가 우리가 던진 것과 같다면 ping이 통했다는 것을 의미하며, 그렇지 않으면 이상을 던져 catch가 재연결을 수행하는 것을 포착하도록 한다.
다시 접속하지 않을 때 $_instance->ping()을 다시 호출하면 Redis server went away 이상이 바로 catch에 포착됩니다
public static function getInstance( )
{
if (!self::$_instance) {
new self( )
}
else{
try {
@trigger_error('flag', E_USER_NOTICE)
self::$_instance->ping(;
$error = error_get_last(;
if($error['message'] != 'flag')
throw new \Exception('Redis server went away');
} catch (\Exception $e) {
// 끊기고 다시 이어지다.
new self( )
}
}
return self::$_instance;
}
Redis 클래스 전체 코드
<?php
namespace lib
class Redis
{
private static $_instance; // 저장 개체
private function __construct( ){
$config = Config::get('redis');
self::$_instance = new \Redis( );
// 설정에서 읽기
self::$_instance->pconnect($config['host'], $config['port']);
if ('''! = $config['password']) {
self::$_instance->auth($config['password']);
}
}
public static function getInstance( )
{
if (!self::$_instance) {
new self( )
}
else{
try {
@trigger_error('flag', E_USER_NOTICE)
self::$_instance->ping(;
$error = error_get_last(;
if($error['message'] != 'flag')
throw new \Exception('Redis server went away');
} catch (\Exception $e) {
// 끊기고 다시 이어지다.
new self( )
}
}
return self::$_instance;
}
// public static function getInstance( )
// {
// try {
// if (!self::$_instance) {
// new self( )
// } else {
// if (!self::$_instance->ping())
// new self( )
// }
// } catch (\Exception $e) {
// // 끊어졌다가 다시 연결되다
// new self( )
// }
// return self::$_instance;
// }
/**
* clone 금지
*/
private function _clone({})
/**
* 다른 메서드 자동 호출
* @param $method
* @param $args
* @return mixed
*/
public function __call($method,$args)
{
return call_user_func_array([self::$_instance, $method], $args);
}
/**
* 정적 호출
* @param $method
* @param $args
* @return mixed
*/
public static function __callStatic($method,$args)
{
self::getInstance( );
return call_user_func_array([self::$_instance, $method], $args);
}
}
호출하다
$this->handler = Redis::getInstance();
$key = $this->getCacheKey($name);
$value = $this->handler->get($key);
보충하다
pconnect 연결 설정 후 재연결 실패 문제
긴 연결을 테스트한 후 pconnect를 사용하여 연결을 설정한 후 redis는 시간 초과에 수동적으로 연결을 끊었습니다.
$res = self::$_instance->pconnect($config['host'], $config['port']);
$res는 true를 반환하지만, 새로 만든 링크는 아닙니다. $res-get()을 호출하면 오류가 발생합니다.
원인
연구 결과
pconnect를 사용하여 링크는 php 프로세스의 전체 수명 주기 동안 재사용되며 close의 역할은 현재 php가 redis 요청을 더 이상 수행할 수 없도록 하는 것이지만 redis 긴 연결을 실제로 닫을 수는 없으며 연결은 fpm 프로세스의 수명 주기가 끝날 때까지 후속 요청에서 재사용됩니다.
긴 연결에서는 프로세스가 중단되어야 연결이 끊어지기 때문에 연결이 끊겼을 때 new가 작동하지 않고 연결이 성공적으로 반환되었으며, 사실상 연결이 끊겼으며, 최초의 연결로 인해 후속 데이터 판독 작업이 불가능했습니다.
그래서 긴 연결에는 connect를 사용해 주세요
'개발 꿀팁 > PHP' 카테고리의 다른 글
php 그림을 mysql bolb 형식으로 저장 (0) | 2022.11.02 |
---|---|
PHP 패키지 이상 클래스, 등록 오류 및 이상 처리 메커니즘 (0) | 2022.11.02 |
php Warning, Notice 오류 캡처 (0) | 2022.11.02 |
php가 같은 id를 병합 (0) | 2022.11.02 |
php는 2차원 배열에 중복된 id가 있는지 여부를 판단한다 (0) | 2022.11.02 |