Serialization
com.alibaba.dubbo.common.serialize.Serialization 是传输数据的序列化和反序列化的组件,首先来看看这个SPI类
@SPI("hessian2")
public interface Serialization {
byte getContentTypeId();
String getContentType();
@Adaptive
ObjectOutput serialize(URL url, OutputStream output) throws IOException;
@Adaptive
ObjectInput deserialize(URL url, InputStream input) throws IOException;
}
从注解上看序列化的默认组件采用hassian2
从配置文件里看,hassian2的实现类是
com.alibaba.dubbo.common.serialize.support.hessian.Hessian2Serialization
那么序列化和反序列化的动作是在哪里触发的呢,我们后面再讲Serialization的使用契机。
NettyClient
我们直接来到客户端启动与服务端创建连接的入口:com.alibaba.dubbo.remoting.transport.netty.NettyClient#doOpen
NettyClient是在DubboProtocol执行refer的时候根据URL初始化的,即客户端与服务端调用的传输层网络通信方式;
它的初始化调用链路:
com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol#refer
↓
com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol#getClients
↓
com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol#getSharedClient
↓
com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol#initClient
↓
com.alibaba.dubbo.remoting.exchange.Exchangers#connect(com.alibaba.dubbo.common.URL, com.alibaba.dubbo.remoting.exchange.ExchangeHandler)
↓
com.alibaba.dubbo.remoting.transport.netty.NettyTransporter#connect
最终会实例化一个NettyClient对象
他的默认类关系图
以下是netty客户端启动并连接服务端的代码,这里注入了一个 NettyCodecAdapter,
protected void doOpen() throws Throwable {
NettyHelper.setNettyLoggerFactory();
bootstrap = new ClientBootstrap(channelFactory);
// config
// @see org.jboss.netty.channel.socket.SocketChannelConfig
bootstrap.setOption("keepAlive", true);
bootstrap.setOption("tcpNoDelay", true);
bootstrap.setOption("connectTimeoutMillis", getTimeout());
//NettyHandler 这里只是将this做了一个包装,它的作用类似一个包装器,真正做事情的还是this
final NettyHandler nettyHandler = new NettyHandler(getUrl(), this);
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() {
NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyClient.this);
ChannelPipeline pipeline = Channels.pipeline();
//向pipleline里注册handler
pipeline.addLast("decoder", adapter.getDecoder());
pipeline.addLast("encoder", adapter.getEncoder());
pipeline.addLast("handler", nettyHandler);
return pipeline;
}
});
}
NettyCodecAdapter
NettyCodecAdapter的构造函数有三个参数
public NettyCodecAdapter(Codec2 codec, URL url, com.alibaba.dubbo.remoting.ChannelHandler handler) {
this.codec = codec;
this.url = url;
this.handler = handler;
int b = url.getPositiveParameter(Constants.BUFFER_KEY, Constants.DEFAULT_BUFFER_SIZE);
this.bufferSize = b >= Constants.MIN_BUFFER_SIZE && b <= Constants.MAX_BUFFER_SIZE ? b : Constants.DEFAULT_BUFFER_SIZE;
}
Codec2也是一个SPI接口,第二个是URL类型,第三个是netty的ChannelHandler;Codec2对象是在NettyClient父类AbstractEndpoint 的构造函数执行的时候初始化的
protected static Codec2 getChannelCodec(URL url) {
String codecName = url.getParameter(Constants.CODEC_KEY, "telnet");
if (ExtensionLoader.getExtensionLoader(Codec2.class).hasExtension(codecName)) {
return ExtensionLoader.getExtensionLoader(Codec2.class).getExtension(codecName);
} else {
return new CodecAdapter(ExtensionLoader.getExtensionLoader(Codec.class).getExtension(codecName));
}
}
从代码可以看出Codec2对象是通过URL的codecName参数集合SPI机制获得的,以dubbo协议为例子这里会获得一个DubboCountCodec类型的Code2对象,而在DubboCountCodec类里实例化的时候会实例化一个DubboCodec的对象,而这个对象会做encode和decode的相关工作,DubboCountCodec只是对DubboCodec做了一层装饰,附加了记录日志等其他功能。
回到NettyCodecAdapter,在客户端启动的时候一共向PipleLine里注入了3个handler:
前面两个分别注入了“decoder”"encoder"两个handler,处理传输数据的序列化和反序列化就是两个了(最终处理者是Codec2的对象);
第三个handler主要是处理一些建立连接,关闭连接相关的事情(NettyClient也是ChannelHandler的子类,从上面的类继承关系图可以看出来)。
DubboCodec
先看看DubboCodec的类关系图
序列化
DubboCountCodec在接收到encode的调用时,会调用DubboCodec的encode方法(其实是父类ExchangeCodec的方法),这里会区分是请求的数据还是响应的数据而走不同的分支;
public void encode(Channel channel, ChannelBuffer buffer, Object msg) throws IOException {
if (msg instanceof Request) {
//客户端发送请求,数据会被包装成Request,消费端做数据的编码
encodeRequest(channel, buffer, (Request) msg);
} else if (msg instanceof Response) {
//服务端响应消费端的请求,响应数据被包装成Response,服务端做数据编码
encodeResponse(channel, buffer, (Response) msg);
} else {
super.encode(channel, buffer, msg);
}
}
这两个方法中都会去获得一个 Serialization 对象(后续方法就不分析了,序列化主要就是数据协议转换相关的操作):
Serialization serialization = getSerialization(channel); 最终调用的是:
public static Serialization getSerialization(URL url) {
return ExtensionLoader.getExtensionLoader(Serialization.class).getExtension(url.getParameter(Constants.SERIALIZATION_KEY, Constants.DEFAULT_REMOTING_SERIALIZATION));
}
如果参数中指定了 serialization 配置,则通过该配置获得 Serialization对象,否则获得默认的 hessian2对应的Hessian2Serialization;然后调用Serialization对象的 serialize 方法;
public class Hessian2Serialization implements Serialization {
public static final byte ID = 2;
public byte getContentTypeId() {
return ID;
}
public String getContentType() {
return "x-application/hessian2";
}
//将OutputStream输出流包装成一个Hessian2ObjectOutput对象
public ObjectOutput serialize(URL url, OutputStream out) throws IOException {
return new Hessian2ObjectOutput(out);
}
//将InputStream输入流对象包装成一个Hessian2ObjectOutput对象
public ObjectInput deserialize(URL url, InputStream is) throws IOException {
return new Hessian2ObjectInput(is);
}
}
Hessian2ObjectOutput类是一个适配器,适配的类是Hessian2Output,主要做序列话相关的动作;有兴趣研究Hessian协议的可以看看Hessian2Output里序列化和反序列化的代码(不忍直视,大部分是位计算);
而Hessian2ObjectInput 类似,适配了Hessian2Input这个类。这个类主要做反序列化相关的动作。
反序列化
反序列化是在接收到消息后由handler发起,NettyCodecAdapter会实例化它的内部类
com.alibaba.dubbo.remoting.transport.netty.NettyCodecAdapter.InternalDecoder,这个decoder继承自SimpleChannelUpstreamHandler,主要负责了dubbo协议的解析、数据包半包、粘包等情况的处理
private class InternalDecoder extends SimpleChannelUpstreamHandler {
private com.alibaba.dubbo.remoting.buffer.ChannelBuffer buffer =
com.alibaba.dubbo.remoting.buffer.ChannelBuffers.EMPTY_BUFFER;
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent event) throws Exception {
Object o = event.getMessage();
if (!(o instanceof ChannelBuffer)) {
ctx.sendUpstream(event);
return;
}
ChannelBuffer input = (ChannelBuffer) o;
int readable = input.readableBytes();
if (readable <= 0) {
return;
}
com.alibaba.dubbo.remoting.buffer.ChannelBuffer message;
if (buffer.readable()) {
if (buffer instanceof DynamicChannelBuffer) {
buffer.writeBytes(input.toByteBuffer());
message = buffer;
} else {
int size = buffer.readableBytes() + input.readableBytes();
message = com.alibaba.dubbo.remoting.buffer.ChannelBuffers.dynamicBuffer(
size > bufferSize ? size : bufferSize);
message.writeBytes(buffer, buffer.readableBytes());
message.writeBytes(input.toByteBuffer());
}
} else {
message = com.alibaba.dubbo.remoting.buffer.ChannelBuffers.wrappedBuffer(
input.toByteBuffer());
}
NettyChannel channel = NettyChannel.getOrAddChannel(ctx.getChannel(), url, handler);
Object msg;
int saveReaderIndex;
try {
// decode object.
do {
saveReaderIndex = message.readerIndex();
try {
msg = codec.decode(channel, message);
} catch (IOException e) {
buffer = com.alibaba.dubbo.remoting.buffer.ChannelBuffers.EMPTY_BUFFER;
throw e;
}
if (msg == Codec2.DecodeResult.NEED_MORE_INPUT) {
message.readerIndex(saveReaderIndex);
break;
} else {
if (saveReaderIndex == message.readerIndex()) {
buffer = com.alibaba.dubbo.remoting.buffer.ChannelBuffers.EMPTY_BUFFER;
throw new IOException("Decode without read data.");
}
if (msg != null) {
DefaultChannelPipeline a;
Channels.fireMessageReceived(ctx, msg, event.getRemoteAddress());
}
}
} while (message.readable());
} finally {
if (message.readable()) {
message.discardReadBytes();
buffer = message;
} else {
buffer = com.alibaba.dubbo.remoting.buffer.ChannelBuffers.EMPTY_BUFFER;
}
NettyChannel.removeChannelIfDisconnected(ctx.getChannel());
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
ctx.sendUpstream(e);
}
}
待续...