Redis 클라이언트는 RESP(REdis 직렬화 프로토콜, redis 직렬화 프로토콜)라는 프로토콜을 사용하여 Redis 서버와 상호 작용합니다. 이 프로토콜은 Redis용으로 설계되었지만 다른 클라이언트-서버 아키텍처 소프트웨어 시스템에서도 사용할 수 있습니다. (주석: 일부 공개 정보에 따르면 Momo의 IM 프로토콜 설계는 Redis 프로토콜을 참조합니다.)
RESP는 다음 측면을 고려합니다.
구현은 구문 분석이 간단하고 사람들이 읽기 쉽고 빠르고 쉬워야 합니다.
RESP는 직렬화 가능 정수, 문자열, 배열과 같은 다양한 데이터 유형에도 오류를 위해 설계된 특수 유형이 있습니다. 클라이언트는 실행을 위해 Redis 서버에 문자열 매개변수 배열 형태로 요청을 보내고, Redis는 명령과 관련된 데이터 유형을 반환합니다.
RESP는 바이너리 안전하며 대량 데이터를 전송하기 위해 길이 접두사를 사용하므로 한 프로세스에서 다른 프로세스로 전송된 대량 데이터를 구문 분석할 필요가 없습니다.
참고: 여기에 언급된 프로토콜은 클라이언트-서버 통신에만 사용됩니다. Redis 클러스터는 노드 간 메시지 교환을 위해 다양한 바이너리 프로토콜을 사용합니다.
네트워크 계층
클라이언트는 포트 6379로 TCP 연결을 설정하여 Redis와 통신합니다.
RESP는 기술적으로 TCP와 관련이 없지만 Redis의 경우 프로토콜은 TCP(또는 Unix 도메인 프로토콜과 같은 기타 스트리밍 프로토콜)에만 사용됩니다. (주석: 반면에 memcached는 tcp와 udp를 모두 지원하지만 실제로 프로덕션 환경은 기본적으로 tcp만 사용합니다. 이는 과도한 설계라고 생각되며 해커가 memcached udp 반사 공격을 수행하는 데 사용될 수도 있습니다. . . )
요청 응답 모델
Redis는 다양한 매개변수로 구성된 명령을 받습니다. 명령이 수신되면 처리되고 응답이 클라이언트로 전송됩니다.
이것은 가장 간단한 모델이지만 두 가지 예외가 있습니다.
Redis는 파이프라이닝을 지원합니다(나중에 언급됨). 따라서 클라이언트는 한 번에 여러 명령을 보내고 응답을 기다릴 수 있습니다. 클라이언트가 Pub/Sub 채널을 구독하면 프로토콜은 의미 체계를 변경하고 푸시 프로토콜이 됩니다. 즉, 서버가 메시지를 받은 후 자동으로 클라이언트에 새 메시지를 보내기 때문에 클라이언트가 명령을 보낼 필요가 없습니다( 클라이언트가 구독하는 채널).
이 두 가지 점을 제외하면 Redis 프로토콜은 간단한 요청-응답 프로토콜입니다.
RESP 프로토콜 설명
RESP 프로토콜은 Redis 1.2에서 도입되었지만 이제 Redis 2.0에서는 표준 상호 작용 프로토콜이 되었습니다. Redis 클라이언트를 구현할 때 이 프로토콜을 사용해야 합니다.
RESP는 실제로 단순 문자열, 오류, 정수, 대량 문자열 및 배열 유형을 지원하는 직렬화 프로토콜입니다.
RESP는 요청 응답 프로토콜로서 Redis에서 다음과 같이 사용됩니다.
클라이언트는 RESP 대량 문자열 배열 형식으로 Redis 서버에 명령을 보냅니다. 서버는 다양한 명령을 구현하고 해당 RESP 구현을 반환합니다.
RESP에서 일부 데이터 유형은 첫 번째 바이트에 의해 결정됩니다.
SIMple 문자열의 경우 응답의 첫 번째 바이트는 "+"이고, 오류의 경우 응답의 첫 번째 바이트는 정수의 경우 "-"입니다. 응답 대량 문자열의 경우 첫 번째 바이트는 ":"이고 응답의 첫 번째 바이트는 "$"이며 배열의 경우 응답의 첫 번째 바이트는 "*"입니다.
또한 RESP는 특수 대량 문자열 또는 배열을 사용할 수 있습니다. Null 값을 표현하는 방법에 대해서는 나중에 언급하겠습니다.
RESP에서 프로토콜의 여러 부분은 항상 "rn"(CRLF)으로 구분됩니다.
RESP 단순 문자열
단순 문자열은 더하기 기호 다음에 CR 또는 LF 문자(개행 없음)를 포함하지 않고 CRLF("rn")로 끝나는 문자열로 인코딩됩니다.
SImple Strings는 최소한의 노력으로 바이너리가 아닌 안전한 문자열을 전송합니다. 예를 들어 많은 Redis 명령은 성공 시 "OK"로 응답합니다. 이는 RESP 단순 문자열:
"+OKrn"
으로 인코딩된 5바이트입니다. 바이너리 안전 문자열을 전송하기 위해 RESP 대량 문자열이 사용됩니다.
Redis가 단순 문자열에 응답하면 클라이언트 라이브러리는 CRLF 바이트를 제외하고 첫 번째 "+" 문자부터 문자열 끝까지의 문자열을 호출자에게 반환해야 합니다.
RESP 오류
RESP에는 오류에 대한 특별한 데이터 유형이 있습니다. 실제로 오류는 RESP Simple String과 비슷하지만 첫 번째 문자열은 더하기 기호 대신 "-"입니다. RESP에서 단순 문자열과 오류의 실제 차이점은 오류가 클라이언트에 의해 예외로 처리되고 오류 유형을 구성하는 문자열이 문자열 자체라는 것입니다. 기본 형식은 다음과 같습니다.
"-Error messagern"
오류 응답은 오류가 발생한 경우에만 전송됩니다(예: 잘못된 데이터 유형을 사용했거나 명령이 존재하지 않는 경우 등). 오류 응답을 받으면 클라이언트는 예외를 발생시켜야 합니다.
다음은 오류 응답의 예입니다.
-ERR 알 수 없는 명령 'foobar'-WRONGTYPE 잘못된 종류의 값을 보유한 키에 대한 작업
"-"에서 첫 번째 공백 또는 첫 번째 새 줄까지 A 반환된 오류 유형을 나타내는 단어입니다. 이는 RESP Error에서 지정한 형식이 아닌 Redis 자체의 규칙일 뿐입니다.
例如,ERR 是通用错误,而 WRONGTYPE 是一种更加具体的错误,表示客户端尝试操作错误的数据类型。这称为 Error Prefix (Error前缀),客户端可从此得知服务器返回错误的类型而不需依赖于那个确切的消息描述,后者会随时改变。
一个客户端的实现可能对不同的error返回不同类型的异常,或者向调用者返回代表错误的字符串。然而这种特性并不是必须的,因为这并没什么卵用,一些精简的客户端实现可能简单的返回一般的错误情况,例如 false。
RESP Integers
这种类型就是一个代表整数的以CRLF结尾的字符串,并以“:”字节开头。例如 ":0\r\n", 或 ":1000\r\n" 都是整数响应。
很多Redis命令返回RESP Integers, 像 INCR, LLEN 和 LASTSAVE。
返回的整数并没什么特殊的含义,它就是 INCR 增加后的数字,LASTSAVE 的UNIX时间戳等。但返回的整数可以保证是在64位有符号整数的范围内。
整数响应也被大量的用于表示true或false。例如EXISTS和 SISMEMBER 等命令会返回1表示true, 0表示false。
以下命令会返回一个整数: SETNX, DEL, EXISTS, INCR, INCRBY, DECR, DECRBY, DBSIZE, LASTSAVE, RENAMENX, MOVE, LLEN, SADD, SREM, SISMEMBER, SCARD。
RESP Bulk Strings
Bulk Strings 用于表示一个二进制安全的字符串,最大长度为512M。
Bulk Strings 的编码格式如下:
“$” 后跟字符串字节数(prefix length),以CRLF结束实际的字符串CRLF结束
所以字符串"foobar" 被编码成:
"$6\r\nfoobar\r\n"
空字符串:
"$0\r\n\r\n"
RESP Bulk String 也可以用一种代表Null值的特殊格式来表示不存在的值。这种特殊格式的长度值为-1, 并且没数据,所以Null表示为:
"$-1\r\n"
这称为 Null Bulk String。
当服务器返回Null Bulk String时,客户端API不应该返回空串,而是nil 对象。例如Ruby库应该返回 'nil' 而 C 库应该返回 NULL (或在返回的对象设置特殊的标记),等等。
RESP Arrays
客户端用RESP Arrays向Redis服务器发送命令。同样某些Redis命令要返回一个元素集合时也使用RESP Arrays作为返回的类型。一个例子是LRANGE 命令返回一个元素列表。
RESP Arrays使用以下格式发送:
“*” 为第一个字节,后跟数组的元素个数,然后CRLF。然后是数组中的每一个RESP类型表示的元素。
例如一个空数组表示为:
"*0\r\n"
而有两个RESP Bulk Strings "foo" 和 "bar" 的数组编码为:
"*2\r\n$3\r\nfoo\r\n$3\r\nbar\r\n"
正如你所见,在数组前面的 *
"*3\r\n:1\r\n:2\r\n:3\r\n"
数组可以包含混合类型,它不要求所有的元素都是相同的类型。例如,一个有4个interges和1个bulk string的数组可以编码为:
*5\r\n :1\r\n :2\r\n :3\r\n :4\r\n $6\r\n foobar\r\n
(为清晰起见响应被分为多行)。
服务器发送的第一行 *5\r\n 表示后跟有5个响应,然后每个代表元素的响应被发送。
Null 数组的概念同样存在,它是Null值的替代方式 (通常使用Null Bulk String,但由于历史原因我们有两种格式)。
例如当BLPOP命令超时,它返回一个长度为-1的Null 数组,如下所示:
"*-1\r\n"
在服务端返回Null数组时,客户端库API应该返回null对象而不是空数组。区分返回空的列表与其他的情况(如BLPOP命令超时的情况)是有必要的。
RESP允许数组的数组。例如一个含两个数组的数组编码如下:
*2\r\n *3\r\n :1\r\n :2\r\n :3\r\n *2\r\n +Foo\r\n -Bar\r\n
高效解析Redis协议
尽管Redis协议非常可读并且容易实现,它却可以兼得二进制协议的高效。
RESP使用长度前缀来传输bulk 数据,所以不需要像JSON一样扫描数据负载中的特殊符号,或者用引号括住数据负载。
Bulk和Multi Bulk长度的处理可以一次处理一个字符,同时可以扫描CR字符,像如下的C代码:
#include <stdio.h> int main(void) { unsigned char *p = "$123\r\n"; int len = 0; p++; while(*p != '\r') { len = (len*10)+(*p - '0'); p++; } /* Now p points at '\r', and the len is in bulk_len. */ printf("%d\n", len); return 0; }
当第一个CR被识别后,后面的LF可以忽略不处理。然后bulk数据可以一次读取而不需要分析 数据负载。最后剩下的CR和LF字符串可以丢弃不处理。
与二进制协议比较性能时,Redis协议在大部分的高级语言实现起来足够简单,减少了客户端软件的bug数量。
注:
1. 协议中的CR和LF相当于分割符,命令间存在多个CRLF不应影响后续解析,应为多个CRLF应被忽略掉。例如:
$> (printf "PING\r\nPING\r\nPING\r\n\r\n\rPING\r\n"; sleep 1) | nc localhost 6379 +PONG +PONG +PONG +PONG
2. 对比一下memcached的协议,redis的协议确实设计得比较精简:
(1) 一致的请求形式。redis的请求都是以 Bluk String 数组发送,不同命令只是数组的元素个数不同,所有命令的处理可以先读取完整个数组再根据不同命令解析数组的参数;而不是像mc协议一样,不同请求的命令格式不同,那么在读取网络字节流的过程中就要对不同命令做不同的处理,增加了协议解析的难度。
(2) 长度前缀是高效解析协议的关键。字段长度信息并不是二进制协议的专利,文本协议也可以有。
更多Redis相关知识,请访问Redis使用教程栏目!
위 내용은 Redis 프로토콜은 무엇을 의미하나요?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!