redisson实现分布式锁底层通信与协议

3 篇文章 0 订阅

在之前的Netty在Redis客户端中的应用文章中我们介绍了Netty在Redisson中的使用.

在之前的jedis实现分布式锁底层通信与协议文章中我们介绍了关于Redis客户端与服务器通信的协议.

那么,在本篇文章我们介绍下Redisson中如何实现客户端与服务器通信协议的.

阅读本篇文章之前请先阅读上面的两篇文章

在创建Redisson的过程中会创建一个org.redisson.client.RedisClient类,在这个类的内部会创建一个io.netty.bootstrap.Bootstrap类,熟悉Netty的童鞋,以及看过我上面两篇文章的童鞋,会对Bootstrap类不陌生.

RedisClient类内部创建Bootstrap的代码如下

private Bootstrap createBootstrap(RedisClientConfig config, Type type) {
  Bootstrap bootstrap = new Bootstrap()
    .resolver(config.getResolverGroup())
    .channel(config.getSocketChannelClass())
    .group(config.getGroup());

  // #1
  bootstrap.handler(new RedisChannelInitializer(bootstrap, config, this, channels, type));
  bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, config.getConnectTimeout());
  bootstrap.option(ChannelOption.SO_KEEPALIVE, config.isKeepAlive());
  bootstrap.option(ChannelOption.TCP_NODELAY, config.isTcpNoDelay());
  config.getNettyHook().afterBoostrapInitialization(bootstrap);
  return bootstrap;
}

对以上代码感到陌生的童鞋可以先阅读下我的Netty文章系列或者部分文章

我们关注下RedisChannelInitializer这个类,进入到这个类,在它的里面有个org.redisson.client.handler.RedisChannelInitializer#initChannel方法

protected void initChannel(Channel ch) throws Exception {
  initSsl(config, ch);

  if (type == Type.PLAIN) {
    ch.pipeline().addLast(new RedisConnectionHandler(redisClient));
  } else {
    ch.pipeline().addLast(new RedisPubSubConnectionHandler(redisClient));
  }

  ch.pipeline().addLast(
    connectionWatchdog,
    CommandEncoder.INSTANCE,// 编码器
    CommandBatchEncoder.INSTANCE,
    new CommandsQueue());

  if (pingConnectionHandler != null) {
    ch.pipeline().addLast(pingConnectionHandler);
  }

  if (type == Type.PLAIN) {
    // 解码器
    ch.pipeline().addLast(new CommandDecoder(config.getExecutor(), config.isDecodeInExecutor()));
  } else {
    ch.pipeline().addLast(new CommandPubSubDecoder(config.getExecutor(), config.isKeepPubSubOrder(), config.isDecodeInExecutor()));
  }

  ch.pipeline().addLast(new ErrorsLoggingHandler());

  config.getNettyHook().afterChannelInitialization(ch);
}

有一个CommandEncoder编码器,它的作用就是客户端在向服务器发送命令的时候,会由这个编码器将命令依据Redis协议编码之后再发送出去.

在CommandEncoder类内部有个encode方法

// CommandEncoder类的属性
private static final char ARGS_PREFIX = '*';
private static final char BYTES_PREFIX = '$';
private static final byte[] CRLF = "\r\n".getBytes();
// encode方法.根据名字也可以知道它的作用
protected void encode(ChannelHandlerContext ctx, CommandData<?, ?> msg, ByteBuf out) throws Exception {
  try {
    out.writeByte(ARGS_PREFIX);
    int len = 1 + msg.getParams().length;
    if (msg.getCommand().getSubName() != null) {
      len++;
    }
    out.writeCharSequence(Long.toString(len), CharsetUtil.US_ASCII);
    out.writeBytes(CRLF);

    writeArgument(out, msg.getCommand().getName().getBytes(CharsetUtil.UTF_8));
    if (msg.getCommand().getSubName() != null) {
      writeArgument(out, msg.getCommand().getSubName().getBytes(CharsetUtil.UTF_8));
    }

    for (Object param : msg.getParams()) {
      ByteBuf buf = encode(param);
      writeArgument(out, buf);
      if (!(param instanceof ByteBuf)) {
        buf.release();
      }
    }

    if (log.isTraceEnabled()) {
      String info = out.toString(CharsetUtil.UTF_8);
      if (RedisCommands.AUTH.equals(msg.getCommand())) {
        info = info.substring(0, info.indexOf(RedisCommands.AUTH.getName()) + RedisCommands.AUTH.getName().length()) + "(password masked)";
      }
      log.trace("channel: {} message: {}", ctx.channel(), info);
    }
  } catch (Exception e) {
    msg.tryFailure(e);
    throw e;
  }
}

Redisson通过Netty框架,框架中使用了一个编码器,而这个编码器的功能与之前我们介绍的jedis通过原生socket向服务器发送命令是一样的,并没有本质区别.

在Dubbo和RocketMQ框架中也使用了Netty作为通信框架,在它的内部也有对应的编码器,当然也有解码器.通过编解码器实现相应的协议.客户端与服务器之间通过协议’明白’对方说的内容.

不管是Dubbo,RocketMQ,Redisson,Arthas等一系列产品,在它们的底层都是(或者绝大多数)通过Netty进行网络通信,因为不懂Netty也就不能彻底的搞清楚这些产品.


个人站点
语雀

公众号

微信公众号

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值