当前位置:首页 > 科技  > 软件

基于 Netty 的 Lettuce 居然是这样解析RESP协议的

来源: 责编: 时间:2024-05-20 17:55:35 75观看
导读今天来分享 Lettuce —— 基于 Netty 实现,Springboot2 中默认的 redis 客户端。那它是不是直接用 Netty 中的那几个 handler 来处理 RESP 协议的呢?一起看看吧。可以看到这里并没有 codec-redis 模块,所以 Lettuce 并没

今天来分享 Lettuce —— 基于 Netty 实现,Springboot2 中默认的 redis 客户端。0Fi28资讯网——每日最新资讯28at.com

那它是不是直接用 Netty 中的那几个 handler 来处理 RESP 协议的呢?一起看看吧。0Fi28资讯网——每日最新资讯28at.com

可以看到这里并没有 codec-redis 模块,所以 Lettuce 并没有使用 Netty 提供的 redis 模块。0Fi28资讯网——每日最新资讯28at.com

图片图片0Fi28资讯网——每日最新资讯28at.com

(⊙﹏⊙),问题解决得太快了,那就再来思考下,它是怎么做的呢?0Fi28资讯网——每日最新资讯28at.com

既然 Lettuce 基于 Netty 实现,那么它必然在 ChannelHandler 上动手脚,直接搜索可以发现有 9 个实现类。0Fi28资讯网——每日最新资讯28at.com

图片图片0Fi28资讯网——每日最新资讯28at.com

这里我关心的就是它怎么编解码,所以直接来看 CommandEncoder 和 CommandHandler 。0Fi28资讯网——每日最新资讯28at.com

打上断点,使用测试例子直接 debug。0Fi28资讯网——每日最新资讯28at.com

代码

