Redis:redission 源代码剖析2 编码解码过程
来源:互联网 发布:安川 机器人 编程 技巧 编辑:程序博客网 时间:2024/06/17 06:28
在Netty和redission中都使用了java的public interface Future<V> 模式。
理解redission的核心流程关键在于Future模式,关键代码在RedisClient 构造函数中。
在RedisClient中初始化Channel的时候,用到了ConnectionWatchdog CommandEncoder CommandsListEncoder CommandsQueue CommandDecoder
ConnectionWatchdog 功能是实现了断线重连的功能。在上一篇博客中已经提及。
CommandEncoder CommandsListEncoder CommandsQueue 这三个类与写操作有关系。
CommandDecoder 与 读取数据有关系。
redis协议使用请求-回应模式, 而netty网络层使用了异步Nio模式,所以redission使用了Future 模式,使把请求和回应关联在一起。 每一个请求消息都关联了Promise .
当消息收到时,找到关联的promise . redission提供了异步和同步读写模式。
既然,redis协议采用了请求-回应模式。所以,在redission中我们先来看看写操作。
RedisConnection.send(CommandData<T,R>)
public <T, R> ChannelFuture send(CommandData<T, R> data) {
return channel.writeAndFlush(data);
}
当写数据时,会进入第一个CommandsQueue ChannelOutboundHandler .进行处理。
在CommandsQueue 处理的数据类型为QueueCommand 接口。
代码如下:
public interface QueueCommand { List<CommandData<Object, Object>> getPubSubOperations();}
CommandData 为QueueCommand接口的实现类。
public class CommandData<T, R> implements QueueCommand { //对写入数据的Future final Promise<R> promise; //redis命令 final RedisCommand<T> command; //命令参数 final Object[] params; //编码器 final Codec codec; //解码器 final MultiDecoder<Object> messageDecoder;}主要是把请求消息内容和响应消息相关联。因为redis 消息协议是请求-回应,这样来做一一对应。
public class CommandsQueue extends ChannelDuplexHandler { //在channel中管理的返回消息。 public static final AttributeKey<QueueCommand> REPLAY = AttributeKey.valueOf("promise"); //所有写出数据的队列 private final Queue<QueueCommandHolder> queue = PlatformDependent.newMpscQueue(); public void sendNextCommand(ChannelHandlerContext ctx) { ctx.channel().attr(CommandsQueue.REPLAY).remove(); queue.poll(); sendData(ctx); } //当redission写数据时,在这里首先被处理 @Override public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { if (msg instanceof QueueCommand) { QueueCommand data = (QueueCommand) msg; QueueCommandHolder holder = queue.peek(); if (holder != null && holder.getCommand() == data) { super.write(ctx, msg, promise); } else { //所有写出数据都被管理到queue队列中 queue.add(new QueueCommandHolder(data, promise)); sendData(ctx); } } else { super.write(ctx, msg, promise); } } private void sendData(final ChannelHandlerContext ctx) { QueueCommandHolder command = queue.peek(); if (command != null && command.getSended().compareAndSet(false, true)) { QueueCommand data = command.getCommand(); List<CommandData<Object, Object>> pubSubOps = data.getPubSubOperations(); if (!pubSubOps.isEmpty()) { for (CommandData<Object, Object> cd : pubSubOps) { for (Object channel : cd.getParams()) { ctx.pipeline().get(CommandDecoder.class).addChannel(channel.toString(), cd); } } } else { //在channel中把响应信息和请求信息相关联。 ctx.channel().attr(REPLAY).set(data); } command.getChannelPromise().addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { if (!future.isSuccess()) { sendNextCommand(ctx); } } }); ctx.channel().writeAndFlush(data, command.getChannelPromise()); } }}根据不通的编码器进行编码
public class CommandsListEncoder extends MessageToByteEncoder<CommandsData> { //根据不通的编码器进行编码 @Override protected void encode(ChannelHandlerContext ctx, CommandsData msg, ByteBuf out) throws Exception { for (CommandData<?, ?> commandData : msg.getCommands()) { ctx.pipeline().get(CommandEncoder.class).encode(ctx, (CommandData<Object, Object>)commandData, out); } }}
CommandEncoder 类才是Redis协议处理的地方。
/** * Redis protocol command encoder * * Code parts from Sam Pullara * * @author Nikita Koksharov * */public class CommandEncoder extends MessageToByteEncoder<CommandData<Object, Object>> {}
CommandDecoder 类是处理Redis协议的地方。
public class CommandDecoder extends ReplayingDecoder<State> {}
在解码中,首先获取响应信息管理的请求信息。
@Override protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception { //在解码时,首先获取在写数据管理的数据 QueueCommand data = ctx.channel().attr(CommandsQueue.REPLAY).get(); }
//这里处理对请求结果的回应 private void handleResult(CommandData<Object, Object> data, List<Object> parts, Object result, boolean multiResult, Channel channel) { if (data != null) { if (multiResult) { result = data.getCommand().getConvertor().convertMulti(result); } else { result = data.getCommand().getConvertor().convert(result); } } if (parts != null) { parts.add(result); } else { //在这里设置Promise 的结果 if (!data.getPromise().trySuccess(result) && data.getPromise().cause() instanceof RedisTimeoutException) { log.warn("response has been skipped due to timeout! channel: {}, command: {}, result: {}", channel, data, result); } } }
- Redis:redission 源代码剖析2 编码解码过程
- Redis:redission 源代码剖析1 连接建立
- Redis:redission 源代码剖析3 future模式
- Redis --- Redission客户端
- Redis: Jedis 源代码剖析2- 发布者/订阅者模式剖析
- JMVC 编码解码过程
- java编码解码过程
- C++ Base64编码/解码源代码
- Base64编码/解码(源代码)
- Redission--基于redis的分布式协调客户端
- base64编码解码存储过程
- Base64编码及解码程序源代码
- Base64编码解码〔源代码〕
- BASE64编码和解码(VC源代码)
- Base64编码及解码程序源代码
- BASE64编码和解码(VC源代码)
- BASE64编码和解码(VC源代码)
- 源代码——哈夫曼树的编码解码
- jquery 音乐添加
- nginx缓存映射在内存中的结点的生命周期
- listView的加载更多
- 一个图像高斯模糊的处理类
- java文件上传和下载
- Redis:redission 源代码剖析2 编码解码过程
- HDU 1248 寒冰王座 完全背包
- GameKit的蓝牙开发代码总结
- Ajax跨域访问
- adb 使用问题笔记
- ubuntu15.04 C客户端操作mysql
- 对话框里面有EdiText,而EdiText获取焦点之后,软件盘会盖住对话框,解决问题
- struts2的基本配置
- js图片轮播手动切换