resp
Redis RESP
Redis Internel Course | Redis Technical Support | Redis Enterprise Server |
---|
RESP (REdis Serialization Protocol)
개요
RESP(REdis Serialization Protocol)는 레디스 통신 프로토콜(포멧) 이름입니다.redis-cli, 마스터-복제간 통신, redis client library(lettuce, jedis)가 이 프로토콜에 따라 통신합니다.
버전
- RESP는 버전 2와 3이 있는데, RESP 버전 2는 레디스의 기본 출력 프로토콜입니다. 레디스 6.x 부터는 RESP 버전 3을 선택해서 사용할 수 있습니다.
- RESP 3는 2018년 5월 부터 시작해서 2019년에 개발이 완료되었고, 2020년 4월에 발표된 레디스 6에 적용되었습니다.
- RESP 3를 사용하려면 hello 3 명령을 실행해야 하고, 접속 컨텍션(connection)별로 적용됩니다.
RESP 버전 2
프로토콜(포멧)
다섯 가지 포멧으로 구성됩니다.- status: 구분자 + 특수문자가 포함되지 않는 일반 문자열을 표시할 때 사용한다.
포멧: +<String><CR><LF> 예) OK - error: 구분자 - 특수문자가 포함되지 않는 에러 문자열을 표시할 때 사용한다.
포멧: -<ERR msg><CR><LF> 예) ERR no such key - integer: 구분자 : 정수를 표시할 때 사용한다.
포멧: :<1234><CR><LF> 예) 1234
사용하는 명령: llen, scard - bulk: 구분자 $ 특수문자가 포함된 문자열을 표시할 때 사용한다.
포멧: $11<CR><LF><hello world><CR><LF>
값(value)를 표시할 때 사용하고, 실수(double)도 이것을 사용한다.
사용하는 명령: get, hget, 등 - multibulk: 구분자 * 배열을 표시할 때 사용한다.
포멧: *10<CR><LF>
사용하는 명령: mget, lrange, smembers, zrange, hgetall, 등
RESP 버전 3
개발 배경
RESP 2에서 부족한 점- 정수(integer)와 실수(floating point number)의 구분이 없습니다.
정수는 ':'로 구분했지만, 실수는 bulk('$') 문자열을 사용했습니다. - 불린(boolean, 참 거짓)을 명시적으로 표현하는 방법이 없었습니다.
그래서 1/0로 대신 했습니다. - 널(null)도 구분 방법이 없어서 bulk $-1을 사용했습니다.
- JAVA같은 프로그래밍 언어에서 제공하는 array, set, map을 따로 구분하지 않고
모두 multibulk(array)를 사용했습니다.
레디스 데이터 타입과 대응해보면 List -> array, Set -> set, Hash -> map에 해당합니다. - 그리고 status(+), error(-) 같은 메시지도 CR, LF 같은 특수문자를 포함할 필요도 발생했습니다.
프로토콜(포멧) - 일반 데이터 타입
- Simple string: '+' v2 status
특수문자가 포함되지 않는 일반 문자열을 표시할 때 사용한다.
포멧: +String<CR><LF> - Simple error: '-' v2 error
특수문자가 포함되지 않는 에러 문자열을 표시할 때 사용한다.
포멧: -ERR msg<CR><LF> - Blob string: '$' v2 bulk
특수문자가 포함된 문자열을 표시할 때 사용한다. binary safe
포멧: $<length><CR><LF><String value><CR><LF>
- Blob error: '!'
특수문자가 포함된 문자열로 에러를 표시할 때 사용한다. binary safe
포멧: !<length><CR><LF><Error String><CR><LF>
- Number: ':' v2 integer
정수를 표시할 때 사용한다. 사용 명령: LLEN, SCARD, 등
포멧: :<number><CR<<LF>
- Big number: '(' v2 bulk
큰 정수를 표시할 때 사용한다.
포멧: (<big number><CR><LF> - Double: ',' v2 bulk
실수를 표시할 때 사용한다. ZSet의 스코어는 이 포멧을 사용한다.
포멧: ,<double><CR><LF> - Null: '_' v2 bulk "$-1\r\n"
널을 표시할 때 사용한다.
포멧: _<CR><LF> - Boolean: '#' v2 1/0
참/거짓을 표시할 때 사용한다.
포멧: #t<CR><LF> #f<CR><LF> - Verbatim string: '='
text를 그대로 출력할 때 사용
사용하는 명령: INFO, MEMORY, CLIENT, LATENCY, CLUSTER
포멧: =<length><CR><LF> txt:Some string <CR><LF> txt: plain text
포멧: =<length><CR><LF> mkd:Some string <CR><LF> mkd: markdown
mkd는 정의는 되어 있으나 아직 사용되지는 않는다. 위 명령은 모두 txt를 사용한다.
프로토콜(포멧) - 반복 지정
Array, Set, Map을 지정할 수 있습니다.- Array: *
반복을 지정할 때 사용한다. Array 안에 array를 또 지정할 수 있다.
포멧: *<length><CR><LF> v2 multibulk
List에서 사용 예
ZSet에서 사용 예
레디스에서 아래와 같이 사용하는 경우는 없습니다만, 프로토콜 상으로는 가능합니다. - Set: ~
반복을 지정할 때 사용한다. 레디스 데이터 타입 Set에서 사용한다.
이 구분자로 데이터를 받으면 Java의 Set 컬렉션을 사용하면 된다.
포멧: ~<length><CR><LF> v2 multibulk
Set에서 사용 예: ~3
레디스에서 아래와 같이 사용하는 경우는 없습니다만, 프로토콜 상으로는 가능합니다. ~5 - Map: %
Map 반복을 지정할 때 사용한다. 레디스 데이터 타입 Hash에서 사용한다.
이 구분자로 데이터를 받으면 Java의 Map 컬렉션을 사용하면 된다.
포멧: %<length><CR><LF> v2 multibulk
Hash에서 사용 예: %2 레디스에서 아래와 같이 사용하는 경우는 없습니다만, 프로토콜 상으로는 가능합니다.
프로토콜(포멧) - 스트리밍(Streaming)
전체 데이터의 길이를 모를 경우 사용할 수 있습니다.*, %, $ 다음에 '?'를 사용합니다.
*, %의 END 구분자는 .<CR><LF> 이고
$의 END 구분자는 ;0<CR><LF> 입니다.
레디스에서 실제 사용되지는 않습니다.
- Array *? 사용 예
- Map %? 사용 예
- Bulk $? 사용 예
RESP 버전 2 - Functions
공통
- void addReply(client *c, robj *obj)
- addReply(c,shared.mbulkhdr[ll]); - "*%d\r\n"
- addReply(c,shared.bulkhdr[ll]); - "$%d\r\n"
- addReply(c,shared.nullbulk); - "$-1\r\n" --> RESP3: addReplyNull(c);
- addReply(c,shared.czero); - ":0\r\n"
- addReply(c,shared.cone); - ":1\r\n"
- addReply(c,shared.ok); - "+OK\r\n"
- addReply(c,shared.syntaxerr); "-ERR syntax error\r\n"
- RESP2와 3에서 이름이 변경된 functions.
- void addReplyString(client *c, const char *s, size_t len) --> RESP3:
void addReplyProto(client *c, const char *s, size_t len) - void *addDeferredMultiBulkLength(client *c) --> RESP3:
void *addReplyDeferredLen(client *c) - void setDeferredMultiBulkLength(client *c, void *node, long length) --> RESP3:
void setDeferredReply(client *c, void *node, const char *s, size_t length) - void addReplyMultiBulkLen(client *c, long length) --> RESP3:
void addReplyArrayLen(client *c, long length) - void _addReplyStringToList(client *c, const char *s, size_t len) --> RESP3:
void _addReplyProtoToList(client *c, const char *s, size_t len)
- void addReplyString(client *c, const char *s, size_t len) --> RESP3:
Status: 구분자 +
- void addReplyStatus(client *c, const char *status)
- void addReplyStatusLength(client *c, const char *s, size_t len)
- void addReplyStatusFormat(client *c, const char *fmt, ...)
Error: 구분자 -
- void addReplyError(client *c, const char *err)
- void addReplyErrorLength(client *c, const char *s, size_t len)
- void addReplyErrorFormat(client *c, const char *fmt, ...)
Integer: 구분자 :
- void addReplyLongLong(client *c, long long ll)
- void addReplyLongLongWithPrefix(client *c, long long ll, char prefix)
Bulk: 구분자 $
- void addReplyString(client *c, const char *s, size_t len)
- void addReplyBulk(client *c, robj *obj)
- void addReplyBulkLen(client *c, robj *obj)
- void addReplyBulkCBuffer(client *c, const void *p, size_t len)
- void addReplyBulkSds(client *c, sds s)
- void addReplyBulkCString(client *c, const char *s)
- void addReplyBulkLongLong(client *c, long long ll)
- void addReplySds(client *c, sds s)
- void addReplyDouble(client *c, double d) - $%d\r\n%s\r\n
- void addReplyHumanLongDouble(client *c, long double d)
Multibulk: 구분자 *
- void addReplyMultiBulkLen(client *c, long length)
--> RESP3: addReplyArrayLen(client *c, long length) - void *addDeferredMultiBulkLength(client *c)
--> RESP3: addReplyDeferredLen(client *c) - void setDeferredMultiBulkLength(client *c, void *node, long length)
--> RESP3: setDeferredArrayLen(client *c, void *node, long length)
RESP 버전 3 - Functions
공통
- void addReply(client *c, robj *obj)
Simple string: '+' v2 status
- void addReplyStatus(client *c, const char *status)
- void addReplyStatusLength(client *c, const char *s, size_t len)
- void addReplyStatusFormat(client *c, const char *fmt, ...)
Simple error: '-' v2 error
- void addReplyError(client *c, const char *err)
- void addReplyErrorLength(client *c, const char *s, size_t len)
- void addReplyErrorFormat(client *c, const char *fmt, ...)
- void afterErrorReply(client *c, const char *s, size_t len)
- void addReplyErrorObject(client *c, robj *err)
- void addReplyErrorSds(client *c, sds err)
Blob string: '$' v2 bulk
- void addReplyProto(client *c, const char *s, size_t len)
- void addReplyBulk(client *c, robj *obj)
- void addReplyBulkLen(client *c, robj *obj)
- void addReplyBulkCBuffer(client *c, const void *p, size_t len)
- void addReplyBulkSds(client *c, sds s)
- void addReplyBulkCString(client *c, const char *s)
- void addReplyBulkLongLong(client *c, long long ll)
- void addReplySds(client *c, sds s)
Blob error: '!'
- Blob error를 사용하는 function은 없다.
Number: ':' v2 integer
- void addReplyLongLong(client *c, long long ll)
- void addReplyLongLongWithPrefix(client *c, long long ll, char prefix)
Big number: '(' v2 bulk
- void addReplyBigNum(client *c, const char* num, size_t len) - resp2 $, resp3 (
Double: ',' v2 bulk
- void addReplyDouble(client *c, double d) - ,%.17g\r\n
- void addReplyHumanLongDouble(client *c, long double d)
Null: '_' v2 bulk "$-1\r\n"
- void addReplyNull(client *c) - resp2 "$-1\r\n", resp3 "_\r\n"
Boolean: '#' v2 1/0
- void addReplyBool(client *c, int b) - resp2 shared.cone, shared.czero, resp3 "#t\r\n", "#f\r\n"
Verbatim string: '='
- void addReplyVerbatim(client *c, const char *s, size_t len, const char *ext) - "=%zu\r\nxxx:" txt, mkd
Aggregate: *,~,%,|,>
- void addReplyAggregateLen(client *c, long length, int prefix)
- void setDeferredAggregateLen(client *c, void *node, long length, char prefix)
Array: *
- void addReplyArrayLen(client *c, long length)
- void *addReplyDeferredLen(client *c)
- void setDeferredArrayLen(client *c, void *node, long length)
Set: ~
- void addReplySetLen(client *c, long length) -> int prefix = c->resp == 2 ? '*' : '~';
- void setDeferredSetLen(client *c, void *node, long length) int prefix = c->resp == 2 ? '*' : '~';
Map: %
- void addReplyMapLen(client *c, long length) -> int prefix = c->resp == 2 ? '*' : '%';
- void setDeferredMapLen(client *c, void *node, long length) int prefix = c->resp == 2 ? '*' : '%';
Attribute: |
- void addReplyAttributeLen(client *c, long length) c->resp >= 3
- void setDeferredAttributeLen(client *c, void *node, long length) -> '|' c->resp >= 3
Push: >
- void addReplyPushLen(client *c, long length) c->resp >= 3
- void setDeferredPushLen(client *c, void *node, long length) -> '>' c->resp >= 3
<< Stream | RESP |
---|
Email
답글이 올라오면 이메일로 알려드리겠습니다.