上文抓包分析了这个 Redis RESP 协议,看到 TCP 层面的 byte 信息,直观感受到这个 Human Readble 的二进制协议。
比如 bulk String 的格式就是:$<length>\r\n<data>\r\n
那么,剩下的特点就是 Simple to implement 和 Fast to parse。
刚好,Netty 中就有个现成的轮子,这节就一起来看看吧~
RESP2协议
Netty中的Redis模块
可以看到,整体代码并不多。
核心就在 RedisEncoder 和 RedisDecoder 上,其他就是针对这个 RESP 协议的 model。
这节是请求篇,对应 RedisEncoder ,相对比较简单,我们直接从 test 模块中找个例子来看。
RedisEncoderTest
这个代表 redis-cli ,负责发请求到这个 redis-server。
这里我摘抄了其中的 shouldEncodeSimpleString 例子,简单字符串的格式是:+OK\r\n
public class RedisEncoderTest {
private EmbeddedChannel channel;
@BeforeEach
public void setup() throws Exception {
channel = new EmbeddedChannel(new RedisEncoder());
}
@AfterEach
public void teardown() throws Exception {
assertFalse(channel.finish());
}
@Test
public void shouldEncodeSimpleString() {
RedisMessage msg = new SimpleStringRedisMessage("simple");
// 看这里
boolean result = channel.writeOutbound(msg);
assertThat(result, is(true));
ByteBuf written = readAll(channel);
assertThat(bytesOf(written), is(bytesOf("+simple\r\n")));
written.release();
}
private static ByteBuf readAll(EmbeddedChannel channel) {
ByteBuf buf = Unpooled.buffer();
ByteBuf read;
while ((read = channel.readOutbound()) != null) {
buf.writeBytes(read);
read.release();
}
return buf;
}
}
图解
上面的核心流程大致如下
- 创建 EmbeddedChannel,并添加 RedisEncoder 这个 handler
- 创建 SimpleStringRedisMessage 简单字符串类型的消息
- 调用 writeOutbound 方法,实际会调用 handler 中的 encode 方法
这里画了个简略图
下面开始源码解读
创建EmbeddedChannel
内嵌型的 Channel ,常见的还有 NioServerSockerChannel 等。
添加 RedisEncoder
new EmbeddedChannel(new RedisEncoder());
创建好这个通道后,给它添加 RedisEncoder 这个 Handler
这里的核心方法在 writeRedisMessage , 下面再聊。
创建 SimpleStringRedisMessage
// 创建一个简单类型的字符串消息
// 简单字符串的格式是:`+OK\r\n`
RedisMessage msg = new SimpleStringRedisMessage("simple");
主要提供 content 方法来获取这个字符串消息。
writeOutbound
实际去调用各个 OutboundHandler 上的 encode 方法。
这里调用到 RedisEncoder 中的 encode 方法,
writeSimpleStringMessage
多一个 RedisMessageType.SIMPLE_STRING 参数。
RedisMessageType
一个枚举类,对应RESP2协议
writeString
创建 ByteBuf ,并初始化长度。
这里用 utf8 去编码这个 content,取最大值就是 3 * 6 = 18 的长度。再加上这个 +
1 个长度,EOL 两个长度,初始化时就是 21 的长度。
RedisConstants 如下
最终,写入了 9 个字节。
到了这里, channel.writeOutbound(msg);
就执行完了。
剩下就是 ByteBuf 的主场了 👇
执行 ByteBuf written = readAll(channel);
把数据读取到 ByteBuf 中,和 "+simple\r\n"
进行比较。
assertThat(bytesOf(written), is(bytesOf("+simple\r\n")));
结尾
RedisEncoder 的小例子到这就结束了。
大家也可以换成下面这种,模拟常见的请求场景测试一下。
上面的例子更像是服务器对客户端的应答。
RedisMessage msg = new InlineCommandRedisMessage("get name");
暂无评论内容