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<string> 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 < args.length; i++) { request.append("$" + args[i].getbytes("utf8").length).append("\r\n");//参数的长度 request.append(args[i]).append("\r\n");//参数的内容 } socket.getoutputstream().write(request.tostring().getbytes()); socket.getoutputstream().flush(); stringbuilder reply = new stringbuilder(); int bufsize = 1024; while (true) { byte[] buf = new byte[bufsize]; int len = socket.getinputstream().read(buf); if (len < 0) { break; } string str = new string(buf, 0, len); reply.append(str); if (str.endswith("\r\n")) { break; } } string response = reply.tostring(); //system.out.println("response: " + response); return response; } }
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<string> 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<string> 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 < multsize; index++) { values.add(getstring()); } return values; } else { return null; } } }
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<string> keylist = client.getkeys("api_*"); for (int i = 0; i < keylist.size(); i++) { system.out.println((i + 1) + "\t" + keylist.get(i)); } system.out.println("keys: " + keylist != null ? keylist.size() : "null"); system.out.println(client.getstring("api_getcustomername")); } catch (exception ex) { ex.printstacktrace(); } finally { if (client != null) { client.close(); } } } }
优点:
1、不依赖任何第三方组件,可以顺利编译通过;
2、代码极其简单。
不足之处:
1、未考虑并发访问;
2、未提供更多的数据类型,以及读写方法,大家可以在此基础上包装一下。
以上就是如何用java socket实现一个简单的redis客户端的详细内容,更多关于java socket redis客户端的资料请关注其它相关文章!
推荐阅读
-
Java Socket实现Redis客户端的详细说明
-
基于Java的Socket类Tcp网络编程实现实时聊天互动程序(三):回车实现数据到发送(详细代码完结)
-
java socket编程 实现一个简单的客户端和服务端
-
java实现sftp客户端上传文件以及文件夹的功能代码的详细介绍
-
使用 Java Socket 和 ServerSocket 实现客户端服务端通信的例子
-
一个用java的NIO实现的socket的客户端和服务端的demo
-
Java Socket实现文件的断点续传的详细方法介绍(代码示例)
-
Java Socket实现Redis客户端的详细说明
-
java实现sftp客户端上传文件以及文件夹的功能代码的详细介绍
-
基于Java的Socket类Tcp网络编程实现实时聊天互动程序(三):回车实现数据到发送(详细代码完结)