前排说一下,这是一个十分简陋的KV内存数据库,作为笔者实现redis的第一章,大佬可以走了,因为真的很简陋。仅供学习。

心血来潮,看到了开源项目godis,但自己对go又没有很熟悉,一开始去看了godis,一头雾水,索性想到为什么不用java来实现一个redis呢?说干就干

​ 第一步,我们来实现一个简单的运行在单机的内存型的KV数据库,严格来说这不是redis,和redis差了十万八千里。就是将一个字典,通过网络的方式提供了出去。但毕竟第一步,我们就来实现一个简单一点的(十分的简陋)。

​ 我们主要来实现几个部分

  • 客户端——负责发起请求
  • 服务端——负责接收客户端的请求并处理
  • 存储——负责存储具体的KV键值对(这里我们暂定只能存的KV均为String类型)

客户端

客户端负责根据用户输入发起请求,通过网络的方式向服务端发送命令,并接受服务端的返回的结果,我们这里使用最原生的socket来实现。

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();       //读取服务端返回结果,单独启动一个线程,防止阻塞

            Scanner input = new Scanner(System.in);
            while(input.hasNext()){
                String command = input.nextLine();      //接受用户输入
                bufferedWriter.write(command+"\n");
                bufferedWriter.flush();
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

}

/**
 * 服务读取服务器的结果,为了防止阻塞,所以新起了线程进行读
 */
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();
        }
    }

服务端

服务端负责接收用户的请求,并对请求进行解析,存储,返回结果,这里假设只有set和get两种结果

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();
        }
    }

}

/**
 * 负责对请求进行处理
 */
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")) {             //识别为set命令就set
                    KvStore.set(s[1], s[2]);
                } else if (s[0].equals("get") || s[0].equals("GET")) {      //识别为get命令就get
                    String s1 = KvStore.get(s[1]);
                    bufferedWriter.write(s1 + "\n");
                    bufferedWriter.flush();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

存储

存储这里KV存储,所以就使用了java内置的map进行存储,由于可能有并发的问题,所以使用ConcurrentHashMap来解决。

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);
    }

}

到这里我们就实现了一个简单的KV数据库,是的,它相当的简陋,没有任何异常处理,只能set和get,没有用到任何高深的技术,没有定义任何协议,甚至没有导入任何的maven依赖。

下面让我们来测试一下,启动客户端,启动服务端:

alt

他顺利的工作了起来。