@Test    void redisTest() {        // 创建 redis 客户端        RedisClient redisClient = RedisClient.create("redis://123456@192.168.200.128:6379/0");        // 创建 channel        StatefulRedisConnection<String, String> connection = redisClient.connect();        // 使用 sync 同步命令        RedisCommands<String, String> syncCommands = connection.sync();        String name = syncCommands.get("name");        System.out.println(name);//        syncCommands.set("key", "Hello, Redis!");        connection.close();        redisClient.shutdown();    }

刚开始时,要和服务器建立连接,发送数据,涉及到 encode 流程。0Fi28资讯网——每日最新资讯28at.com

CommandHandler

图片图片0Fi28资讯网——每日最新资讯28at.com

如图,直接来到 nioEventLoop 线程,并调用了 write 方法。0Fi28资讯网——每日最新资讯28at.com

write:382, CommandHandler (io.lettuce.core.protocol)

从右边可以看到,发了一个 HELLO 的命令出去,其中 CommandArgs 如下:0Fi28资讯网——每日最新资讯28at.com

CommandArgs [buffer=$13$4AUTH$7default$6123456]

CommandArgs⭐

直接来到 toString 方法,可以发现 encode 方法。0Fi28资讯网——每日最新资讯28at.com

图片图片0Fi28资讯网——每日最新资讯28at.com

如图,有 4 个 SingularArgument:0Fi28资讯网——每日最新资讯28at.com

图片图片0Fi28资讯网——每日最新资讯28at.com

看看他们是怎么 encode 的 。0Fi28资讯网——每日最新资讯28at.com

ProtocolKeywordArgument

图片图片0Fi28资讯网——每日最新资讯28at.com

StringArgument

图片图片0Fi28资讯网——每日最新资讯28at.com

对比 Netty

图片图片0Fi28资讯网——每日最新资讯28at.com

貌似没啥大的区别,可以看到 Lettuce 中,对 ByteBuf 的使用比较粗一些,Netty 中会计算这个 ByteBuf 的初始容量,而 Lettuce 就简单些处理,直接 singularArguments.size() * 10 。0Fi28资讯网——每日最新资讯28at.com

还有一个 大小端序 的处理,只能说 Netty 太细了。0Fi28资讯网——每日最新资讯28at.com

图片图片0Fi28资讯网——每日最新资讯28at.com

CommandEncoder

直接 F9 来到这一个断点。0Fi28资讯网——每日最新资讯28at.com

图片图片0Fi28资讯网——每日最新资讯28at.com

继续 debug ,会来到 Command 类,在这里完成对发送数据的 encode。0Fi28资讯网——每日最新资讯28at.com

图片图片0Fi28资讯网——每日最新资讯28at.com

解析下要发送的数据。0Fi28资讯网——每日最新资讯28at.com

图片图片0Fi28资讯网——每日最新资讯28at.com

小结

那么到了这里,我们就了解完 encode 的实现了。0Fi28资讯网——每日最新资讯28at.com

核心:CommandArgs 中的各种 SingularArgument0Fi28资讯网——每日最新资讯28at.com

图片图片0Fi28资讯网——每日最新资讯28at.com

下面就是接受服务器数据,进行 decode 的流程了。0Fi28资讯网——每日最新资讯28at.com

CommandHandler

来到 channelRead 。0Fi28资讯网——每日最新资讯28at.com

图片图片0Fi28资讯网——每日最新资讯28at.com

decode 时,会调用到 RedisStateMachine 的 decode ,它是这个流程的核心。0Fi28资讯网——每日最新资讯28at.com

图片图片0Fi28资讯网——每日最新资讯28at.com

RedisStateMachine⭐

Redis 状态机:0Fi28资讯网——每日最新资讯28at.com

图片图片0Fi28资讯网——每日最新资讯28at.com

这里我直接 copy 了一份 。0Fi28资讯网——每日最新资讯28at.com

static class State {    // Callback interface to handle a {@link State}.    @FunctionalInterface    interface StateHandler {        Result handle(RedisStateMachine rsm, State state, ByteBuf buffer, CommandOutput<?, ?, ?> output,                Consumer<Exception> errorHandler);    }    enum Type implements StateHandler {        SINGLE('+', RedisStateMachine::handleSingle),        ERROR('-', RedisStateMachine::handleError),        INTEGER(':', RedisStateMachine::handleInteger),        // 下面开始都是 @since 6.0/RESP3        FLOAT(',', RedisStateMachine::handleFloat),        BOOLEAN('#', RedisStateMachine::handleBoolean),        BULK_ERROR('!', RedisStateMachine::handleBulkError),        VERBATIM('=', RedisStateMachine::handleBulkAndVerbatim), VERBATIM_STRING('=', RedisStateMachine::handleVerbatim),        BIG_NUMBER('(', RedisStateMachine::handleBigNumber),        MAP('%', RedisStateMachine::handleMap),        SET('~', RedisStateMachine::handleSet),        ATTRIBUTE('|', RedisStateMachine::handleAttribute),        PUSH('>', RedisStateMachine::handlePushAndMulti),               HELLO_V3('@', RedisStateMachine::handleHelloV3),        NULL('_', RedisStateMachine::handleNull),        BULK('$', RedisStateMachine::handleBulkAndVerbatim),        MULTI('*', RedisStateMachine::handlePushAndMulti), BYTES('*', RedisStateMachine::handleBytes);        final byte marker;        private final StateHandler behavior;        Type(char marker, StateHandler behavior) {            this.marker = (byte) marker;            this.behavior = behavior;        }        @Override        public Result handle(RedisStateMachine rsm, State state, ByteBuf buffer, CommandOutput<?, ?, ?> output,                Consumer<Exception> errorHandler) {            return behavior.handle(rsm, state, buffer, output, errorHandler);        }    }    enum Result {        NORMAL_END, BREAK_LOOP, CONTINUE_LOOP    }    Type type = null;    int count = NOT_FOUND;    @Override    public String toString() {        final StringBuffer sb = new StringBuffer();        sb.append(getClass().getSimpleName());        sb.append(" [type=").append(type);        sb.append(", count=").append(count);        sb.append(']');        return sb.toString();    }}

继续 debug,会来到 doDecode 方法。0Fi28资讯网——每日最新资讯28at.com

这里有两个核心步骤:0Fi28资讯网——每日最新资讯28at.com

  1. 根据读取到的第一个字节,判断是不是 RESP3。
  2. 调用 状态机 中的 State.Type 枚举类,处理 handle。

图片0Fi28资讯网——每日最新资讯28at.com

这里先手动解析下服务器返回的数据。0Fi28资讯网——每日最新资讯28at.com

ByteBufUtil.decodeString(buffer,0,146, Charset.defaultCharset());
%7$6server$5redis$7version$66.0.12$5proto:3$2id:74$4mode$10standalone$4role$6master$7modules*0

handleMap

%7 对应的 handler 处理。0Fi28资讯网——每日最新资讯28at.com

图片图片0Fi28资讯网——每日最新资讯28at.com

后面就进入 状态机 流程判断了,上面我们拿到的数据要循环好久,就不一一列举出来了。0Fi28资讯网——每日最新资讯28at.com

$6 对应的 handler 处理。0Fi28资讯网——每日最新资讯28at.com

图片图片0Fi28资讯网——每日最新资讯28at.com

最后解析出来刚好 7 个,可以对比上面手动解析的结果验证下。0Fi28资讯网——每日最新资讯28at.com

图片图片0Fi28资讯网——每日最新资讯28at.com

小结

到了这里,decode 的流程也完毕了,画个图总结下

本文链接:http://www.28at.com/showinfo-26-89411-0.html基于 Netty 的 Lettuce 居然是这样解析RESP协议的

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: Java引用类型解析:掌握强引用、软引用、弱引用和幻象引用的妙用

下一篇: 我发现了大厂OpenApi接口的bug!你发现了吗?

标签:
  • 热门焦点
  • 一加Ace2 Pro官宣:普及16G内存 引领24G

    一加Ace2 Pro官宣:普及16G内存 引领24G

    一加官方今天继续为本月发布的新机一加Ace2 Pro带来预热,公布了内存方面的信息。“淘汰 8GB ,12GB 起步,16GB 普及,24GB 引领,还有呢?#一加Ace2Pro#,2023 年 8 月,敬请期待。”同时
  • MIX Fold3包装盒泄露 新机本月登场

    MIX Fold3包装盒泄露 新机本月登场

    小米的全新折叠屏旗舰MIX Fold3将于本月发布,近日该机的真机包装盒在网上泄露。从图上来看,新的MIX Fold3包装盒在外观设计方面延续了之前的方案,变化不大,这也是目前小米旗舰
  • 官方承诺:K60至尊版将会首批升级MIUI 15

    官方承诺:K60至尊版将会首批升级MIUI 15

    全新的MIUI 15今天也有了消息,在官宣了K60至尊版将会搭载天玑9200+处理器和独显芯片X7的同时,Redmi给出了官方承诺,K60至尊重大更新首批升级,会首批推送MIUI 15。也就是说虽然
  • 石头智能洗地机A10 Plus体验:双向自清洁治好了我的懒癌

    石头智能洗地机A10 Plus体验:双向自清洁治好了我的懒癌

    一、前言和介绍专为家庭请假懒人而生的石头科技在近日又带来了自己的全新旗舰新品,石头智能洗地机A10 Plus。从这个产品名上就不难看出,这次石头推出的并不是常见的扫地机器
  • 线程通讯的三种方法!通俗易懂

    线程通讯的三种方法!通俗易懂

    线程通信是指多个线程之间通过某种机制进行协调和交互,例如,线程等待和通知机制就是线程通讯的主要手段之一。 在 Java 中,线程等待和通知的实现手段有以下几种方式:Object 类下
  • 微信语音大揭秘:为什么禁止转发?

    微信语音大揭秘:为什么禁止转发?

    大家好,我是你们的小米。今天,我要和大家聊一个有趣的话题:为什么微信语音不可以转发?这是一个我们经常在日常使用中遇到的问题,也是一个让很多人好奇的问题。让我们一起来揭开这
  • 小红书1周涨粉49W+,我总结了小白可以用的N条涨粉笔记

    小红书1周涨粉49W+,我总结了小白可以用的N条涨粉笔记

    作者:黄河懂运营一条性教育视频,被54万人&ldquo;珍藏&rdquo;是什么体验?最近,情感博主@公主是用鲜花做的,火了!仅仅凭借一条视频,光小红书就有超过128万人,为她疯狂点赞!更疯狂的是,这
  • 自研Exynos回归!三星Galaxy S24系列将提供Exynos和骁龙双版本

    自研Exynos回归!三星Galaxy S24系列将提供Exynos和骁龙双版本

    年初,全新的三星Galaxy S23系列发布,包含Galaxy S23、Galaxy S23+和Galaxy S23 Ultra三个版本,全系搭载超频版骁龙8 Gen 2,虽同样采用台积电4nm工艺制
  • 2022爆款:ROG魔霸6 冰川散热系统持续护航

    2022爆款:ROG魔霸6 冰川散热系统持续护航

    喜逢开学季,各大商家开始推出自己的新产品,进行打折促销活动。对于忠实的端游爱好者来说,能够拥有一款梦寐以求的笔记本电脑是一件十分开心的事。但是现在的
Top