<?php

namespace xmvc\daemon\lib;

use xmvc\daemon\lib\BaseDaemon;
use Redis;

/**
 * Redis 守护进程类
 */
class RedisDaemon extends BaseDaemon
{
    protected array $options;
    protected ?Redis $redis = null;
    public function __construct(array $options)
    {
        $this->options = $options;
    }

    /**
     * 清理资源
     */
    protected function cleanup()
    {
        $this->closeRedis();
        parent::cleanup();
    }

    /** 连接 Redis */
    protected function connectRedis()
    {
        if ($this->redis) {
            return;
        }
        try {
            $this->redis = new Redis();
            $this->redis->connect($this->options['host'], $this->options['port']);
            if (isset($this->options['password']) && $this->options['password'] != '') {
                $this->redis->auth($this->options['password']);
            }
        } catch (\Throwable $th) {
            self::error('Redis ' . $th->getMessage() . ' ' . $this->options['host'] . ':' . $this->options['port'], __FILE__, __LINE__);
        }
    }

    /** 重连 Redis */
    protected function reconnectRedis()
    {
        $this->closeRedis();
        $this->connectRedis();
    }

    /** 关闭 Redis */
    protected function closeRedis()
    {
        if ($this->redis) {
            $this->redis->close();
        }
    }

    /**
     * 请求停止子进程进程 - 多进程模式下使用
     */
    public static function stopProcess()
    {
        static::$stopRequested = true;
        if (file_exists(static::$forceFilename)) {
            static::log('强制关闭子进程 PID:' . getmypid());
            exit(0);
        } else {
            static::log('优雅关闭子进程 PID:' . getmypid());
        }
    }

    /** 执行 Redis 命令, 并监控执行是否有错误, 有错误则重连 */
    protected function tryRedisCommand($command, $arguments = [])
    {
        $maxRetries = 13; // 尝试重连 13 次, 根据指数退避策略, 13 次大概是在24小时的时间内间断性尝试
        $retryDelay = 1; //等待时间

        for ($retry = 0; $retry < $maxRetries; $retry++) {
            try {
                $result = call_user_func_array([$this->redis, $command], $arguments);
                if ($result === false && $this->redis->getLastError() !== '') {
                    $errormsg = "Redis 命令执行失败: " . $this->redis->getLastError();
                    self::log($errormsg);
                    throw new \Throwable($errormsg, 1);
                }
                return $result;
            } catch (\Throwable $th) {
                self::log($th->getMessage());
                self::log("重连 Redis, 第 " . ($retry + 1) . " / " . $maxRetries . " 次");
                $this->reconnectRedis();
                sleep($retryDelay); // 等待一段时间后重试
                $retryDelay *= 2; // 指数退避策略
            }
        }
        self::error("多次重试后执行Redis命令失败, 服务退出", __FILE__, __LINE__);
        exit();
    }
}
