【解…決…？】php-resqueで、redisサーバ側のダウン後復帰するには？

php-resque超便利。
[こちら](http://qiita.com/tototoshi/items/0e77b7bfa19f51ad52d0)の記事等を元に設定して、redisが受けてくれたキューをWorkerでガンガンイベントドリブンで処理できるんだけど、一つ困った点が…。

Workerを立ち上げている最中に接続先のredisサーバが落ちれば、サーバ側が復帰しても再接続してくれません。
Workerは受け取った処理に応じた処理クラスをプラグイン的に追加して、全体の起動を司るデーモンは標準スクリプトの

```
./vendor/bin/resque
```

を実行させる仕組みなので、エラーを検知したら再接続させるということができません。
というか接続エラーを検知する事すら至難の業…（エラーログを吐かせて、別プロセスから監視とか？）

もちろん、オープンソースなので ./vendor/bin/resque に手を入れてしまうというのも手なのですが、そこまでやるのは「拳銃は最後の武器だ」的なところで…。
よくあるユースケースと思うので、なにか見落としてるかも知れず…ご存知の方、おられませんでしょうか…。

【追記18:17】
結局最後の武器を使ってしまいました。
[lib/Resque/Redis.php](https://github.com/chrisboulton/php-resque/blob/master/lib/Resque/Redis.php)を変更(飲み会迫ってて時間ないのでdiffとか後で取ります)

```php:lib/Resque/Redis.php
<?php
/**
 * Wrap Credis to add namespace support and various helper methods.
 *
 * @package		Resque/Redis
 * @author		Chris Boulton <chris@bigcommerce.com>
 * @license		http://www.opensource.org/licenses/mit-license.php
 */
class Resque_Redis
{
    /**
     * Redis namespace
     * @var string
     */
    private static $defaultNamespace = 'resque:';

    private $server;
    private $database;
    private $_driver = null; // Add by kochizufan

	/**
	 * @var array List of all commands in Redis that supply a key as their
	 *	first argument. Used to prefix keys with the Resque namespace.
	 */
	private $keyCommands = array(
		'exists',
		'del',
		'type',
		'keys',
		'expire',
		'ttl',
		'move',
		'set',
		'setex',
		'get',
		'getset',
		'setnx',
		'incr',
		'incrby',
		'decr',
		'decrby',
		'rpush',
		'lpush',
		'llen',
		'lrange',
		'ltrim',
		'lindex',
		'lset',
		'lrem',
		'lpop',
		'blpop',
		'rpop',
		'sadd',
		'srem',
		'spop',
		'scard',
		'sismember',
		'smembers',
		'srandmember',
		'zadd',
		'zrem',
		'zrange',
		'zrevrange',
		'zrangebyscore',
		'zcard',
		'zscore',
		'zremrangebyscore',
		'sort'
	);
	// sinterstore
	// sunion
	// sunionstore
	// sdiff
	// sdiffstore
	// sinter
	// smove
	// rename
	// rpoplpush
	// mget
	// msetnx
	// mset
	// renamenx

	/**
	 * Set Redis namespace (prefix) default: resque
	 * @param string $namespace
	 */
	public static function prefix($namespace)
	{
	    if (strpos($namespace, ':') === false) {
	        $namespace .= ':';
	    }
	    self::$defaultNamespace = $namespace;
	}

	public function __construct($server, $database = null)
	{
		$this->server = $server;
		$this->database = $database;

        	//Delete by kochizufan : lazy creating driver object
	}

    //Add by kochizufan
    //Creating driver object if it is noy initialized yet.
    //If error occured (redis server is not ready), driver object keep null.
    private function driver() {
        if ($this->_driver == null) {

            if (is_array($this->server)) {
                try {
                    $this->_driver = new Credis_Cluster($this->server);
                } catch (\Exception $e) {
                    $this->_driver = null;
                }
            }
            else {
                $port = null;
                $password = null;
                $host = $this->server;

                // If not a UNIX socket path or tcp:// formatted connections string
                // assume host:port combination.
                if (strpos($this->server, '/') === false) {
                    $parts = explode(':', $this->server);
                    if (isset($parts[1])) {
                        $port = $parts[1];
                    }
                    $host = $parts[0];
                }else if (strpos($this->server, 'redis://') !== false){
                    // Redis format is:
                    // redis://[user]:[password]@[host]:[port]
                    list($userpwd,$hostport) = explode('@', $this->server);
                    $userpwd = substr($userpwd, strpos($userpwd, 'redis://')+8);
                    list($host, $port) = explode(':', $hostport);
                    list($user, $password) = explode(':', $userpwd);
                }

                try {
                    $this->_driver = new Credis_Client($host, $port);
                    if (isset($password)){
                        $this->_driver->auth($password);
                    }
                } catch (\Exception $e) {
                    $this->_driver = null;
                }
            }

            if ($this->_driver !== null && $this->database !== null) {
                $this->_driver->select($this->database);
            }
        }

        return $this->_driver;
    }

	/**
	 * Magic method to handle all function requests and prefix key based
	 * operations with the {self::$defaultNamespace} key prefix.
	 *
	 * @param string $name The name of the method called.
	 * @param array $args Array of supplied arguments to the method.
	 * @return mixed Return value from Resident::call() based on the command.
	 */
	public function __call($name, $args) {
		if(in_array($name, $this->keyCommands)) {
            if(is_array($args[0])) {
                foreach($args[0] AS $i => $v) {
                    $args[0][$i] = self::$defaultNamespace . $v;
                }
            } else {
                $args[0] = self::$defaultNamespace . $args[0];
            }
		}

        //Modified by kochizufan
		try {
            // if driver is not initialized yet, return false.
            $driver = $this->driver();
            if ($driver === null) return false;

			$ret = $driver->__call($name, $args);

            //if method name is "blpop" and return value is not array, driver should be re-initializing.
            if ($name === 'blpop' && !is_array($ret)) {
                $this->_driver = null;
                return false;
            }

            return $ret;
		}
		catch(CredisException $e) {

            //if exception occured, driver should be re-initializing.
            $this->_driver = null;
			return false;
		}
	}

    public static function getPrefix()
    {
        return self::$defaultNamespace;
    }

    public static function removePrefix($string)
    {
        $prefix=self::getPrefix();

        if (substr($string, 0, strlen($prefix)) == $prefix) {
            $string = substr($string, strlen($prefix), strlen($string) );
        }
        return $string;
    }
}
?>
```

起動時にredisサーバが落ちていたらエラーで起動しない、途中でredisサーバが落ちたらデーモンは落ちはしないけど二度と復帰しない、という状況だったので、DBドライバを遅延接続にして、エラーが出る限りはつなぎ直しにいく、エラーが出だしたらまたつなぎ直しにいく、というふうに変更しました。
とりあえず最低限の変更で対応したので、執拗的な再接続を繰り返しますし、個人的要件のためだけに作ったのでこのコードだとblpopでの処理しか扱えませんが、参考になれば…。