IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 大数据 -> 实现基于Socket自定义的redis简单客户端 -> 正文阅读

[大数据]实现基于Socket自定义的redis简单客户端

一、RESP协议

首先需要明白,Redis是一个CS架构的软件,通信一般分两步(不包括pipeline和PubSub):

  • 客户端(client)向服务端(server)发送一条命令
  • 服务端解析并执行命令,返回响应结果给客户端 因此客户端发送命令的格式、服务端响应结果的格式必须有一个规范,这个规范就是通信协议。

而在Redis中采用的是RESP(Redis Serialization Protocol)协议:

  • Redis 1.2版本引入了RESP协议
  • Redis 2.0版本中成为与Redis服务端通信的标准,称为
  • RESP2 Redis 6.0版本中,从RESP2升级到了RESP3协议,增加了更多数据类型并且支持6.0的新特性--客户端缓存?

????????在RESP中,通过首字节的字符来区分不同数据类型,常用的数据类型包括5种:

  1. 单行字符串:首字节是 ‘+’ ,后面跟上单行字符串,以CRLF( "\r\n" )结尾。例如返回"OK": "+OK\r\n"
  2. 错误(Errors):首字节是 ‘-’ ,与单行字符串格式一样,只是字符串是异常信息,例如:"-Error message\r\n"
  3. 数值:首字节是 ‘:’ ,后面跟上数字格式的字符串,以CRLF结尾。例如:":10\r\n"
  4. 多行字符串:首字节是 ‘$’ ,表示二进制安全的字符串,最大支持512MB:如果大小为0,则代表空字符串:"$0\r\n\r\n" 如果大小为-1,则代表不存在:"$-1\r\n"
  5. 数组:首字节是 ‘*’,后面跟上数组元素个数,再跟上元素,元素数据类型不限:例如

?

?????????

?二、模拟redis客户端实现

【响应解析请求模块】


    /**
     * 解析响应请求信息
     *
     * @return 解析结果
     */
    private static Object handleResponse() throws IOException {
        //五种情况读取数据
        int opt = READER.read();
        switch (opt) {
            case '+'://单行字符串,读取单行信息
                return READER.readLine();
            case '-'://异常信息,读取单行信息返回异常
                return READER.readLine();
            case ':'://数值类型,读取单行
                return Long.parseLong(READER.readLine());
            case '*':
                return readBulkString();
            case '$'://读取多行字符串
                int len = Integer.parseInt(READER.readLine());
                if (len == -1) {
                    return null;
                } else if (len == 0) {
                    return "";
                } else {
                    return READER.readLine();
                }
            default:
                throw new RuntimeException("错误的数据格式!");
        }
    }


    /**
     * 数组结果解析
     *
     * @return
     * @throws IOException
     */
    private static Object readBulkString() throws IOException {
        //获取数组大小
        int size = Integer.parseInt(READER.readLine());
        if (size <= 0) {
            return null;
        } else {
            List<Object> result = new ArrayList<>();
            for (int i = 0; i < size; i++) {
                result.add(handleResponse());
            }
            return result;
        }
    }

【完整代码】

/**
 * @Author 蜂蜜柚子茶
 * @Date 2022/6/14 20:34
 */
public class MyRedisClient {

    private static Socket socket;

    private static PrintWriter WRITER;
    private static BufferedReader READER;
    private static BufferedReader KEYBOARD_INPUT;

    private static final String INFO = "127.0.0.1:6379> ";

