├── .gitignore ├── .travis-ci ├── etc │ ├── redis.6379.conf │ ├── redis.6380.conf │ ├── redis.6381.conf │ ├── redis.6382.conf │ ├── sentinel.26379.conf │ ├── sentinel.26380.conf │ └── sentinel.26381.conf ├── init.sh └── redis_install.sh ├── .travis.yml ├── README.md ├── composer.json ├── examples ├── pool.php └── simple.php ├── phpunit.xml ├── src ├── Sentinel.php ├── SentinelClientNotConnectException.php └── SentinelPool.php └── tests ├── SentinelPoolTest.php ├── SentinelTest.php └── SentinelTimeoutTest.php /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | -------------------------------------------------------------------------------- /.travis-ci/etc/redis.6379.conf: -------------------------------------------------------------------------------- 1 | daemonize yes 2 | pidfile "./data/redis/6379/redis.pid" 3 | port 6379 4 | #maxmemory 5 | timeout 0 6 | 7 | loglevel notice 8 | logfile "./data/redis/6379/redis.log" 9 | databases 16 10 | hz 10 11 | 12 | save 900 1 13 | save 300 10 14 | save 60 10000 15 | stop-writes-on-bgsave-error yes 16 | rdbcompression yes 17 | rdbchecksum yes 18 | dbfilename redis.rdb 19 | dir ./data/redis/6379/ 20 | 21 | appendonly no 22 | appendfilename "appendonly.aof" 23 | appendfsync everysec 24 | no-appendfsync-on-rewrite no 25 | auto-aof-rewrite-percentage 100 26 | auto-aof-rewrite-min-size 64mb 27 | aof-load-truncated no 28 | aof-rewrite-incremental-fsync yes 29 | 30 | lua-time-limit 5000 31 | slowlog-log-slower-than 10000 32 | slowlog-max-len 128 33 | 34 | hash-max-ziplist-entries 512 35 | hash-max-ziplist-value 64 36 | list-max-ziplist-entries 512 37 | list-max-ziplist-value 64 38 | set-max-intset-entries 512 39 | zset-max-ziplist-entries 128 40 | zset-max-ziplist-value 64 41 | hll-sparse-max-bytes 3000 42 | activerehashing yes 43 | client-output-buffer-limit normal 0 0 0 44 | client-output-buffer-limit slave 256mb 64mb 60 45 | client-output-buffer-limit pubsub 32mb 8mb 60 -------------------------------------------------------------------------------- /.travis-ci/etc/redis.6380.conf: -------------------------------------------------------------------------------- 1 | daemonize yes 2 | pidfile "./data/redis/6380/redis.pid" 3 | port 6380 4 | #maxmemory 5 | timeout 0 6 | 7 | loglevel notice 8 | logfile "./data/redis/6380/redis.log" 9 | databases 16 10 | hz 10 11 | 12 | save 900 1 13 | save 300 10 14 | save 60 10000 15 | stop-writes-on-bgsave-error yes 16 | rdbcompression yes 17 | rdbchecksum yes 18 | dbfilename redis.rdb 19 | dir ./data/redis/6380/ 20 | 21 | appendonly no 22 | appendfilename "appendonly.aof" 23 | appendfsync everysec 24 | no-appendfsync-on-rewrite no 25 | auto-aof-rewrite-percentage 100 26 | auto-aof-rewrite-min-size 64mb 27 | aof-load-truncated no 28 | aof-rewrite-incremental-fsync yes 29 | 30 | lua-time-limit 5000 31 | slowlog-log-slower-than 10000 32 | slowlog-max-len 128 33 | 34 | hash-max-ziplist-entries 512 35 | hash-max-ziplist-value 64 36 | list-max-ziplist-entries 512 37 | list-max-ziplist-value 64 38 | set-max-intset-entries 512 39 | zset-max-ziplist-entries 128 40 | zset-max-ziplist-value 64 41 | hll-sparse-max-bytes 3000 42 | activerehashing yes 43 | client-output-buffer-limit normal 0 0 0 44 | client-output-buffer-limit slave 256mb 64mb 60 45 | client-output-buffer-limit pubsub 32mb 8mb 60 46 | 47 | slaveof 127.0.0.1 6379 48 | slave-read-only yes -------------------------------------------------------------------------------- /.travis-ci/etc/redis.6381.conf: -------------------------------------------------------------------------------- 1 | daemonize yes 2 | pidfile "./data/redis/6381/redis.pid" 3 | port 6381 4 | #maxmemory 5 | timeout 0 6 | 7 | loglevel notice 8 | logfile "./data/redis/6381/redis.log" 9 | databases 16 10 | hz 10 11 | 12 | save 900 1 13 | save 300 10 14 | save 60 10000 15 | stop-writes-on-bgsave-error yes 16 | rdbcompression yes 17 | rdbchecksum yes 18 | dbfilename redis.rdb 19 | dir ./data/redis/6381/ 20 | 21 | appendonly no 22 | appendfilename "appendonly.aof" 23 | appendfsync everysec 24 | no-appendfsync-on-rewrite no 25 | auto-aof-rewrite-percentage 100 26 | auto-aof-rewrite-min-size 64mb 27 | aof-load-truncated no 28 | aof-rewrite-incremental-fsync yes 29 | 30 | lua-time-limit 5000 31 | slowlog-log-slower-than 10000 32 | slowlog-max-len 128 33 | 34 | hash-max-ziplist-entries 512 35 | hash-max-ziplist-value 64 36 | list-max-ziplist-entries 512 37 | list-max-ziplist-value 64 38 | set-max-intset-entries 512 39 | zset-max-ziplist-entries 128 40 | zset-max-ziplist-value 64 41 | hll-sparse-max-bytes 3000 42 | activerehashing yes 43 | client-output-buffer-limit normal 0 0 0 44 | client-output-buffer-limit slave 256mb 64mb 60 45 | client-output-buffer-limit pubsub 32mb 8mb 60 46 | 47 | slaveof 127.0.0.1 6379 48 | slave-read-only yes -------------------------------------------------------------------------------- /.travis-ci/etc/redis.6382.conf: -------------------------------------------------------------------------------- 1 | daemonize yes 2 | pidfile "./data/redis/6382/redis.pid" 3 | port 6382 4 | #maxmemory 5 | timeout 0 6 | 7 | loglevel notice 8 | logfile "./data/redis/6382/redis.log" 9 | databases 16 10 | hz 10 11 | 12 | save 900 1 13 | save 300 10 14 | save 60 10000 15 | stop-writes-on-bgsave-error yes 16 | rdbcompression yes 17 | rdbchecksum yes 18 | dbfilename redis.rdb 19 | dir ./data/redis/6382/ 20 | 21 | appendonly no 22 | appendfilename "appendonly.aof" 23 | appendfsync everysec 24 | no-appendfsync-on-rewrite no 25 | auto-aof-rewrite-percentage 100 26 | auto-aof-rewrite-min-size 64mb 27 | aof-load-truncated no 28 | aof-rewrite-incremental-fsync yes 29 | 30 | lua-time-limit 5000 31 | slowlog-log-slower-than 10000 32 | slowlog-max-len 128 33 | 34 | hash-max-ziplist-entries 512 35 | hash-max-ziplist-value 64 36 | list-max-ziplist-entries 512 37 | list-max-ziplist-value 64 38 | set-max-intset-entries 512 39 | zset-max-ziplist-entries 128 40 | zset-max-ziplist-value 64 41 | hll-sparse-max-bytes 3000 42 | activerehashing yes 43 | client-output-buffer-limit normal 0 0 0 44 | client-output-buffer-limit slave 256mb 64mb 60 45 | client-output-buffer-limit pubsub 32mb 8mb 60 46 | 47 | slaveof 127.0.0.1 6379 48 | slave-read-only yes -------------------------------------------------------------------------------- /.travis-ci/etc/sentinel.26379.conf: -------------------------------------------------------------------------------- 1 | port 26379 2 | daemonize yes 3 | dir ./data/sentinel/26379 4 | sentinel monitor mymaster 127.0.0.1 6379 2 5 | sentinel down-after-milliseconds mymaster 5000 6 | sentinel parallel-syncs mymaster 1 7 | sentinel failover-timeout mymaster 180000 -------------------------------------------------------------------------------- /.travis-ci/etc/sentinel.26380.conf: -------------------------------------------------------------------------------- 1 | port 26380 2 | daemonize yes 3 | dir ./data/sentinel/26380 4 | sentinel monitor mymaster 127.0.0.1 6379 2 5 | sentinel down-after-milliseconds mymaster 5000 6 | sentinel parallel-syncs mymaster 1 7 | sentinel failover-timeout mymaster 180000 -------------------------------------------------------------------------------- /.travis-ci/etc/sentinel.26381.conf: -------------------------------------------------------------------------------- 1 | port 26381 2 | daemonize yes 3 | dir ./data/sentinel/26381 4 | sentinel monitor mymaster 127.0.0.1 6379 2 5 | sentinel down-after-milliseconds mymaster 5000 6 | sentinel parallel-syncs mymaster 1 7 | sentinel failover-timeout mymaster 180000 -------------------------------------------------------------------------------- /.travis-ci/init.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | sh .travis-ci/redis_install.sh 3 | mkdir -p ./data/redis/6379 4 | mkdir -p ./data/redis/6380 5 | mkdir -p ./data/redis/6381 6 | mkdir -p ./data/sentinel/26379 7 | mkdir -p ./data/sentinel/26380 8 | mkdir -p ./data/sentinel/26381 9 | mkdir -p ./data/sentinel/26382 10 | 11 | 12 | ./redis/bin/redis-server .travis-ci/etc/redis.6379.conf 13 | ./redis/bin/redis-server .travis-ci/etc/redis.6380.conf 14 | ./redis/bin/redis-server .travis-ci/etc/redis.6381.conf 15 | ./redis/bin/redis-server .travis-ci/etc/redis.6382.conf 16 | 17 | ./redis/bin/redis-sentinel .travis-ci/etc/sentinel.26379.conf 18 | ./redis/bin/redis-sentinel .travis-ci/etc/sentinel.26380.conf 19 | ./redis/bin/redis-sentinel .travis-ci/etc/sentinel.26381.conf 20 | 21 | 22 | ss -l 23 | -------------------------------------------------------------------------------- /.travis-ci/redis_install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | SOURCE=https://github.com/antirez/redis/archive/3.2.6.tar.gz 3 | 4 | if [ ! -f redis-server.tar.gz ]; then 5 | wget $SOURCE -O redis-server.tar.gz 6 | fi 7 | 8 | tar zxvf redis-server.tar.gz 9 | cd redis-3.2.6 10 | PREFIX=../redis 11 | make && make install 12 | mkdir $PREFIX 13 | mkdir $PREFIX/bin 14 | mkdir $PREFIX/etc 15 | mkdir $PREFIX/var 16 | mkdir $PREFIX/var/log 17 | mkdir $PREFIX/var/data 18 | cp redis.conf $PREFIX/etc/ 19 | cp sentinel.conf $PREFIX/etc/ 20 | find src/ -type f -executable -exec cp {} $PREFIX/bin \; 21 | 22 | echo "install redis success" 23 | rm redis-server.tar.gz -rf 24 | 25 | cd .. 26 | 27 | 28 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 5.4 5 | - 5.5 6 | - 5.6 7 | 8 | before_script: 9 | - pecl install -f redis-2.2.8 10 | - sh .travis-ci/init.sh 11 | - composer self-update 12 | - composer update 13 | - php -i | grep redis 14 | 15 | script: 16 | - sleep 10 && phpunit -v --coverage-text --coverage-clover=coverage.clover 17 | 18 | after_script: 19 | - wget https://scrutinizer-ci.com/ocular.phar 20 | - php ocular.phar code-coverage:upload --format=php-clover coverage.clover 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # redis-sentinel 2 | [![Latest Stable Version](https://poser.pugx.org/jenner/redis_sentinel/v/stable)](https://packagist.org/packages/jenner/simple_fork) 3 | [![Total Downloads](https://poser.pugx.org/jenner/redis_sentinel/downloads)](https://packagist.org/packages/jenner/simple_fork) 4 | [![Latest Unstable Version](https://poser.pugx.org/jenner/redis_sentinel/v/unstable)](https://packagist.org/packages/jenner/simple_fork) 5 | [![License](https://poser.pugx.org/jenner/redis_sentinel/license)](https://packagist.org/packages/jenner/simple_fork) 6 | [![travis](https://travis-ci.org/huyanping/redis-sentinel.svg)](https://travis-ci.org/huyanping/simple-fork-php) 7 | 8 | redis-sentinel client for php based on phpredis extension. 9 | 10 | ## examples 11 | Get Redis master address and create Redis object: 12 | ```php 13 | $sentinel = new \Jenner\RedisSentinel\Sentinel(); 14 | $sentinel->connect('127.0.0.1', 6379); 15 | $address = $sentinel->getMasterAddrByName('mymaster'); 16 | 17 | $redis = new Redis(); 18 | $redis->connect($address['ip'], $address['port']); 19 | $info = $redis->info(); 20 | print_r($info); 21 | ``` 22 | 23 | Create redis-sentinel pool and create Redis object: 24 | ```php 25 | $sentinel_pool = new \Jenner\RedisSentinel\SentinelPool(); 26 | $sentinel_pool->addSentinel('127.0.0.1', 26379); 27 | $sentinel_pool->addSentinel('127.0.0.1', 26380); 28 | 29 | $address = $sentinel_pool->master('mymaster'); 30 | print_r($address); 31 | 32 | $redis = $sentinel_pool->getRedis('mymaster'); 33 | $info = $redis->info(); 34 | print_r($info); 35 | ``` 36 | 37 | In order to prevent redis/sentinel to wait too long for connections in case of 38 | issues with the Redis backend it's advisable to use a timeout (in seconds): 39 | 40 | ```php 41 | $sentinel_pool->addSentinel('127.0.0.1', 26380, 1.0); # 1 second timeout 42 | ``` -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jenner/redis_sentinel", 3 | "description": "redis sentinel client for php 5.3+", 4 | "keywords": [ 5 | "sentinel", 6 | "redis sentinel", 7 | "sentinel client", 8 | "redis sentinel client" 9 | ], 10 | "authors": [ 11 | { 12 | "name": "Jenner", 13 | "email": "hypxm@qq.com", 14 | "homepage": "http://www.huyanping.cn" 15 | } 16 | ], 17 | "license": "MIT", 18 | "type": "package", 19 | "require": { 20 | "php": ">=5.3.0", 21 | "ext-redis": ">=2.2.8" 22 | }, 23 | "autoload": { 24 | "psr-4": { 25 | "Jenner\\RedisSentinel\\": "./src", 26 | "Jenner\\RedisSentinel\\Test\\": "./tests" 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /examples/pool.php: -------------------------------------------------------------------------------- 1 | addSentinel('127.0.0.1', 26379); 15 | $sentinel_pool->addSentinel('127.0.0.1', 26380); 16 | 17 | $redis = $sentinel_pool->getRedis('mymaster'); 18 | $info = $redis->info(); 19 | print_r($info); -------------------------------------------------------------------------------- /examples/simple.php: -------------------------------------------------------------------------------- 1 | connect('127.0.0.1', 6379); 15 | $address = $sentinel->getMasterAddrByName('mymaster'); 16 | 17 | $redis = new Redis(); 18 | $redis->connect($address['ip'], $address['port']); 19 | $info = $redis->info(); 20 | print_r($info); -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | tests 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/Sentinel.php: -------------------------------------------------------------------------------- 1 | redis = new \Redis(); 26 | $this->timeout = $timeout === null ? 0.0 : $timeout; 27 | } 28 | 29 | public function __destruct() 30 | { 31 | try { 32 | $this->redis->close(); 33 | } catch (\Exception $e) { 34 | } 35 | } 36 | 37 | /** 38 | * @param $host 39 | * @param int $port 40 | * @param float $timeout connect timeout in seconds 41 | * @return boolean 42 | */ 43 | public function connect($host, $port = 26379, $timeout = null) 44 | { 45 | if (!$this->redis->connect($host, $port, $timeout === null ? $this->timeout : $timeout)) { 46 | return false; 47 | } 48 | 49 | return true; 50 | } 51 | 52 | /** 53 | * This command simply returns PONG. 54 | * 55 | * @return string STRING: +PONG on success. Throws a RedisException object on connectivity error. 56 | */ 57 | public function ping() 58 | { 59 | return $this->redis->ping(); 60 | } 61 | 62 | /** 63 | * Show a list of monitored masters and their state. 64 | * 65 | * @return array 66 | */ 67 | public function masters() 68 | { 69 | return $this->parseArrayResult($this->redis->rawCommand('SENTINEL', 'masters')); 70 | } 71 | 72 | /** 73 | * parse redis response data 74 | * 75 | * @param array $data 76 | * @return array 77 | */ 78 | private function parseArrayResult(array $data) 79 | { 80 | $result = array(); 81 | $count = count($data); 82 | for ($i = 0; $i < $count;) { 83 | $record = $data[$i]; 84 | if (is_array($record)) { 85 | $result[] = $this->parseArrayResult($record); 86 | $i++; 87 | } else { 88 | $result[$record] = $data[$i + 1]; 89 | $i += 2; 90 | } 91 | } 92 | 93 | return $result; 94 | } 95 | 96 | /** 97 | * Show the state and info of the specified master. 98 | * 99 | * @param string $master_name 100 | * @return array 101 | */ 102 | public function master($master_name) 103 | { 104 | return $this->parseArrayResult($this->redis->rawCommand('SENTINEL', 'master', $master_name)); 105 | } 106 | 107 | /** 108 | * Show a list of slaves for this master, and their state. 109 | * 110 | * @param string $master_name 111 | * @return array 112 | */ 113 | public function slaves($master_name) 114 | { 115 | return $this->parseArrayResult($this->redis->rawCommand('SENTINEL', 'slaves', $master_name)); 116 | } 117 | 118 | /** 119 | * Show a list of sentinel instances for this master, and their state. 120 | * 121 | * @param string $master_name 122 | * @return array 123 | */ 124 | public function sentinels($master_name) 125 | { 126 | return $this->parseArrayResult($this->redis->rawCommand('SENTINEL', 'sentinels', $master_name)); 127 | } 128 | 129 | /** 130 | * Return the ip and port number of the master with that name. 131 | * If a failover is in progress or terminated successfully 132 | * for this master it returns the address and port of the promoted slave. 133 | * 134 | * @param string $master_name 135 | * @return array 136 | */ 137 | public function getMasterAddrByName($master_name) 138 | { 139 | $data = $this->redis->rawCommand('SENTINEL', 'get-master-addr-by-name', $master_name); 140 | return array( 141 | 'ip' => $data[0], 142 | 'port' => $data[1] 143 | ); 144 | } 145 | 146 | /** 147 | * This command will reset all the masters with matching name. 148 | * The pattern argument is a glob-style pattern. 149 | * The reset process clears any previous state in a master 150 | * (including a failover in progress), and removes every slave 151 | * and sentinel already discovered and associated with the master. 152 | * 153 | * @param string $pattern 154 | * @return int 155 | */ 156 | public function reset($pattern) 157 | { 158 | return $this->redis->rawCommand('SENTINEL', 'reset', $pattern); 159 | } 160 | 161 | /** 162 | * Force a failover as if the master was not reachable, 163 | * and without asking for agreement to other Sentinels 164 | * (however a new version of the configuration will be published 165 | * so that the other Sentinels will update their configurations). 166 | * 167 | * @param string $master_name 168 | * @return boolean 169 | */ 170 | public function failOver($master_name) 171 | { 172 | return $this->redis->rawCommand('SENTINEL', 'failover', $master_name) === 'OK'; 173 | } 174 | 175 | /** 176 | * @param string $master_name 177 | * @return boolean 178 | */ 179 | public function ckquorum($master_name) 180 | { 181 | return $this->checkQuorum($master_name); 182 | } 183 | 184 | /** 185 | * Check if the current Sentinel configuration is able to 186 | * reach the quorum needed to failover a master, and the majority 187 | * needed to authorize the failover. This command should be 188 | * used in monitoring systems to check if a Sentinel deployment is ok. 189 | * 190 | * @param string $master_name 191 | * @return boolean 192 | */ 193 | public function checkQuorum($master_name) 194 | { 195 | return $this->redis->rawCommand('SENTINEL', 'ckquorum', $master_name); 196 | } 197 | 198 | /** 199 | * Force Sentinel to rewrite its configuration on disk, 200 | * including the current Sentinel state. Normally Sentinel rewrites 201 | * the configuration every time something changes in its state 202 | * (in the context of the subset of the state which is persisted on disk across restart). 203 | * However sometimes it is possible that the configuration file is lost because of 204 | * operation errors, disk failures, package upgrade scripts or configuration managers. 205 | * In those cases a way to to force Sentinel to rewrite the configuration file is handy. 206 | * This command works even if the previous configuration file is completely missing. 207 | * 208 | * @return boolean 209 | */ 210 | public function flushConfig() 211 | { 212 | return $this->redis->rawCommand('SENTINEL', 'flushconfig'); 213 | } 214 | 215 | /** 216 | * This command tells the Sentinel to start monitoring a new master with the specified name, 217 | * ip, port, and quorum. It is identical to the sentinel monitor configuration directive 218 | * in sentinel.conf configuration file, with the difference that you can't use an hostname in as ip, 219 | * but you need to provide an IPv4 or IPv6 address. 220 | * 221 | * @param $master_name 222 | * @param $ip 223 | * @param $port 224 | * @param $quorum 225 | * @return boolean 226 | */ 227 | public function monitor($master_name, $ip, $port, $quorum) 228 | { 229 | return $this->redis->rawCommand('SENTINEL', 'monitor', $master_name, $ip, $port, $quorum); 230 | } 231 | 232 | /** 233 | * is used in order to remove the specified master: the master will no longer be monitored, 234 | * and will totally be removed from the internal state of the Sentinel, 235 | * so it will no longer listed by SENTINEL masters and so forth. 236 | * 237 | * @param $master_name 238 | * @return boolean 239 | */ 240 | public function remove($master_name) 241 | { 242 | return $this->redis->rawCommand('SENTINEL', 'remove', $master_name); 243 | } 244 | 245 | /** 246 | * The SET command is very similar to the CONFIG SET command of Redis, 247 | * and is used in order to change configuration parameters of a specific master. 248 | * Multiple option / value pairs can be specified (or none at all). 249 | * All the configuration parameters that can be configured via sentinel.conf 250 | * are also configurable using the SET command. 251 | * 252 | * @param $master_name 253 | * @param $option 254 | * @param $value 255 | * @return boolean 256 | */ 257 | public function set($master_name, $option, $value) 258 | { 259 | return $this->redis->rawCommand('SENTINEL', 'set', $master_name, $option, $value); 260 | } 261 | 262 | /** 263 | * get last error 264 | * 265 | * @return string 266 | */ 267 | public function getLastError() 268 | { 269 | return $this->redis->getLastError(); 270 | } 271 | 272 | /** 273 | * clear last error 274 | * 275 | * @return boolean 276 | */ 277 | public function clearLastError() 278 | { 279 | return $this->redis->clearLastError(); 280 | } 281 | 282 | /** 283 | * sentinel server info 284 | * 285 | * @return string 286 | */ 287 | public function info() 288 | { 289 | return $this->redis->info(); 290 | } 291 | 292 | /** 293 | * @return float 294 | */ 295 | public function getTimeout() 296 | { 297 | return $this->timeout; 298 | } 299 | 300 | } 301 | -------------------------------------------------------------------------------- /src/SentinelClientNotConnectException.php: -------------------------------------------------------------------------------- 1 | 'host', 'port'=>'port']] 46 | */ 47 | public function __construct(array $sentinels = array()) 48 | { 49 | foreach ($sentinels as $sentinel) { 50 | $this->addSentinel($sentinel['host'], $sentinel['port']); 51 | } 52 | } 53 | 54 | /** 55 | * add sentinel to sentinel pool 56 | * 57 | * @param string $host sentinel server host 58 | * @param int $port sentinel server port 59 | * @param null|float $timeout connect timeout in seconds 60 | * @return bool 61 | */ 62 | public function addSentinel($host, $port, $timeout = null) 63 | { 64 | $sentinel = new Sentinel($timeout); 65 | // if connect to sentinel successfully, add it to sentinels array 66 | if ($sentinel->connect($host, $port, $timeout)) { 67 | $this->sentinels[] = $sentinel; 68 | return true; 69 | } 70 | 71 | return false; 72 | } 73 | 74 | /** 75 | * get Redis object by master name 76 | * 77 | * @param $master_name 78 | * @return \Redis 79 | * @throws \RedisException 80 | */ 81 | public function getRedis($master_name) 82 | { 83 | $address = $this->getMasterAddrByName($master_name); 84 | $redis = new \Redis(); 85 | if (!$redis->connect($address['ip'], $address['port'], $this->currentSentinel->getTimeout())) { 86 | throw new \RedisException("connect to redis failed"); 87 | } 88 | 89 | return $redis; 90 | } 91 | 92 | public function __call($name, $arguments) 93 | { 94 | foreach ($this->sentinels as $sentinel) { 95 | if (!method_exists($sentinel, $name)) { 96 | throw new \BadMethodCallException("method not exists. method: {$name}"); 97 | } 98 | try { 99 | $this->currentSentinel = $sentinel; 100 | return call_user_func_array(array($sentinel, $name), $arguments); 101 | } catch (\Exception $e) { 102 | continue; 103 | } 104 | } 105 | 106 | throw new SentinelClientNotConnectException("all sentinel failed"); 107 | } 108 | 109 | /** 110 | * @return Sentinel 111 | */ 112 | public function getCurrentSentinel() 113 | { 114 | return $this->currentSentinel; 115 | } 116 | } -------------------------------------------------------------------------------- /tests/SentinelPoolTest.php: -------------------------------------------------------------------------------- 1 | sentinel_pool = new SentinelPool(); 25 | $this->sentinel_pool->addSentinel('127.0.0.1', 26379); 26 | $this->sentinel_pool->addSentinel('127.0.0.1', 26380); 27 | $this->sentinel_pool->addSentinel('127.0.0.1', 26381); 28 | } 29 | 30 | public function testAll() 31 | { 32 | $this->assertEquals('+PONG', $this->sentinel_pool->ping()); 33 | $masters = $this->sentinel_pool->masters(); 34 | $this->assertEquals(1, count($masters)); 35 | $this->assertEquals($this->master_name, $masters[0]['name']); 36 | 37 | $master = $this->sentinel_pool->master($this->master_name); 38 | $this->assertEquals($this->master_name, $master['name']); 39 | 40 | $slaves = $this->sentinel_pool->slaves($this->master_name); 41 | $this->assertEquals(2, count($slaves)); 42 | $this->assertEquals('127.0.0.1', $slaves[0]['ip']); 43 | $this->assertTrue(in_array($slaves[0]['port'], array('6380', '6381'))); 44 | 45 | $sentinels = $this->sentinel_pool->sentinels($this->master_name); 46 | $this->assertEquals(2, count($sentinels)); 47 | $this->assertEquals('127.0.0.1', $sentinels[0]['ip']); 48 | $this->assertTrue(in_array($sentinels[0]['port'], array('26380', '26381'))); 49 | 50 | $address = $this->sentinel_pool->getMasterAddrByName($this->master_name); 51 | $this->assertEquals('127.0.0.1', $address['ip']); 52 | $this->assertEquals(6379, $address['port']); 53 | 54 | $this->assertTrue($this->sentinel_pool->flushConfig()); 55 | $this->assertTrue($this->sentinel_pool->checkQuorum($this->master_name)); 56 | $this->assertTrue($this->sentinel_pool->ckquorum($this->master_name)); 57 | $this->assertFalse($this->sentinel_pool->failOver($this->master_name)); 58 | 59 | $redis = $this->sentinel_pool->getRedis($this->master_name); 60 | $this->assertEquals('+PONG', $redis->ping()); 61 | } 62 | } -------------------------------------------------------------------------------- /tests/SentinelTest.php: -------------------------------------------------------------------------------- 1 | sentinel = new Sentinel(); 27 | if (!$this->sentinel->connect('127.0.0.1', 26379)) { 28 | throw new SentinelClientNotConnectException("connect to sentinel server failed"); 29 | } 30 | } 31 | 32 | public function testAll() 33 | { 34 | $this->assertEquals('+PONG', $this->sentinel->ping()); 35 | 36 | $masters = $this->sentinel->masters(); 37 | $this->assertEquals(1, count($masters)); 38 | $this->assertEquals($this->master_name, $masters[0]['name']); 39 | 40 | $master = $this->sentinel->master($this->master_name); 41 | $this->assertEquals($this->master_name, $master['name']); 42 | 43 | $slaves = $this->sentinel->slaves($this->master_name); 44 | $this->assertEquals(2, count($slaves)); 45 | $this->assertEquals('127.0.0.1', $slaves[0]['ip']); 46 | $this->assertTrue(in_array($slaves[0]['port'], array('6380', '6381'))); 47 | 48 | $sentinels = $this->sentinel->sentinels($this->master_name); 49 | $this->assertEquals(2, count($sentinels)); 50 | $this->assertEquals('127.0.0.1', $sentinels[0]['ip']); 51 | $this->assertTrue(in_array($sentinels[0]['port'], array('26380', '26381'))); 52 | 53 | $address = $this->sentinel->getMasterAddrByName($this->master_name); 54 | $this->assertEquals('127.0.0.1', $address['ip']); 55 | $this->assertEquals(6379, $address['port']); 56 | 57 | $this->assertTrue($this->sentinel->flushConfig()); 58 | 59 | $this->assertTrue($this->sentinel->checkQuorum($this->master_name)); 60 | $this->assertTrue($this->sentinel->ckquorum($this->master_name)); 61 | 62 | $this->assertFalse($this->sentinel->failOver($this->master_name)); 63 | 64 | $this->assertTrue($this->sentinel->monitor('add_master', '127.0.0.1', 6382, 2)); 65 | $masters = $this->sentinel->masters(); 66 | $this->assertEquals(2, count($masters)); 67 | $master_names = array(); 68 | foreach ($masters as $master) { 69 | $master_names[] = $master['name']; 70 | } 71 | $this->assertTrue(in_array('add_master', $master_names)); 72 | $this->assertTrue($this->sentinel->set('add_master', 'down-after-milliseconds', 10000)); 73 | $this->assertTrue($this->sentinel->remove('add_master')); 74 | } 75 | } -------------------------------------------------------------------------------- /tests/SentinelTimeoutTest.php: -------------------------------------------------------------------------------- 1 | sentinel_pool = new SentinelPool(); 12 | $this->sentinel_pool->addSentinel('127.0.0.1', 26379, 1.1); 13 | $this->sentinel_pool->addSentinel('127.0.0.1', 26380, 1.2); 14 | $this->sentinel_pool->addSentinel('127.0.0.1', 26381, 1.3); 15 | } 16 | 17 | public function testTimeout() 18 | { 19 | // run command to populate current sentinel 20 | $this->assertEquals('+PONG', $this->sentinel_pool->ping()); 21 | 22 | $this->assertNotNull($this->sentinel_pool->getCurrentSentinel()); 23 | 24 | $this->assertEquals(1.1, $this->sentinel_pool->getCurrentSentinel()->getTimeout()); 25 | } 26 | } --------------------------------------------------------------------------------