30分钟手写Jedis
Posted 一小页
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了30分钟手写Jedis相关的知识,希望对你有一定的参考价值。
道阻且长,行则将至。请相信我,你一定会更优秀!
一张图带你理清思路:
请注意,所有CS架构的服务都离不开这三层。通俗点讲就是,CS之间约定一个协议,只有符合此协议的数据信息S才会处理,同样,S会遵循此协议回复给C。传输层即连接层,我们使用 socket连接redis-server即可;API层就是供C使用的操作命令;协议层即CS之间的协议,我们必须知道它们之间的协议。
目录
第一步,解密Jedis Protocol
- 我们自己模拟 redis-server;
- 使用官方Jedis发送请求,抓取报文信息;
- 分析报文信息,解密 protocol;
按照此思路,逐一实现:
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
第二步,手写Jedis传输层&API层
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;
第三步,手写Jedis协议层
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:
完工。
努力改变自己和身边人的生活。
特别希望本文可以对你有所帮助,原创不易,感谢你留个赞和关注,道阻且长,我们并肩前行!
转载请注明出处。感谢大家留言讨论交流。
以上是关于30分钟手写Jedis的主要内容,如果未能解决你的问题,请参考以下文章