Redis Protocol
数据类型
Redis协议种数据类型总共就5种,你拿到一坨字节后根据第一个字节来判断这一坨字节代表的是哪种数据类型,Redis协议实现起来比较简单,而且是human readable,可以说是文本协议。
1.Simple String
简单字符串类型,第一个字节是"+",后面跟着一个字符串,该字符串不能包含CR和LF,以CRLF("\r\n")结尾
比如"hello"编码为: "+hello\r\n"
"+hello\rworld\r\n"或者"+hello\nworld\r\n"是不合法的,如果串中包含"\r"或者"\n"等,你就不能编码为Simple String,而应该用Bulk String
2.Errors
错误类型,Errors再编码方式上和Simple String一样,除了第一个字节是"-"符号外。
比如"-Err unkown command 'fuck'",从‘-’到第一个空格间的内容为错误类型,不过这个类型并没有严格的标准,所以意义也不是很大
3.Integers
数值类型,第一个字节是‘:’,后面是数值,最后以"\r\n"结尾
例如20 编码为 ":20\r\n"
很多redis命令都是返回一个Integers,这个返回数值也没啥严格的意义,当是incr命令是数值代表incr后的值,当时lastsave时返回的是unix时间戳等等。不过可以保证该数值是64位的有符号整数,因此Java里恰好用long来表示。
返回布尔值的命令都是以Integers来替代的,1代表true,0代表false
所有返回Integers的命令如下:
SETNX, DEL, EXISTS, INCR, INCRBY, DECR, DECRBY, DBSIZE, LASTSAVE, RENAMENX, MOVE, LLEN, SADD, SREM, SISMEMBER, SCARD.
4.Bulk String
第一个字节是"$",用来传输二进制安全的字符串,最大长度为512MB
格式:$后面跟着一个整数N,表示字符串长度,后面跟着"\r\n",后面跟着实际的字符串(N个字节),最后以\r\n结尾。
例如"hello" 编码为 : "$5\r\nhello\r\n"
空字符串编码为:"$0\r\n\r\n"
值得注意的是,NUL编码为 "$-1\r\n",当收到NUL串时,redis-cli命令行辉提示"nil"字样,在Java里应该返回给用户NULL。
5.Arrays
数组类型,客户端给redis服务器发命令都是发一个Arrays 过去,有些命令有以Arrays响应,
第一个字节是' * ',后面跟着整数N,代表数组长度,后面跟着\r\n,后面跟着N个数组元素,每个数组元素都可以是上面说的任一类型(包括Arrays)。
例如array = [0, 1, "hello"]编码为:
*3\r\n:0\r\n:1\r\n$5\r\nhello\r\n
空数组编码为:
"*0\r\n"
NUL数组编码为:
"*-1\r\n"
当数组中某个元素为NUL时,比如数组array = [1, NUL],编码为:
"*2\r\n:1\r\n$-1\r\n"
发送命令
1.客户端发送一个Arrays,表示命令
2.服务端回应任一种数据类型作为响应。
例如要执行:LLEN mylist,则其序列如下:
C: *2\r\n C: $4\r\n C: LLEN\r\n C: $6\r\n C: mylist\r\n S: :48293\r\n
C为客户端,S为Server,上面发送的多行是为了方便看。实际是*2\r\n$4\r\nLLEN\r\n$6\r\nmylist\r\n
Java简单示例:执行命令get name, 期望返回值是"Jack":
package cc.lixiaohui.demo.redis; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.Socket; public class RedisClient { static final String CRLF = "\r\n"; public static void main(String[] args) throws Exception, IOException { Socket socket = new Socket("127.0.0.1", 6379); socket.getOutputStream().write(getCmd("name")); InputStream in = socket.getInputStream(); byte[] buf = new byte[1024];// 不循环读, 免得阻塞 int len = in.read(buf); socket.close(); byte[] resp = new byte[len]; System.arraycopy(buf, 0, resp, 0, len); System.out.println(resp.length); System.out.println(new String(resp)); } static byte[] getCmd(String key) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(baos); dos.writeBytes("*2"); dos.writeBytes(CRLF); dos.writeBytes("$3"); dos.writeBytes(CRLF); dos.writeBytes("get"); dos.writeBytes(CRLF); dos.writeBytes("$" + key.length()); dos.writeBytes(CRLF); dos.writeBytes(key); dos.writeBytes(CRLF); return baos.toByteArray(); } }
看看返回的字节:
参考ASCII码表后,以上字节序列即为:$ 4 \r \n J a c k \r \n