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


在解码完成后,来设置Promise的结果。

    //这里处理对请求结果的回应    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);            }        }    }









0 0
原创粉丝点击