Java Socket实现Redis客户端的详细说明

socket编程是一门技术,它主要是在网络通信中经常用到.这篇文章主要介绍了如何用Java Socket实现一个简单的Redis客户端,需要的朋友可以参考下

Redis是最常见的缓存服务中间件,在java开发中,一般使用 jedis 来实现。

如果不想依赖第三方组件,自己实现一个简单的redis客户端工具,该如何实现呢?本文就是介绍这样一种方法。

Redis的协议非常简单,而且输入数据和输出数据都遵循统一的协议,具体规则参考这里:

http://redisdoc.com/topic/protocol.html

Redis的命令协议:

$参数数量n

$参数1的值的字节数组长度

$参数1的值的字符串表示

$参数2的值的字节数组长度

$参数2的值的字符串表示

...

$参数n的值的字节数组长度

$参数n的值的字符串表示

Redis的返回协议:

1、状态回复(status reply)的第一个字节是 "+",单行字符串;
2、错误回复(error reply)的第一个字节是 "-";
3、整数回复(integer reply)的第一个字节是 ":";
4、批量回复(bulk reply)的第一个字节是 "$";
5、多条批量回复(multi bulk reply)的第一个字节是 "*";
6、所有的命令都是以 \r\n 结尾。

Java代码说明

针对上述规则,我们用两个类来实现:

1、SimpleRedisClient类,主要用于发送请求,并读取响应结果(字符串);

整体比较简单,稍微复杂点的地方就是读取流数据,遇到两种情况就该结束循环,一是返回长度为-1,二是返回字符串以 \r\n 结尾。

如果处理不当,可能会导致 read 阻塞,Socket卡住。

2、SimpleRedisData类,用于解析响应结果,把redis统一协议的字符串,解析为具体的对象。

这部分代码完全是按照协议规则来实现的,通过一个游标 pos 来向前移动,在移动过程中识别不同格式的数据。

最复杂的是 list 类型的数据,以 * 开头,后面跟着一个整数,表示列表中所有元素的数量,然后就是每一个列表元素的值,循环解析即可。

 package demo; import java.io.Closeable; import java.io.IOException; import java.net.Socket; import java.util.List; public class SimpleRedisClient implements Closeable { private String host; private int port; private String auth; private Socket socket = null; public SimpleRedisClient(String host, int port, String auth) { this.host = host; this.port = port; this.auth = auth; try { socket = new Socket(this.host, this.port); socket.setSoTimeout(8 * 1000);//8秒 } catch (Exception ex) { socket = null; ex.printStackTrace(); } } public boolean connect() throws IOException { if (socket == null || auth == null || auth.length() <= 0) { return false; } String response = execute("AUTH", auth); if (response == null || response.length() <= 0) { return false; } String res = new SimpleRedisData(response).getString(); return "OK".compareTo(res) == 0; } @Override public void close()  { try { if (socket != null) { socket.shutdownOutput(); socket.close(); } //System.out.println("closed"); } catch (Exception ex) { ex.printStackTrace(); } } public String getString(String key) { if (socket == null || key == null || key.isEmpty()) { return null; } try { String response = execute("GET", key); return new SimpleRedisData(response).getString(); } catch (Exception ex) { ex.printStackTrace(); return null; } } public String setString(String key, String value) { if (socket == null || key == null || key.isEmpty()) { return null; } try { String response = execute("SET", key, value); return new SimpleRedisData(response).getString(); } catch (Exception ex) { ex.printStackTrace(); return null; } } public String deleteKey(String key) throws IOException { if (socket == null || key == null || key.isEmpty()) { return null; } String response = execute("DEL", key); return new SimpleRedisData(response).getString(); } public List getKeys(String pattern) throws IOException { if (socket == null || pattern == null || pattern.isEmpty()) { return null; } String response = execute("KEYS", pattern); return new SimpleRedisData(response).getStringList(); } public String execute(String... args) throws IOException { if (socket == null || args == null || args.length <= 0) { return null; } //System.out.println(StringUtil.join(args, " ")); StringBuilder request = new StringBuilder(); request.append("*" + args.length).append("\r\n");//参数的数量 for (int i = 0; i 
 package demo; import java.util.ArrayList; import java.util.List; public class SimpleRedisData { public SimpleRedisData(String rawData) { this.rawData = rawData; //System.out.println(rawData); } private int pos; private String rawData; public String getString() { if (rawData == null || rawData.length() <= 0) { return null; } int i = rawData.indexOf("\r\n", pos); if (i <= 0) { return null; } char c = rawData.charAt(pos); if (c == '+') { int from = pos + 1; int to = i; String v = rawData.substring(from, to); pos = to + 2; return v; } else if (c == '-') { int from = pos + 1; int to = i; String v = rawData.substring(from, to); pos = to + 2; return v; } else if (c == ':') { int from = pos + 1; int to = i; String v = rawData.substring(from, to); pos = to + 2; return v; } else if (c == '$') { int from = pos + 1; int to = i; int bulkSize = Integer.parseInt(rawData.substring(from, to)); pos = to + 2; from = pos; to = pos + bulkSize; try { //$符号后面的数值是指内容的字节长度,而不是字符数量,所以要转换为二进制字节数组,再取指定长度的数据 byte[] buf = rawData.substring(from).getBytes("utf-8"); String v = new String(buf, 0, bulkSize); pos = to + 2; return v; } catch (Exception ex) { ex.printStackTrace(); return null; } } else { return null; } } public List getStringList() { if (rawData == null || rawData.length() <= 0) { return null; } int i = rawData.indexOf("\r\n", pos); if (i <= 0) { return null; } char c = rawData.charAt(pos); if (c == '*') { List values = new ArrayList<>(); int from = pos + 1; int to = i; int multSize = Integer.parseInt(rawData.substring(from, to)); pos = to + 2; for (int index = 0; index 
 package demo; import org.junit.jupiter.api.Test; import java.util.List; public class RedisTest { @Test public void test() { SimpleRedisClient client = null; try { client = new SimpleRedisClient("127.0.0.1", 6379, "123456"); System.out.println("connected: " + client.connect()); List keyList = client.getKeys("api_*"); for (int i = 0; i 

优点:

1、不依赖任何第三方组件,可以顺利编译通过;

2、代码极其简单。

不足之处:

1、未考虑并发访问;

2、未提供更多的数据类型,以及读写方法,大家可以在此基础上包装一下。

以上就是Java Socket实现Redis客户端的详细说明的详细内容,更多请关注0133技术站其它相关文章!

赞(0) 打赏
未经允许不得转载:0133技术站首页 » 数据库