Redis Jedis原理 自定义Redis客户端

通常我们调用Jedis提供的API来操作Redis,本文将手写一个简单的Redis客户端,实现Jedis的set、get和incr功能。

用Jedis连接NIO服务端,发送指令

NIO服务端以前写过,代码就不贴了,可参考此链接Java基础 BIO & NIO 设计思想

JedisTest类
package com.tbryant.jedistest;

import redis.clients.jedis.Jedis;

public class JedisTest {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1", 8888);
        System.out.println(jedis.set("tonny", "bryant"));
        System.out.println(jedis.get("tonny"));
        System.out.println(jedis.incr("count"));
    }
}
运行结果

NIO服务端
*:星号是开始符,3表示后面跟着3组指令,每2行组成一个指令。
$:dollar符后面跟数字,表示指令长度。
Jedis客户端
由此可见Jedis连接NIO服务端成功,并发送set方法对应的三组指令,但未收到服务端的返回数据,最终超时异常关闭连接。

自定义Redis Client

角色划分

常量类:存储指令符号和操作谓词。
自定义Socket类:用于发送和读取数据。
自定义RedisClient类:提供API操作Redis。
测试类:使用自定义RedisClient与Redis交互。

Constant类
package com.tbryant.customredisclient;

public class Constant {
    public static final String START = "*";// 开始符
    public static final String LENGTH = "$";// 指令长度符
    public static final String LINE = "\r\n";// 换行符

    public enum command {
        SET,
        GET,
        INCR
    }
}
CustomSocket类
package com.tbryant.customredisclient;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

public class CustomSocket {
    private Socket socket;
    private InputStream inputStream;
    private OutputStream outputStream;

    public CustomSocket(String ip, int port) {
        try {
            socket = new Socket(ip, port);
            inputStream = socket.getInputStream();
            outputStream = socket.getOutputStream();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // 把拼接完的指令发送给redis
    public void send(String cmd) {
        try {
            outputStream.write(cmd.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // 接收redis返回的数据
    public String read() {
        byte[] bytes = new byte[1024];// TODO 处理超过1024的返回数据
        int count = 0;
        try {
            count = inputStream.read(bytes);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return new String(bytes, 0, count);
    }
}
CustomRedisClient类
package com.tbryant.customredisclient;

public class CustomRedisClient {
    private CustomSocket redisSocket;

    public CustomRedisClient(String host, int port) {
        redisSocket = new CustomSocket(host, port);
    }

    public String set(String key, String value) {
        redisSocket.send(convertToCommand(Constant.command.SET, key.getBytes(), value.getBytes()));
        return redisSocket.read();
    }

    public String get(String key) {
        redisSocket.send(convertToCommand(Constant.command.GET, key.getBytes()));
        return redisSocket.read();
    }

    public String incr(String key) {
        redisSocket.send(convertToCommand(Constant.command.INCR, key.getBytes()));
        return redisSocket.read();
    }

    public static String convertToCommand(Constant.command command, byte[]... bytes) {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(Constant.START).append(bytes.length + 1).append(Constant.LINE);
        stringBuilder.append(Constant.LENGTH).append(command.toString().length()).append(Constant.LINE);
        stringBuilder.append(command.toString()).append(Constant.LINE);
        for (byte[] aByte : bytes) {
            stringBuilder.append(Constant.LENGTH).append(aByte.length).append(Constant.LINE);
            stringBuilder.append(new String(aByte)).append(Constant.LINE);
        }
        return stringBuilder.toString();
    }
}

Application类
package com.tbryant.customredisclient;

public class Application {
    public static void main(String[] args) {
        CustomRedisClient redisClient = new CustomRedisClient("127.0.0.1", 6379);
        System.out.println(redisClient.set("tonny", "bryant"));
        System.out.println(redisClient.get("tonny"));
        System.out.println(redisClient.incr("count"));
    }
}

测试代码用CustomRedisClient替换Jedis,端口号指向Redis服务端,其他代码与JedisTest保持一致。

运行结果

Application Test
操作Redis成功,并接收到Redis返回数据。查看Redis官网可以得知Redis遵循RESP协议,该协议规定Redis返回符号定义:
+:加号后面跟简单字符串。
-:减号后面跟错误信息。
::冒号后面跟int类型。
$:doller符后面跟长字符串。
*:星号后面跟数组。
源码地址:CustomJedis模块

发布了14 篇原创文章 · 获赞 3 · 访问量 876

猜你喜欢

转载自blog.csdn.net/qq_37956177/article/details/103508618
今日推荐