请相信我,你一定会更优秀!
一张图带你理清思路:
请注意,所有CS架构的服务都离不开这三层。通俗点讲就是,CS之间约定一个协议,只有符合此协议的数据信息S才会处理,同样,S会遵循此协议回复给C。传输层即连接层,我们使用 socket连接redis-server即可;API层就是供C使用的操作命令;协议层即CS之间的协议,我们必须知道它们之间的协议。
目录
第一步,解密Jedis Protocol
第二步,手写Jedis传输层&API层
第三步,手写Jedis协议层
第四步,演示
按照此思路,逐一实现:
1. 我们自己模拟 redis-server;
package com.haolin.java.demos.jedis;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
public class MockRedisServer {
@SuppressWarnings("resource")
public static void main(String[] args) throws IOException {
// 模拟redis-server服务
ServerSocket serverSocket = new ServerSocket(6399);
InputStream inputStream = serverSocket.accept().getInputStream();
byte[] bs = new byte[1024];
inputStream.read(bs);
// 打印请求信息
System.out.println(new String(bs, "UTF-8"));
}
}
2. 使用官方Jedis发送请求,抓取报文信息;
@Test
public void testProtocol() {
// 使用刚刚开启的模拟服务
Jedis jedis = new Jedis("redis://localhost:6399");
jedis.set("iname", "zhanghaolin");
// jedis.get("iname");
}
3. 分析报文信息,解密 protocol;
// 控制台输出
// set("iname", "zhanghaolin")
*3
$3
SET
$5
iname
$11
zhanghaolin
// 控制台输出
// get("iname")
*2
$3
GET
$5
iname
我们执行 set,get发送的这些报文信息里边,$, * 这些都代表什么呢?官网摘抄如下 redis官方文档地址
结合官网帮助,不难分析出报文信息:
# set("iname", "zhanghaolin")
*3 # *代表下边$行个数
$3 # $表示下边一行长度
SET # 命令
$5
iname
$11
zhanghaolin
package com.haolin.hedis;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import com.haolin.hedis.Protocol.Command;
/**
*
* @Description: 传输层/API层
* @author zhanghaolin
* @date 2019年5月8日 上午9:40:47
*/
public class Hedis {
private final String DEFAULT_HOST = "127.0.0.1";
private final Integer DEFAULT_PORT = 6379;
private Socket connection;
private String host;
private Integer port;
private InputStream inputStream;
private OutputStream outputStream;
public Hedis() {
this.host = DEFAULT_HOST;
this.port = DEFAULT_PORT;
connect();
}
public Hedis(String host, Integer port) {
this.host = host;
this.port = port;
connect();
}
public void set(String key, String value) {
Protocol.sendMessage(outputStream, Command.SET, SafeEncoder.castValue(key), SafeEncoder.castValue(value));
}
public String get(String key) {
Protocol.sendMessage(outputStream, Command.GET, SafeEncoder.castValue(key));
String replyMessage = Protocol.getReplyMessage(inputStream);
String temp1 = replyMessage.substring(replyMessage.indexOf(Protocol.LINEFLAG), replyMessage.lastIndexOf(Protocol.LINEFLAG));
String temp2 = temp1.substring(replyMessage.indexOf(Protocol.LINEFLAG));
String temp3 = temp2.substring(replyMessage.indexOf(Protocol.LINEFLAG)).replace(Protocol.LINEFLAG, Protocol.EMPTY);
return temp3;
}
public String incr(String key) {
Protocol.sendMessage(outputStream, Command.INCR, SafeEncoder.castValue(key));
String replyMessage = Protocol.getReplyMessage(inputStream);
return replyMessage.substring(replyMessage.indexOf(Protocol.MAOHAO), replyMessage.lastIndexOf(Protocol.LINEFLAG)).replace(Protocol.MAOHAO, Protocol.EMPTY);
}
public String decr(String key) {
Protocol.sendMessage(outputStream, Command.DECR, SafeEncoder.castValue(key));
String replyMessage = Protocol.getReplyMessage(inputStream);
return replyMessage.substring(replyMessage.indexOf(Protocol.MAOHAO), replyMessage.lastIndexOf(Protocol.LINEFLAG)).replace(Protocol.MAOHAO, Protocol.EMPTY);
}
public void del(String key) {
Protocol.sendMessage(outputStream, Command.DEL, SafeEncoder.castValue(key));
}
private void connect() {
if (!isConnected()) {
try {
connection = new Socket(host, port);
inputStream = connection.getInputStream();
outputStream = connection.getOutputStream();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private boolean isConnected() {
return connection != null && connection.isBound() && !connection.isClosed() && connection.isConnected()
&& !connection.isInputShutdown() && !connection.isOutputShutdown();
}
public void close() {
if (isConnected()) {
try {
outputStream.flush();
connection.close();
} catch (IOException ex) {
ex.printStackTrace();
} finally {
finallyClose(connection);
}
}
}
private void finallyClose(Socket connection) {
if (connection != null) {
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static class SafeEncoder {
public static byte[] castValue(String value) {
if (value == null)
value = Protocol.EMPTY;
return value.getBytes();
}
public static String castValue(final byte[] bs) {
try {
return new String(bs, Protocol.CHARSET);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return Protocol.EMPTY;
}
}
// Setter Getter
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public Integer getPort() {
return port;
}
public void setPort(Integer port) {
this.port = port;
}
}
package com.haolin.hedis;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import com.haolin.hedis.Hedis.SafeEncoder;
/**
*
* @Description: 协议层
* @author zhanghaolin
* @date 2019年5月8日 上午9:41:26
*/
public class Protocol {
public static final String CHARSET = "UTF-8";
public static final String HEAD = "*";
public static final String DOLLARS = "$";
public static final String MAOHAO = ":";
public static final String LINEFLAG = "\r\n";
public static final String EMPTY = "";
public enum Command {
SET, GET, INCR, DECR, HSET, DEL
}
public static void sendMessage(OutputStream outputStream, Command commond, byte[]... bs) {
String msg = buildSendMessage(commond, bs);
try {
outputStream.write(SafeEncoder.castValue(msg));
} catch (IOException e) {
e.printStackTrace();
}
}
public static String getReplyMessage(InputStream inputStream) {
byte[] reply = new byte[1024];
try {
inputStream.read(reply);
} catch (IOException e) {
e.printStackTrace();
}
return new String(reply);
}
public static String buildSendMessage(Command commond, byte[]... bs) {
StringBuilder msg = new StringBuilder();
msg.append(HEAD).append(bs.length + 1).append(LINEFLAG);
msg.append(DOLLARS).append(commond.toString().length()).append(LINEFLAG);
msg.append(commond).append(LINEFLAG);
for (int i = 0; i < bs.length; i++) {
msg.append(DOLLARS).append(bs[i].length).append(LINEFLAG);
msg.append(SafeEncoder.castValue(bs[i])).append(LINEFLAG);
}
return msg.toString();
}
}
为了更好的查看效果,我先进行清库
执行 set,get
package com.jedis.test;
import org.junit.Test;
import com.haolin.java.demos.jedis.Hedis;
public class HedisTest {
@Test
public void testJedis() {
// 使用我们手写的Hedis
Hedis hedis = new Hedis();
hedis.set("name", "Hello uuimi");
String value = hedis.get("name");
System.out.println(value);
}
}
控制台输出:
Hello uuimi
查看redis-server:
完工。
努力改变自己和身边人的生活。
特别希望本文可以对您有所帮助,转载请注明出处。感谢大家留言讨论交流。