一、Redis命令协议
- Redis客户端和服务器之间通过套接字(socket)进行通信
- 比如客户端向服务器发出 set name www.codecoord.com 命令,将会被转换成以下命令格式发送
*3\r\n$3\r\nSET\r\n$4\r\nname\r\n$17\r\nwww.codecoord.com\r\n
复制代码
- 下面拆解以下命令格式
- *3:表示命令去掉空格后的命令+参数的数量,比如set name www.codecoord.com 就是由set、name、www.codecoord.com 三个参数,所以格式为:*3
- \r\n:换行符,从一个参数后每个命令单位都需要使用换行符分隔
- $3:表示即将执行的命令或者参数的长度,此处为set,长度为3,故为$3
- set:set命令
- $4:name参数的长度
- name:set命令需要的key名称
- $17:key参数值的长度
- www.codecoord.com :key参数
- 如果显示换行符,效果会是下面的样子
*3
$3
set
$4
name
$17
www.codecoord.com
复制代码
- 如果打开持久化文件appendonly.aof,该文件就是保存的该命令协议格式
5. 如果现在执行get name命令,协议格式如下
*2\r\n$3\r\nGET\r\n$4\r\nname\r\n
复制代码
- 符合以上命令协议的命令将会被redis服务器解析,所以我们可以这样做别的事情,比如自己做一个redis客户端等,第三节命令行工具实战将可以实现redis-cli工具的功能
- 测试使用redis命令
127.0.0.1:6379> set name www.codecoord.com
OK
127.0.0.1:6379> get name
"www.codecoord.com"
复制代码
二、Java Socket连接
- 可以利用Java Socket与Redis服务器连接,如果对Socket使用还不太熟悉的朋友可以先了解一下Socket的相关内容
- Socket伪代码如下
// 1、创建连接
Socket socket = new Socket(IP, Port);
// 2、按照命令协议拼接命令cmd,比如get name
// 3、定义存储数据数组
byte[] bytes;
int read;
// 4、获取socket的输出流,写入到输出流
socket.getOutputStream().write(cmd.toString().getBytes(StandardCharsets.UTF_8));
// 5、获取输入流,用于获取结果
InputStream stream = socket.getInputStream();
bytes = new byte[Short.MAX_VALUE];
read = stream.read(bytes);
// 6、显示结果
String response = new String(bytes, 0 , read, StandardCharsets.UTF_8);
System.out.println("执行命令:\r\n" + cmd.toString() + " response = " + response);
// 7、关闭流
stream.close();
socket.close();
复制代码
- 示例代码
package com.codecoord;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.net.SocketException;
import java.nio.charset.StandardCharsets;
/**
* Redis服务器命令协议
*
* @author tianxincode@163.com
* @date 2020/4/5
*/
public class RedisSocketTest {
private static final String SEPARATOR = "\r\n";
public static void main(String[] args) {
socketConnect();
}
private static void socketConnect() {
Socket socket;
try {
socket = new Socket("127.0.0.1", 6379);
} catch (IOException e) {
System.err.println("创建连接失败~");
return;
}
try {
// 设置超时,避免命令错误或者别的原因长时间等待
socket.setSoTimeout(3000);
} catch (SocketException e) {
System.err.println("设置超时时间失败~");
return;
}
StringBuilder cmd = new StringBuilder();
// 命令加参数个数
cmd.append("*2").append(SEPARATOR);
// 当前命令长度
cmd.append("$3").append(SEPARATOR);
cmd.append("get").append(SEPARATOR);
// 命令参数长度
cmd.append("$4").append(SEPARATOR);
cmd.append("name").append(SEPARATOR);
byte[] bytes;
int read;
// 需要捕获异常,如果命令错误将会进入死等待,可以响应设置的超时时间
try {
socket.getOutputStream().write(cmd.toString().getBytes(StandardCharsets.UTF_8));
InputStream stream = socket.getInputStream();
bytes = new byte[Short.MAX_VALUE];
read = stream.read(bytes);
stream.close();
} catch (Exception e) {
System.err.println("执行命令超时~");
return;
}
String response = new String(bytes, 0 , read, StandardCharsets.UTF_8);
System.out.println("执行命令:\r\n" + cmd.toString());
System.out.println("===========================");
System.out.println("response = " + response);
try {
socket.close();
} catch (IOException e) {
System.err.println("关闭连接异常~");
}
}
}
复制代码
三、命令行工具实战
- 可以使用Java Socket和Scanner实现redis-cli(客户端命令行工具)
- 实例代码如下
package com.codecoord;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.net.SocketException;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;
/**
* Redis服务器命令协议
*
* @author tianxincode@163.com
* @date 2020/4/5
*/
public class RedisSocketTest {
private static final String SEPARATOR = "\r\n";
private static final String IP = "127.0.0.1";
private static final int PORT = 6379;
public static void main(String[] args) {
// socketConnect();
commandInput();
}
private static void commandInput() {
Scanner scanner = new Scanner(System.in);
String cmd;
// bye作为退出的判断
String exit = "bye";
do {
System.out.print(IP + PORT + "> ");
cmd = scanner.nextLine();
if (!exit.equalsIgnoreCase(cmd)) {
parseCommand(cmd);
}
} while (!exit.equalsIgnoreCase(cmd));
System.out.println("退出客户端~");
scanner.close();
}
/**
* 命令行解析, 这里只是演示,各种异常情况根据需要进行处理
*
* @param command 需要执行的命令
*/
private static void parseCommand(String command) {
Socket socket;
try {
socket = new Socket(IP, PORT);
} catch (IOException e) {
System.err.println("创建连接失败~");
return;
}
try {
socket.setSoTimeout(3000);
} catch (SocketException e) {
System.err.println("设置超时时间失败~");
return;
}
// 空格或者制表符作为分隔依据
String[] split = command.split("\\s+");
// 命令解析
StringBuilder cmd = new StringBuilder();
cmd.append("*").append(split.length).append(SEPARATOR);
for (String arg : split) {
cmd.append("$").append(arg.length()).append(SEPARATOR);
cmd.append(arg).append(SEPARATOR);
}
InputStream stream;
String[] result;
try {
socket.getOutputStream().write(cmd.toString().getBytes(StandardCharsets.UTF_8));
stream = socket.getInputStream();
byte[] bytes = new byte[Short.MAX_VALUE];
int read = stream.read(bytes);
String response = new String(bytes, 0 , read, StandardCharsets.UTF_8);
result = response.split(SEPARATOR);
} catch (IOException e) {
System.err.println("执行命令超时~");
return;
}
// 简单命令如果没有错误最后一个是结果,实际情况中需要根据对应情况进行处理
System.out.println(result[result.length - 1]);
try {
stream.close();
socket.close();
} catch (IOException e) {
System.err.println("关闭连接异常~");
}
}
/**
* socket连接测试
*/
private static void socketConnect() {
Socket socket;
try {
socket = new Socket("127.0.0.1", 6379);
} catch (IOException e) {
System.err.println("创建连接失败~");
return;
}
try {
socket.setSoTimeout(3000);
} catch (SocketException e) {
System.err.println("设置超时时间失败~");
return;
}
StringBuilder cmd = new StringBuilder();
// 命令加参数个数
cmd.append("*2").append(SEPARATOR);
// 当前命令长度
cmd.append("$3").append(SEPARATOR);
cmd.append("get").append(SEPARATOR);
// 命令参数长度
cmd.append("$4").append(SEPARATOR);
cmd.append("name").append(SEPARATOR);
byte[] bytes;
int read;
try {
socket.getOutputStream().write(cmd.toString().getBytes(StandardCharsets.UTF_8));
InputStream stream = socket.getInputStream();
bytes = new byte[Short.MAX_VALUE];
read = stream.read(bytes);
stream.close();
} catch (IOException e) {
System.err.println("执行命令超时~");
return;
}
String response = new String(bytes, 0 , read, StandardCharsets.UTF_8);
System.out.println("执行命令:\r\n" + cmd.toString());
System.out.println("===========================");
System.out.println("response = " + response);
try {
socket.close();
} catch (IOException e) {
System.err.println("关闭连接异常~");
}
}
}
复制代码
- 执行结果
- 127.0.0.16379中间忘记加个 : ,如果要美观点加一下
- 有任何问题可以留言相互交流




近期评论