RESP: The Redis wire format
Once your server speaks RESP, every Redis client works against it. redis-cli, redis-py, the Go and Java drivers. The compatibility comes from one tiny grammar.
RESP grammar
wire
text
*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$5\r\nhello\r\n
*3 array of 3
$3 SET bulk string of length 3, "SET"
$3 foo bulk string of length 3, "foo"
$5 hello bulk string of length 5, "hello"What 'SET foo hello' actually looks like on the wire. Three bulk strings inside an array.
def parse_resp(buf):
if not buf: return None
type_byte = buf[0:1]
end = buf.find(CRLF)
if end < 0: return None
header = buf[1:end].decode("ascii")
after = end + 2
if type_byte == b"+": return header, after
if type_byte == b"-": return Exception(header), after
if type_byte == b":": return int(header), after
if type_byte == b"$":
n = int(header)
if n == -1: return None, after
end_payload = after + n
if len(buf) < end_payload + 2: return None
return buf[after:end_payload].decode("utf-8"), end_payload + 2
if type_byte == b"*":
count = int(header)
if count == -1: return None, after
items, cursor = [], after
for _ in range(count):
r = parse_resp(buf[cursor:])
if r is None: return None
v, c = r
items.append(v); cursor += c
return items, cursorThe whole parser. Recursive for arrays. Returns None when the buffer is incomplete so the read loop knows to wait for more bytes.
TCP gives you a byte stream, not messages. recv() might return half a frame. The parser must handle that. We return None when the buffer is incomplete. The main loop reads more bytes and retries. Same pattern shows up in every networked system.
Ordering exercise: Order the steps to parse one RESP frame
Loading practice…
Quiz: Quiz
Loading practice…