Implement a java version of redis -- implement a memory KV storage

Posted by levidyllan on Wed, 08 Dec 2021 23:53:16 +0100

The front row said that this is a very simple KV memory database. As the first chapter of the author's implementation of redis, the boss can go because it is really simple. For learning only.

On a whim, I saw the open source project godis, but I was not very familiar with go. At first, I went to see godis and was confused. I simply thought why not use java to implement a redis? Just do it

The first step is to implement a simple memory KV database running on a single machine. Strictly speaking, this is not redis, which is thousands of miles away from redis. Is to provide a dictionary through the network. But after all, the first step is to implement a simple (very simple).

Let's mainly implement several parts

  • Client - responsible for initiating the request
  • The server is responsible for receiving and processing requests from the client
  • Storage - responsible for storing specific KV key value pairs (here, we tentatively only store KV of String type)

client

The client is responsible for initiating the request according to the user input, sending commands to the server through the network, and receiving the results returned by the server. We use the most native socket here.

public class Client {

    public static void main(String[] args) {
        try {
            Socket socket = new Socket("127.0.0.1", 8888);
            OutputStream outputStream = socket.getOutputStream();

            BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream));

            new Thread(new Read(socket)).start();       //Read the results returned by the server and start a thread separately to prevent blocking

            Scanner input = new Scanner(System.in);
            while(input.hasNext()){
                String command = input.nextLine();      //Accept user input
                bufferedWriter.write(command+"\n");
                bufferedWriter.flush();
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

}

/**
 * The service reads the results of the server. In order to prevent blocking, a new thread is started to read
 */
class Read implements Runnable{

    private Socket socket;

    Read(Socket socket){
        this.socket = socket;
    }

    @Override
    public void run() {
        try {
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            String info;
            while((info = bufferedReader.readLine())!=null){
                System.out.println(info);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

Server

The server is responsible for receiving the user's request, parsing the request, storing and returning the result. Here, it is assumed that there are only set and get results

public class Server {

    public static void main(String[] args) {
        try {
            ServerSocket serverSocket = new ServerSocket(8888);
            while(true) {
                Socket socket = serverSocket.accept();
                new Thread(new Handler(socket)).start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

/**
 * Responsible for processing requests
 */
class Handler implements Runnable{

    private Socket socket;

    Handler(Socket socket){
        this.socket = socket;
    }

    @Override
    public void run() {
        try {
            InputStream inputStream = socket.getInputStream();
            OutputStream outputStream = socket.getOutputStream();
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
            BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream));
            String info;
            while((info= bufferedReader.readLine())!=null) {    
                String[] s = info.split(" ");
                if (s[0].equals("set") || s[0].equals("SET")) {             //Recognized as a set command
                    KvStore.set(s[1], s[2]);
                } else if (s[0].equals("get") || s[0].equals("GET")) {      //The get command is recognized as get
                    String s1 = KvStore.get(s[1]);
                    bufferedWriter.write(s1 + "\n");
                    bufferedWriter.flush();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

storage

Storage here is KV storage, so the built-in map in java is used for storage. Because there may be concurrency problems, concurrent HashMap is used to solve them.

public class KvStore {

    private static final ConcurrentHashMap<String,String> hashMap = new ConcurrentHashMap<>();

    public static void set(String k,String v){
        hashMap.put(k, v);
    }

    public static String get(String k){
        return hashMap.get(k);
    }

}

Here we have implemented a simple KV database. Yes, it is quite simple, without any exception handling, only set and get, without using any advanced technology, without defining any protocol, or even importing any maven dependency.

Let's test it. Start the client and start the server:

He began to work smoothly.

Topics: Java Database Redis