265 lines
6.8 KiB
PHP
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() {}
|
|
}
|