在之前的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也就不能彻底的搞清楚这些产品.
公众号