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

网友投稿 215 2023-01-12

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秒

} cawfEZWSSLJtch (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 < 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) {

rehttp://turn 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\wfEZWSSLJn", 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 < 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 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客户端的资料请关注我们其它相关文章!

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:java实现简单贪吃蛇小游戏
下一篇:东兴快递物流查询单号是多少(东快递单号查询电话)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~