Skip to content

Instantly share code, notes, and snippets.

@emsearcy
Last active November 14, 2017 21:59
Show Gist options
  • Save emsearcy/a5205d06cc94bdd8c1b02f037f9632fa to your computer and use it in GitHub Desktop.
Save emsearcy/a5205d06cc94bdd8c1b02f037f9632fa to your computer and use it in GitHub Desktop.
Simple PHP Redis Sentinel master request
<?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