RESP Là Gì?
RESP (REdis Serialization Protocol) là tên của giao thức dựa trên văn bản (text-based) mà Redis client và server dùng để giao tiếp với nhau qua TCP. Tất cả giao tiếp giữa server và client bao gồm năm kiểu cơ bản:
- Simple strings được dùng cho các phản hồi server phổ biến như "OK" (sau lệnh ghi thành công) hoặc "PONG" (phản hồi thành công cho lệnh PING).
- Bulk strings được trả về cho hầu hết các lệnh đọc giá trị đơn như GET, LPOP, và HGET. Bulk strings khác với simple strings ở chỗ chúng có thể chứa bất cứ thứ gì — newline, control character, hoặc thậm chí RESP hợp lệ, tất cả mà không cần escape hoặc encode.
- Integers được dùng làm phản hồi cho bất kỳ loại lệnh đếm nào như STRLEN, HLEN, hoặc BITCOUNT.
- Arrays có thể chứa bất kỳ số lượng đối tượng RESP nào, bao gồm cả array khác. Điều này được dùng để gửi lệnh đến server, cũng như cho bất kỳ phản hồi nào trả về nhiều hơn một phần tử như HGETALL, LRANGE, hoặc MGET.
- Errors được trả về bất cứ khi nào Redis gặp lỗi khi xử lý lệnh của bạn, như khi cố chạy lệnh trên key giữ kiểu dữ liệu sai.
Khi ghi và đọc RESP, chúng ta không phải làm bất kỳ escaping hoặc encoding nào cho key hoặc value của mình. Phần duy nhất của RESP chúng ta cần thực sự parse là metadata đơn giản đi kèm với mỗi đối tượng RESP.
RESP Trông Như Thế Nào?
Tất cả đối tượng RESP bắt đầu bằng một ký tự tiền tố (prefix character) và kết thúc bằng một line terminator (trừ array, không bao gồm line terminator của chính nó). Ví dụ đơn giản nhất của đối tượng RESP là phản hồi simple string OK:
+OK\r\n
(RESP là giao thức human-readable nhưng để rõ ràng, tôi sẽ viết rõ các line break (\r\n) trong tất cả ví dụ RESP.)
Trong simple string trên, + là tiền tố simple string, OK là thân của simple string, và \r\n là line terminator đánh dấu kết thúc simple string này.
Để đọc một simple string, chúng ta đọc đến \r\n line terminator tiếp theo, trả về các byte trước đó (lên đến +) làm chuỗi phản hồi trả về. Điều này hoạt động vì RESP simple strings không thể bao gồm bất kỳ ký tự newline nào.
Errors và integers được định dạng tương tự simple strings, nhưng chúng dùng tiền tố khác. Errors có tiền tố -:
-ERR unknown command 'GETT'\r\n
Và integers có tiền tố ::
:99\r\n
Bulk strings là độc đáo vì chúng có hai phần. Một length specification và một body:
$13\r\nHello, World!\r\n
$ là tiền tố bulk string, 13 là số byte trong thân chuỗi thực tế, và sau đó \r\n kết thúc length specification. Hello, World! là chuỗi thân 13 byte, và nó cũng được kết thúc bằng \r\n (không phải là một phần của thân chuỗi).
Bởi vì bulk string container cung cấp độ dài chính xác cho body, chúng ta không bao giờ phải parse thân chuỗi thực tế để tìm cuối – chúng ta có thể đọc thêm 13 byte (cùng line terminator cuối) từ socket mà không cần kiểm tra nội dung chuỗi. Điều này có nghĩa là chúng ta có thể dùng bất kỳ dữ liệu nào chúng ta muốn trong thân chuỗi mà không cần encoding hoặc escaping nội dung.
Arrays cũng bắt đầu bằng một length specification, ngoại trừ length field chỉ ra số lượng đối tượng trong array thay vì số byte trong nội dung array.
*2\r\n$3\r\nfoo\r\n$3\r\nbar\r\n
Trong ví dụ trên, chúng ta có một array chứa hai bulk strings (foo và bar). Mặc dù array length specification không cho phép chúng ta bỏ qua (skip ahead) như khi đọc bulk string, nó làm cho việc triển khai array trong client của chúng ta dễ dàng. Chúng ta đọc array size, đọc thêm số lượng đối tượng đó, và sau đó trả về tất cả các đối tượng đó trong array cuối cùng.
Tất cả các lệnh Redis được gửi dưới dạng array của bulk strings. Ví dụ, lệnh "SET mykey 'my value'" sẽ được ghi và gửi như:
*3\r\n$3\r\nSET\r\n$5\r\nmykey\r\n$8\r\nmy value\r\n
Chi Tiết RESP Thêm
Các prefixed lengths được dùng trong RESP cho phép parser được triển khai mà không cần state modeling phức tạp hoặc nhiều pass qua dữ liệu, cho phép xây dựng các parser rất nhanh.
Có một số chi tiết tôi chưa bao gồm ở đây, như null bulk strings và null arrays. Để biết thêm thông tin, tài liệu chính thức (như thường lệ) có một trang tài liệu rất dễ đọc và toàn diện cho RESP.
Một cách tuyệt vời để hiểu giao thức tốt hơn là tự triển khai một client đơn giản. Hãy xem hướng dẫn đọc và ghi Redis protocol trong Go tiếp theo.