Files
2025-02-10 10:39:00 +08:00

265 lines
6.8 KiB
PHP

<?php
declare(strict_types=1);
namespace app\manager\sys;
use think\facade\Config;
use RedisException;
use Redis;
use RedisCluster;
class RedisManager
{
private static ?self $instance = null;
private Redis|RedisCluster|null $redis = null;
private string $prefix;
/**
* 获取RedisManager实例
*/
public static function getInstance(?array $config = null): self
{
if (self::$instance === null) {
self::$instance = new self($config);
}
return self::$instance;
}
/**
* 构造函数
*/
private function __construct(?array $config = null)
{
$this->initializeConnection($config);
$this->prefix = Config::get('database.connections.redis.prefix', '');
}
/**
* 初始化Redis连接
*/
private function initializeConnection(?array $config = null): void
{
$config ??= Config::get('database.connections.redis', []);
$hostName = config("app.host_name");
try {
match($hostName) {
'HHKK' => $this->initCluster([
'192.168.1.3:7000',
'192.168.1.2:7001',
'192.168.1.31:7002'
]),
'H36HK' => $this->initCluster([
'192.168.1.3:7000',
'192.168.1.2:7001',
'192.168.1.5:7002',
'192.168.1.41:7003',
'192.168.1.51:7004'
]),
default => $this->initSingle($config)
};
} catch (\Throwable $e) {
throw new RedisException("Redis连接失败: " . $e->getMessage());
}
}
/**
* 初始化单机Redis
*/
private function initSingle(array $config): void
{
$this->redis = new Redis();
$this->redis->connect(
$config['host'] ?? '127.0.0.1',
$config['port'] ?? 6379,
$config['timeout'] ?? 10
);
if (!empty($config['password'])) {
$this->redis->auth($config['password']);
}
if (isset($config['db']) && is_numeric($config['db'])) {
$this->redis->select((int)$config['db']);
}
}
/**
* 初始化Redis集群
*/
private function initCluster(array $nodes): void
{
$this->redis = new RedisCluster(null, $nodes, 5);
}
/**
* 获取Redis实例
*/
public function getRedis(): Redis|RedisCluster
{
return $this->redis;
}
/**
* String 操作
*/
public function get(string $key): mixed
{
try {
$value = $this->redis?->get($this->prefix . $key);
return $this->unserialize($value);
} catch (\Throwable) {
return null;
}
}
public function set(string $key, mixed $value, int $expire = 0): bool
{
try {
$value = $this->serialize($value);
if ($expire > 0) {
return (bool)$this->redis?->setex($this->prefix . $key, $expire, $value);
}
return (bool)$this->redis?->set($this->prefix . $key, $value);
} catch (\Throwable) {
return false;
}
}
/**
* List 操作
*/
public function lPush(string $key, mixed $value): int
{
try {
return $this->redis?->lPush($this->prefix . $key, $this->serialize($value)) ?? 0;
} catch (\Throwable) {
return 0;
}
}
public function lRange(string $key, int $start = 0, int $end = -1): array
{
try {
$data = $this->redis?->lRange($this->prefix . $key, $start, $end) ?? [];
return array_map(fn($item) => $this->unserialize($item), $data);
} catch (\Throwable) {
return [];
}
}
/**
* Hash 操作
*/
public function hSet(string $key, string $field, mixed $value): bool
{
try {
return (bool)$this->redis?->hSet(
$this->prefix . $key,
$field,
$this->serialize($value)
);
} catch (\Throwable) {
return false;
}
}
public function hGet(string $key, string|array|null $field = null): mixed
{
try {
if ($field === null) {
$data = $this->redis?->hGetAll($this->prefix . $key) ?? [];
return array_map(fn($item) => $this->unserialize($item), $data);
}
if (is_array($field)) {
$data = $this->redis?->hMGet($this->prefix . $key, $field) ?? [];
return array_map(fn($item) => $this->unserialize($item), $data);
}
$value = $this->redis?->hGet($this->prefix . $key, $field);
return $this->unserialize($value);
} catch (\Throwable) {
return null;
}
}
/**
* Set 操作
*/
public function sAdd(string $key, mixed $value): int
{
try {
if (is_array($value)) {
$count = 0;
foreach ($value as $v) {
$count += $this->redis?->sAdd(
$this->prefix . $key,
$this->serialize($v)
) ?? 0;
}
return $count;
}
return $this->redis?->sAdd($this->prefix . $key, $this->serialize($value)) ?? 0;
} catch (\Throwable) {
return 0;
}
}
public function sMembers(string $key): array
{
try {
$members = $this->redis?->sMembers($this->prefix . $key) ?? [];
return array_map(fn($item) => $this->unserialize($item), $members);
} catch (\Throwable) {
return [];
}
}
/**
* 通用操作
*/
public function delete(string|array $key): bool
{
try {
$keys = is_array($key)
? array_map(fn($k) => $this->prefix . $k, $key)
: $this->prefix . $key;
return (bool)$this->redis?->del($keys);
} catch (\Throwable) {
return false;
}
}
public function exists(string $key): bool
{
try {
return (bool)$this->redis?->exists($this->prefix . $key);
} catch (\Throwable) {
return false;
}
}
/**
* 序列化处理
*/
private function serialize(mixed $data): string
{
if (is_array($data)) {
return json_encode($data, JSON_UNESCAPED_UNICODE) ?: '';
}
return (string)$data;
}
private function unserialize(mixed $data): mixed
{
if (!is_string($data)) {
return $data;
}
$decoded = json_decode($data, true);
return json_last_error() === JSON_ERROR_NONE ? $decoded : $data;
}
private function __clone() {}
}