    public static void main(String[] args) throws Exception {
        try {
            //建立连接
            //虚拟机IP地址:192.168.29.128
            socket = new Socket("192.168.29.128", 6379);
            //获取输入流、输出流
            WRITER = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8));
            READER = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8));
            //键盘输入命令
            KEYBOARD_INPUT = new BufferedReader(new InputStreamReader(System.in));
            //执行命令,同时结果解析
            execute();

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //释放连接
            try {
                if (READER != null)
                    READER.close();
                if (WRITER != null)
                    WRITER.close();
                if (socket != null)
                    socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 获取键盘输入
     *
     * @return
     * @throws Exception
     */
    public static String getInput() throws Exception { // 键盘信息输入
        System.out.print(INFO);
        return KEYBOARD_INPUT.readLine();
    }

    /**
     * 执行命令
     *
     * @throws IOException
     */
    private static void execute() throws Exception {
        while (true) {
            //获取输入命令,去除首位空格
            String string = getInput().trim();
            //解析命令,去除所有空格
            String replace = string.replaceAll("\\s{1,}", "/");
            //System.out.println(replace);
            String[] strings = replace.split("/");
            //发送请求
            sendRequest(strings);
            //解析响应信息
            Object result = handleResponse();
            if (result == null) {
                System.out.println(getFormatResult("null", "warning"));
            } else if (result.toString().startsWith("ERR")) {
                System.out.println(getFormatResult(result.toString(), "error"));
            } else {
                System.out.println(getFormatResult(result.toString(), "info"));
            }
        }
    }

    /**
     * 格式化输出结果
     *
     * @param content 结果
     * @param type    类型
     * @return 格式化输出结果
     */
    private static String getFormatResult(String content, String type) {
        if (type.equals("error")) {
            return String.format("\033[%dm%s\033[0m", 31, content);
        } else if (type.equals("info")) {
            return String.format("\033[%dm%s\033[0m", 34, content);
        } else if (type.equals("warning")) {
            return String.format("\033[%dm%s\033[0m", 33, content);
        } else {
            return content;
        }
    }

    /**
     * 解析响应请求信息
     *
     * @return 解析结果
     */
    private static Object handleResponse() throws IOException {
        //五种情况读取数据
        int prefix = READER.read();
        switch (prefix) {
            case '+'://单行字符串,读取单行信息
                return READER.readLine();
            case '-'://异常信息,读取单行信息返回异常
                return READER.readLine();
            case ':'://数值类型,读取单行
                return Long.parseLong(READER.readLine());
            case '*':
                return readBulkString();
            case '$'://读取多行字符串
                int len = Integer.parseInt(READER.readLine());
                if (len == -1) {
                    return null;
                } else if (len == 0) {
                    return "";
                } else {
                    return READER.readLine();
                }
            default:
                throw new RuntimeException("错误的数据格式!");
        }
    }

    /**
     * 数组结果解析
     *
     * @return
     * @throws IOException
     */
    private static Object readBulkString() throws IOException {
        //获取数组大小
        int size = Integer.parseInt(READER.readLine());
        if (size <= 0) {
            return null;
        } else {
            List<Object> result = new ArrayList<>();
            for (int i = 0; i < size; i++) {
                result.add(handleResponse());
            }
            return result;
        }
    }

    /**
     * 发送请求信息
     *
     * @param args
     */
    private static void sendRequest(String... args) {
        //本质上是命令--> set name XXXX
        WRITER.println("*" + args.length);
        for (String arg : args) {
            WRITER.println("$" + arg.getBytes(StandardCharsets.UTF_8).length);
            WRITER.println(arg);
        }
        //清空缓冲区
        WRITER.flush();
    }
}

三、效果展示

【模拟redis-cli】idear窗口?

win的控制台输出颜色乱码,不支持颜色的转义。


如果文章对你有用,狠狠地三连支持一下吧!!!

  大数据 最新文章
实现Kafka至少消费一次
亚马逊云科技:还在苦于ETL?Zero ETL的时代
初探MapReduce
【SpringBoot框架篇】32.基于注解+redis实现
Elasticsearch:如何减少 Elasticsearch 集
Go redis操作
Redis面试题
专题五 Redis高并发场景
基于GBase8s和Calcite的多数据源查询
Redis——底层数据结构原理
上一篇文章      下一篇文章      查看所有文章
加:2022-06-18 23:28:02  更:2022-06-18 23:28:35 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 20:53:03-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码