Last active
November 14, 2017 21:59
-
-
Save emsearcy/a5205d06cc94bdd8c1b02f037f9632fa to your computer and use it in GitHub Desktop.
Simple PHP Redis Sentinel master request
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/* | |
* Parse RESP (REdis Serialization Protocol) | |
*/ | |
function read_resp($fp = null) { | |
// File descripter to the Redis server connection | |
// (static to limit recursion stack size) | |
static $conn = null; | |
if (!$conn) $conn = $fp; | |
// The max size of a Redis string data type is 512MB | |
$type = fgets($conn, 524290); | |
if ($type === FALSE) { | |
error_log('RESP unexpected stream error reading type'); | |
return false; | |
} | |
// First byte of type string identifies data type | |
switch (substr($type, 0, 1)) { | |
// Array | |
case '*': | |
// Remainder of type string is array item count | |
$count = (int) substr($type, 1); | |
// -1 sized array is a way of representing nil object | |
if ($count === -1) { | |
return null; | |
} | |
$a = array(); | |
for ($i = 0; $i < $count; $i++) { | |
// Recurse to read array value | |
$a[] = read_resp(); | |
} | |
return $a; | |
// Simple String | |
case '+': | |
// Remainder of type string is the string itself | |
return substr($type, 1); | |
// Error | |
case '-': | |
// Remainder of type string is an error message | |
error_log(substr($type, 1)); | |
return false; | |
// Integer | |
case ':': | |
// Remainder of type string is an integer | |
return (int) substr($type, 1); | |
// Bulk String | |
case '$': | |
// Remainder of type string is string length | |
$count = (int) substr($type, 1); | |
// -1 sized string is a way of representing nil object | |
if ($count === -1) { | |
return null; | |
} | |
$s = fread($conn, $count); | |
if ($s === FALSE) { | |
error_log("RESP unable to read bulk string of size $count"); | |
return false; | |
} | |
// consume the final \r\n | |
if (fread($conn, 2) === FALSE) { | |
error_log('RESP termination of bulk string missing'); | |
return false; | |
} | |
return $s; | |
} | |
} | |
// Attempt connection to local Sentinel using very short timeout | |
$fp = @stream_socket_client('tcp://localhost:26379', $errno, $errstr, 0.1); | |
if (!$fp) { | |
error_log("RESP $errstr ($errno)"); | |
header((isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0') . ' 503 Service Unavailable'); | |
exit; | |
} | |
// Set the name of the monitored Redis instance | |
$redis_name = 'CHANGEME'; | |
// Send a get-master-addr-by-name request to the sentinel | |
fwrite($fp, "*3\r\n\$8\r\nSENTINEL\r\n\$23\r\nget-master-addr-by-name\r\n\$" . strlen($redis_name) . "\r\n" . $redis_name . "\r\n"); | |
// Parse the serialized RESP response | |
$r = read_resp($fp); | |
fclose($fp); | |
// Response should be (hostname, port) array | |
if (!is_array($r) || count($r) !== 2) { | |
header((isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0') . ' 503 Service Unavailable'); | |
error_log('RESP unexpected response to get-master-addr-by-name'); | |
exit; | |
} | |
// HOST TO PASS TO REDIS CLIENT IS $r[0] | |
// PORT TO PASS TO REDIS CLIENT IS $r[1] | |
// DEBUG | |
var_dump($r); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment