Implementing redis client using java (simple jedis)

The redis server uses port 6379 by default to communicate with the outside world, so we can actually implement a simple redis client on our own. This is done in java.
Implementing a redis client consists of two main parts

  1. socket communication
  2. Encoding and decoding of redis communication protocol

The second part is mainly the implementation of resp. Here's a brief introduction to resp.
Introduction to RESP Protocol

Redis's client and server use a standalone protocol called RESP(REdis Serialization Protocol) as the standard way of communication at the top of the TCP protocol.

The Redis protocol makes a compromise between the following:
Simple implementation
Quickly parsed by computer
Simple enough to be interpreted manually
The new Unified Protocol was introduced in Redis 1.2, but in Redis 2.0 it became the standard way to communicate with Redis servers. In this Unified Protocol, all parameters sent to the Redis server are binary secure. Redis replies to commands with different reply types. It can check the reply type from the first byte sent by the server:

One-line reply (one-line string reply), the first byte of reply will be'+'
Error message (another form of one-line string reply), the first byte of reply will be'-'
Integer reply (positive integer reply), the first byte of the reply will be ":"
Batch reply (multiline string reply), the first byte of reply will be'$'
Multiple batch replies (array replies), the first byte of replies will be'*'

For example, the command set name ldh is actually an array, which is encoded with resp

*3			//3 means the array length is 3
$3			//3 for string length
set
$4
name
$3
ldh

The OK returned by the server is actually + OK. In a nutshell, our encode and decode functions are defined as follows

//Encoding, will ['set','name','ldh'] -> *3\r\n$3\r\n set\r\r\n$4\r\nname\r\r\n$3\r\nldh\r\n
byte[] encode(String[] command){

}

//Decode by *3\r\n$3\r\n set\r\n$4\r\nname\r\n$3\r\nldhr\n -> ["set", ""ldh"]
String[] decode(byte[] msg){

}

Note that we are only implementing resp encoding for the client, because all the client's sending is data, the client will not send errors to the server, reshape these messages, first we implement encoding (encode)

byte[] encode(String[] commands){
    StringBuilder stringBuilder = new StringBuilder();
    stringBuilder.append("*").append(commands.length).append("\r\n");
    for(String command : commands) {
        stringBuilder.append("$").append(command.length()).append("\r\n");
        stringBuilder.append(command).append("\r\n");
    }
    return stringBuilder.toString().getBytes();
}

Then let's write a simple socket client and code it using the encoding function we have above. Note that while reading here, the java socket read method will block, so we will start the read operation on a separate thread with the following code

public class test {

    public static void main(String[] args) {
        try {
            Scanner in = new Scanner(System.in);
            Socket socket = new Socket("127.0.0.1", 6379);
            OutputStream outputStream = socket.getOutputStream();
            InputStream inputStream = socket.getInputStream();
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));

            while(in.hasNext()) {
                String str = in.nextLine();
                String[] command = str.split(" ");
                outputStream.write(
                        encode(command)
                );
                outputStream.flush();

                new Thread(new read(bufferedReader)).start();

            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    static byte[] encode(String[] commands){
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("*").append(commands.length).append("\r\n");
        for(String command : commands) {
            stringBuilder.append("$").append(command.length()).append("\r\n");
            stringBuilder.append(command).append("\r\n");
        }
        System.out.println(stringBuilder);
        return stringBuilder.toString().getBytes();
    }
}

class read implements Runnable{

    BufferedReader bufferedReader;

    read(BufferedReader bufferedReader){
        this.bufferedReader = bufferedReader;
    }

    @SneakyThrows
    @Override
    public void run() {
        String info;
        while((info = bufferedReader.readLine())!=null){
            System.out.println(info);
        }
    }



Then we start the redis server, fill in the ip and port of the redis server, run our function, and here I fill in the command set name ldh. Then get name

Tags: Java Database Redis

Posted on Mon, 06 Dec 2021 12:49:12 -0500 by goobers