Redisのプロトコルは次のような要求を満たすものとなっています。
Redisのクライアントは、ポート6379でサーバに接続しに行くことでコネクションを張ります。クライアント・サーバ間でやりとりされる、すべてのRedisコマンドやデータは \r\n (CRLF)で終端されます。
Redisは複数の異なる引き数を含むコマンドを受け取ります。コマンドを受け取ると、それを処理して、クライアント側に返信を返します。
新統一リクエスト・プロトコルは、Redis 1.2から導入され、2.0からはサーバが使う標準的な方法になりました。
統一リクエスト・プロトコルの中では、すべての引き数はバイナリセーフな方法でサーバに送られます。一般的には次のような構成をしています。
*<引き数の数> CR LF
$<引き数1のバイト数> CR LF
<引き数データ> CR LF
...
$<引き数Nのバイト数> CR LF
<引き数データ> CR LF
次のようになります。
*3
$3
SET
$5
mykey
$7
myvalue
このコマンドを文字列形式で正確に表現すると、次のようになります。
"*3\r\n$3\r\nSET\r\n$5\r\nmykey\r\n$7\r\nmyvalue\r\n"
これは、Redisからのリプライにも使われます。引き数に "$6\r\nmydata\r\n" という形式を使ったものを、 バルクリプライ と呼んでいます。また、リストを返すなど、実際に統一リクエスト・プロトコルが使用されているものを、 マルチバルクリプライ と呼んでいます。これは、それぞれの引き数(バルクリプライ)の前に、引き数の数<argc>を指定するための、 *<argc>nr を前につけたものになります。
Redisはコマンドに対して、様々な形式のリプライを返します。最初の1バイトを見ることで、どのような種類のリプライかをチェックすることができます。
1行リプライは1行の文字列で構成されたリプライで、 + で始まり、 \r\n で終わります。
+OK
クライアントライブラリは + の後ろの結果を返すべきです。この場合は、 "OK" になります。
1行リプライを返すコマンド: PING, SET, SELECT, SAVE, BGSAVE, SHUTDOWN, RENAME, LPUSH, RPUSH, LSET, LTRIM
エラーリプライは、1行リプライと非常に良く似ていますが、 + の代わりに - が最初のバイトとして返されます。
エラーリプライは、異なるデータ型に対する操作を実行しようとした、存在しないコマンドを実行した場合など、何か想定外のことが発生した場合に送信されます。クライアントライブラリではエラーリプライを受け取ったら、その言語の例外処理機構を使って例外を投げるべきでしょう。
先頭の1バイトが : で始まり、CRLFで終端されている文字列の場合、このリプライは数値であるという意味になります。例えば、 ":0\r\n" や、 ":1000\r\n" などが数値リプライになります。
INCR や LASTSAVE のようなコマンドは実際に意味のある数値を数値リプライを使って返します。 INCR の場合はインクリメンタルした数値を、 LASTSAVE は、UNIX時間を返します。
また、 EXISTS などのコマンドは、True/Falseの意味で、1や0の数値を返します。
SADD や SREM 、 SETNX などの他のコマンドは、操作が実際に行われた場合に1を、そうでない場合に0を返します。
数値リプライを返すコマンド: SETNX, DEL, EXISTS, INCR, INCRBY, DECR, DECRBY, DBSIZE, LASTSAVE, RENAMENX, MOVE, LLEN, SADD, SREM, SISMEMBER, SCARD
バルクリプライは、単一のバイナリセーフな文字列を返すために使用されます。
C: GET mykey
S: $6
S: foobar
サーバは最初に $ + バイト数 + CRLFというデータを送信し、その後実際のデータを送付し、最後に追加でCRLFの2バイトを送信します。サーバから送られる、正確な文字列は次のようになります。
"$6\r\nfoobar\r\n"
もし、要求された値が存在しない場合には、データ超として、特別に-1が返されます。
C: GET nonexistingkey
S: $-1
要求されたオブジェクトが存在しない場合には、クライアントライブラリは空の文字列ではなく、nilオブジェクトを返すようにしてください。例えば、RubyのライブラリはCのライブラリが NULL を返すべきところでは、 nil を返したり、リプライオブジェクトに特別なフラグを設定したりします。
LRANGE のようなコマンドは複数の値(リストの要素もすべて値で、このコマンドは一つ以上の要素を返す)を返します。この時にはまずいくつの要素があるかを出力し、その後複数回のバルク書き出しが行われます。
C: LRANGE mylist 0 3
S: *4
S: $3
S: foo
S: $3
S: bar
S: $5
S: Hello
S: $5
S: World
みていただいた通り、マルチバルクリプライは、Redisサーバが使用する、統一プロトコルでコマンドを送るのに使用しているのとまったく同じフォーマットになっています。
サーバが送信している最初の行の "4\r\n" は、この後4回バルクリプライが送信されることを表しています。その後、回数分のバルクリプライが送信されます。
もし指定されたキーが存在しなかった場合には、リストの要素数ではなく、 -1 を返します。
C: LRANGE nokey 0 1
S: *-1
クライアントライブラリのAPIは、この場合は空のリストを返すのではなく、nilオブジェクトを返すようにしてください。これによって、例えば BLPOP コマンドのタイムアウトのように、本当に空のリストだった場合と、その他の状況を区別できるようになります。
統一リクエストプロトコルを使用する前は、Redisは異なるプロトコルでコマンドを送信していました。このプロトコルはまだサポートされており、telnet越しにタイプできるぐらいシンプルです。このプロトコルには2種類のコマンドがあります。
Redisにコマンドを送る、もっとも簡単な方法が、このインラインコマンドです。次の例は、サーバ/クライアント間でインラインコマンドを使って通信しあっています。(サーバ側は S: 、クライアント側は C と書いています)
C: PING
S: +PONG
次の例は、数値を返す場合のインラインコマンドの例です。
C: EXISTS somekey
S: :0
somekey が存在していなかったため、 :0 が返ってきています。
EXISTS コマンドは一つの引き数を受け取リますが、ここではスペース区切りで一緒に記述していることに注意してください。
いくつかのコマンドは、最後の引き数をバイナリセーフでサーバに送信するために、特別な形式を使ってコマンドを送る必要があります。このコマンドは最後の引き数にバイト数のカウントを設定し、その後、バルクデータを送信します。サーバ側は、何バイト読み込めばよいかを知っているため、安全にこのデータを利用できます。
次の例がサンプルになります。
C: SET mykey 6
C: foobar
S: +OK
コマンドの最後の引き数が '6' になっています。これは次に続くデータのバイト数を表しています。ここでは、次に来る "foobar" という文字列です。この文字列の後ろには CRLF の2バイトが付加されます。
すべてのバルクコマンドはこの形式になっています。最後の引き数として数値が入り、この後ろに送信したいバイナリが付加され、最後にCRLFが来ます。プログラマ向けにもっとわかりやすく表現すると、上記の例は次のような文字列としてクライアントから送信されます。
"SET mykey 6\r\nfoobar\r\n"
Redisは内部で、どのコマンドがインラインで、どのコマンドがバルクなのか、というリストを持っています。クライアントはそれに従ってコマンドを送信する必要があります。現在では、このプロトコルの代わりに、新しい統一リクエストプロトコルを使うことを推奨します。