(这里做的解析不是很详细,等到走完整个流程再来解析)Dubbo中编解码的工作由Codec2接口的实现来处理,回想一下第一次接触到Codec2相关的内容是在服务端暴露服务的时候,根据具体的协议去暴露服务的步骤中,在DubboProtocol的createServer方法中:

1
2
3
4
5
6
7
8
9
10
11
private ExchangeServer createServer(URL url) {
。。。
//这里url会添加codec=dubbo
url = url.addParameter(Constants.CODEC_KEY, Version.isCompatibleVersion() ? COMPATIBLE_CODEC_NAME : DubboCodec.NAME);
ExchangeServer server;
try {
server = Exchangers.bind(url, requestHandler);
}
。。。
return server;
}

紧接着进入Exchangers.bind(url, requestHandler);

1
2
3
4
5
public static ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
//如果url中没有codec属性,就会添加codec=exchange
url = url.addParameterIfAbsent(Constants.CODEC_KEY, "exchange");
return getExchanger(url).bind(url, handler);
}

然后会继续进入HeaderExchanger的bind方法:

1
2
3
public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
return new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))));
}

在这里会创建一个DecodeHandler实例。继续跟踪Transporters的bind方法,会发现直接返回一个NettyServer实例,在NettyServer的父类AbstractEndpoint构造方法初始的时候,会根据url获取一个ChannelCodec,并将其赋值给codec存放到NettyServer的实例中。

我们先看下getChannelCodec(url);方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
protected static Codec2 getChannelCodec(URL url) {
//获取codecName,不存在的话,默认为telnet
String codecName = url.getParameter(Constants.CODEC_KEY, "telnet");
//先看下是不是Codec2的实现,是的话就根据SPI扩展机制获得Codec2扩展的实现
//我们这里默认使用的是DubboCountCodec
if (ExtensionLoader.getExtensionLoader(Codec2.class).hasExtension(codecName)) {
return ExtensionLoader.getExtensionLoader(Codec2.class).getExtension(codecName);
} else {
//如果不是Codec2的实现,就去查找Codec的实现
//然后使用CodecAdapter适配器类来转换成Codec2
return new CodecAdapter(ExtensionLoader.getExtensionLoader(Codec.class)
.getExtension(codecName));
}
}

这里返回的是Codec2,而Codec这个接口已经被标记为过时。到这里的话,在NettyServer中就会存在一个Codec2的实例了。

在继续往下看到NettyServer中的doOpen()方法,这里是使用Netty的逻辑打开服务并绑定监听服务的地方:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
protected void doOpen() throws Throwable {
NettyHelper.setNettyLoggerFactory();
ExecutorService boss = Executors.newCachedThreadPool(new NamedThreadFactory("NettyServerBoss", true));
ExecutorService worker = Executors.newCachedThreadPool(new NamedThreadFactory("NettyServerWorker", true));
ChannelFactory channelFactory = new NioServerSocketChannelFactory(boss, worker, getUrl().getPositiveParameter(Constants.IO_THREADS_KEY, Constants.DEFAULT_IO_THREADS));
bootstrap = new ServerBootstrap(channelFactory);

final NettyHandler nettyHandler = new NettyHandler(getUrl(), this);
channels = nettyHandler.getChannels();
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() {
//这里的getCodec方法获取到的codec就是在AbstractEndpoint中我们获取到的codec
//NettyCodecAdapter,适配器类
NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec() ,getUrl(), NettyServer.this);
ChannelPipeline pipeline = Channels.pipeline();
pipeline.addLast("decoder", adapter.getDecoder());//SimpleChannelUpstreamHandler
pipeline.addLast("encoder", adapter.getEncoder());//OneToOneEncoder
pipeline.addLast("handler", nettyHandler);
return pipeline;
}
});
// bind
channel = bootstrap.bind(getBindAddress());
}

这里就在Netty的pipeline中添加了编解码器。这里涉及到Netty的相关流程,可以先了解下Netty3服务端流程简介

decoder为解码器,是一个SimpleChannelUpstreamHandler,从Socket到Netty中的时候,需要解码,也就是服务提供端接收到消费者的请求的时候,需要解码。

encoder是编码器,是OneToOneEncoder,这个类实现了ChannelDownstreamHandler,从服务提供端发送给服务消费者的时候,需要编码。

nettyHandler实现了ChannelUpstreamHandler, ChannelDownstreamHandler两个,上下的时候都需要处理。

接收到服务消费者的请求的时候,会先执行decoder,然后执行nettyHandler。

发送给消费者的时候,会先执行nettyHandler,然后执行encoder。

dubbo协议头

协议头是16字节的定长数据:

  • 2字节short类型的Magic
  • 1字节的消息标志位

    • 5位序列化id
    • 1位心跳还是正常请求
    • 1位双向还是单向
    • 1位请求还是响应
  • 1字节的状态位

  • 8字节的消息id
  • 4字节数据长度

编码的过程

首先会判断是请求还是响应,代码在ExchangeCodec的encode方法:

1
2
3
4
5
6
7
8
9
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 {//telenet类型的
super.encode(channel, buffer, msg);
}
}

服务提供者对响应信息编码

在服务提供者端一般是对响应来做编码,所以这里重点看下encodeResponse。

encodeResponse:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
protected void encodeResponse(Channel channel, ChannelBuffer buffer, Response res) throws IOException {
try {
//序列化方式
//也是根据SPI扩展来获取,url中没指定的话默认使用hessian2
Serialization serialization = getSerialization(channel);
//长度为16字节的数组,协议头
byte[] header = new byte[HEADER_LENGTH];
//魔数0xdabb
Bytes.short2bytes(MAGIC, header);
//序列化方式
header[2] = serialization.getContentTypeId();
//心跳消息还是正常消息
if (res.isHeartbeat()) header[2] |= FLAG_EVENT;
//响应状态
byte status = res.getStatus();
header[3] = status;
//设置请求id
Bytes.long2bytes(res.getId(), header, 4);
//buffer为1024字节的ChannelBuffer
//获取buffer的写入位置
int savedWriteIndex = buffer.writerIndex();
//需要再加上协议头的长度之后,才是正确的写入位置
buffer.writerIndex(savedWriteIndex + HEADER_LENGTH);
ChannelBufferOutputStream bos = new ChannelBufferOutputStream(buffer);
ObjectOutput out = serialization.serialize(channel.getUrl(), bos);
// 对响应信息或者错误消息进行编码
if (status == Response.OK) {
if (res.isHeartbeat()) {
//心跳
encodeHeartbeatData(channel, out, res.getResult());
} else {
//正常响应
encodeResponseData(channel, out, res.getResult());
}
}
//错误消息
else out.writeUTF(res.getErrorMessage());
out.flushBuffer();
bos.flush();
bos.close();
//写出去的消息的长度
int len = bos.writtenBytes();
//查看消息长度是否过长
checkPayload(channel, len);
Bytes.int2bytes(len, header, 12);
//重置写入的位置
buffer.writerIndex(savedWriteIndex);
//向buffer中写入消息头
buffer.writeBytes(header); // write header.
//buffer写出去的位置从writerIndex开始,加上header长度,加上数据长度
buffer.writerIndex(savedWriteIndex + HEADER_LENGTH + len);
} catch (Throwable t) {
// 发送失败信息给Consumer,否则Consumer只能等超时了
if (! res.isEvent() && res.getStatus() != Response.BAD_RESPONSE) {
try {
// FIXME 在Codec中打印出错日志?在IoHanndler的caught中统一处理?
logger.warn("Fail to encode response: " + res + ", send bad_response info instead, cause: " + t.getMessage(), t);

Response r = new Response(res.getId(), res.getVersion());
r.setStatus(Response.BAD_RESPONSE);
r.setErrorMessage("Failed to send response: " + res + ", cause: " + StringUtils.toString(t));
channel.send(r);

return;
} catch (RemotingException e) {
logger.warn("Failed to send bad_response info back: " + res + ", cause: " + e.getMessage(), e);
}
}

// 重新抛出收到的异常
if (t instanceof IOException) {
throw (IOException) t;
} else if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else if (t instanceof Error) {
throw (Error) t;
} else {
throw new RuntimeException(t.getMessage(), t);
}
}
}

服务消费者对请求信息编码

消费者端暂先不做解析

解码的过程

服务提供者对请求消息的解码

decode方法一次只会解析一个完整的dubbo协议包,但是每次收到的协议包不一定是完整的,或者有可能是多个协议包。看下代码解析,首先看NettyCodecAdapter的内部类InternalDecoder的messageReceived方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
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 {
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);
//跳出循环,之后在finally中把message赋值给buffer保存起来,等到下次接收到数据包的时候会追加到buffer的后面
break;
} else {//有多个协议包,触发messageReceived事件
if (saveReaderIndex == message.readerIndex()) {
buffer = com.alibaba.dubbo.remoting.buffer.ChannelBuffers.EMPTY_BUFFER;
throw new IOException("Decode without read data.");
}
if (msg != null) {
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());
}
}

继续看codec.decode(channel, message);这里是DubboCountCodec的decode方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public Object decode(Channel channel, ChannelBuffer buffer) throws IOException {
//当前的读索引记录下来
int save = buffer.readerIndex();
//多消息
MultiMessage result = MultiMessage.create();
do {
//解码消息
Object obj = codec.decode(channel, buffer);
//不是完整的协议包
if (Codec2.DecodeResult.NEED_MORE_INPUT == obj) {
buffer.readerIndex(save);
break;
} else {//多个协议包
result.addMessage(obj);
logMessageLength(obj, buffer.readerIndex() - save);
save = buffer.readerIndex();
}
} while (true);
if (result.isEmpty()) {
return Codec2.DecodeResult.NEED_MORE_INPUT;
}
if (result.size() == 1) {
return result.get(0);
}
return result;
}

继续看ExchangeCodec的decode方法:

1
2
3
4
5
6
7
8
9
public Object decode(Channel channel, ChannelBuffer buffer) throws IOException {
//可读字节数
int readable = buffer.readableBytes();
byte[] header = new byte[Math.min(readable, HEADER_LENGTH)];
//协议头
buffer.readBytes(header);
//解码
return decode(channel, buffer, readable, header);
}

解码decode:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
protected Object decode(Channel channel, ChannelBuffer buffer, int readable, byte[] header) throws IOException {
//检查魔数.
if (readable > 0 && header[0] != MAGIC_HIGH
|| readable > 1 && header[1] != MAGIC_LOW) {
int length = header.length;
if (header.length < readable) {
header = Bytes.copyOf(header, readable);
buffer.readBytes(header, length, readable - length);
}
for (int i = 1; i < header.length - 1; i ++) {
if (header[i] == MAGIC_HIGH && header[i + 1] == MAGIC_LOW) {
buffer.readerIndex(buffer.readerIndex() - header.length + i);
header = Bytes.copyOf(header, i);
break;
}
}
//telenet
return super.decode(channel, buffer, readable, header);
}
//不完整的包
if (readable < HEADER_LENGTH) {
return DecodeResult.NEED_MORE_INPUT;
}

//数据长度
int len = Bytes.bytes2int(header, 12);
checkPayload(channel, len);

int tt = len + HEADER_LENGTH;
if( readable < tt ) {
return DecodeResult.NEED_MORE_INPUT;
}

// limit input stream.
ChannelBufferInputStream is = new ChannelBufferInputStream(buffer, len);

try {
//解码数据
return decodeBody(channel, is, header);
} finally {
if (is.available() > 0) {
try {
StreamUtils.skipUnusedStream(is);
} catch (IOException e) { }
}
}
}

decodeBody解析数据部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
protected Object decodeBody(Channel channel, InputStream is, byte[] header) throws IOException {
byte flag = header[2], proto = (byte) (flag & SERIALIZATION_MASK);
//获取序列化方式
Serialization s = CodecSupport.getSerialization(channel.getUrl(), proto);
//反序列化
ObjectInput in = s.deserialize(channel.getUrl(), is);
//获取请求id
long id = Bytes.bytes2long(header, 4);
//这里是解码响应数据
if ((flag & FLAG_REQUEST) == 0) {
//response的id设为来时候的Request的id,这样才能对上暗号
Response res = new Response(id);
//判断是什么类型请求
if ((flag & FLAG_EVENT) != 0) {
res.setEvent(Response.HEARTBEAT_EVENT);
}
//获取状态
byte status = header[3];
res.setStatus(status);
if (status == Response.OK) {
try {
Object data;
if (res.isHeartbeat()) {
//解码心跳数据
data = decodeHeartbeatData(channel, in);
} else if (res.isEvent()) {
//事件
data = decodeEventData(channel, in);
} else {
//响应
data = decodeResponseData(channel, in, getRequestData(id));
}
res.setResult(data);
} catch (Throwable t) {
res.setStatus(Response.CLIENT_ERROR);
res.setErrorMessage(StringUtils.toString(t));
}
} else {
res.setErrorMessage(in.readUTF());
}
return res;
} else {//这是解码请求数据
// request的id
Request req = new Request(id);
req.setVersion("2.0.0");
req.setTwoWay((flag & FLAG_TWOWAY) != 0);
if ((flag & FLAG_EVENT) != 0) {
req.setEvent(Request.HEARTBEAT_EVENT);
}
try {
Object data;
if (req.isHeartbeat()) {
//心跳
data = decodeHeartbeatData(channel, in);
} else if (req.isEvent()) {
//事件
data = decodeEventData(channel, in);
} else {
//请求
data = decodeRequestData(channel, in);
}
req.setData(data);
} catch (Throwable t) {
// bad request
req.setBroken(true);
req.setData(t);
}
return req;
}
}

具体的解码细节交给底层解码器,这里是使用的hessian2。

服务消费者对响应消息的解码

暂先不做解释。


Dubbo中编码和解码的解析的更多相关文章

  1. python3中编码与解码的问题

    python3中编码与解码的问题 ASCII .Unicode.UTF-8 ASCII 我们知道,在计算机内部,所有的信息最终都表示为一个二进制的字符串.每一个二进制位(bit)有0和1两种状态,因此 ...

  2. JS高级面试题思路(装箱和拆箱、栈和堆、js中sort()方法、.js中Date对象中的getMounth() 需要注意的、开发中编码和解码使用场景有哪些)

    1.装箱和拆箱: 装箱:把基本数据类型转化为对应的引用数据类型的操作: var num = 123 // num var objNum = new Num(123) // object console ...

  3. Dubbo中暴露服务的过程解析

    dubbo暴露服务有两种情况,一种是设置了延迟暴露(比如delay="5000"),另外一种是没有设置延迟暴露或者延迟设置为-1(delay="-1"): 设置 ...

  4. Dubbo中消费者初始化的过程解析

    首先还是Spring碰到dubbo的标签之后,会使用parseCustomElement解析dubbo标签,使用的解析器是dubbo的DubboBeanDefinitionParser,解析完成之后返 ...

  5. IO 流中编码和解码问题

    编码表 ASCII : American Standard Code for Information Interchange 使用一个字节的 7 位可以表示 ISO8859-1 : 拉丁码表. 欧洲码 ...

  6. 转 python3中SQLLIT编码与解码之Unicode与bytes

    #########sample########## sqlite3.OperationalError: Could not decode to UTF-8 column 'logtype' with ...

  7. ASP.Net中的编码与解码

    当javascript传递的参数中有中文时,服务端获得的将是乱码,此时需要用到编码和解码 javascript中编码与解码的三种方法 escape方法返回一个可在所有计算机上读取的编码 String ...

  8. java、js的编码、解码

    如果在地址栏挂载参数,特别是包含中文,往往要进行编码,取值时再解码,以下是java和js中编码.解码的各自方法. java: @Test public void test3() throws Unsu ...

  9. NET MVC全局异常处理(一) 【转载】网站遭遇DDoS攻击怎么办 使用 HttpRequester 更方便的发起 HTTP 请求 C#文件流。 Url的Base64编码以及解码 C#计算字符串长度,汉字算两个字符 2019周笔记(2.18-2.23) Mysql语句中当前时间不能直接使用C#中的Date.Now传输 Mysql中Count函数的正确使用

    NET MVC全局异常处理(一)   目录 .NET MVC全局异常处理 IIS配置 静态错误页配置 .NET错误页配置 程序设置 全局异常配置 .NET MVC全局异常处理 一直知道有.NET有相关 ...

随机推荐

  1. DB Query Analyzer 5.03 is distributed, EXCEL table name will be enclosed in square bracket

      DB Query Analyzer 5.03 is distributed, table name will be enclosed in square bracket automatically ...

  2. 计算机网络-TCP/IP HTTP Conclusion

    1.1OSI 与 TCP/IP 各层的结构 1.2 三次握手和四次挥手,TCP为什么三次握手,四次挥手 在第一次消息发送中,A随机选取一个序列号作为自己的初始序号发送给B:第二次消息B使用ack对A的 ...

  3. 多线程编程 NSOperation

     前言 1.NSThread的使用,虽然也可以实现多线程编程,但是需要我们去管理线程的生命周期,还要考虑线程同步.加锁问题,造成一些性能上的开销.我们也可以配合使用NSOperation和NSOper ...

  4. majority element(数组中找出出现次数最多的元素)

    Given an array of size n, find the majority element. The majority element is the element that appear ...

  5. 面试题:JQuery有几种选择器?

    很多种,大概归纳为9种. (1)基本 #id element .class * selector1,selector2,selectorN (2)层次选择器: ancestor descendant ...

  6. 手动编译Flume

    1.源码下载: 我用的是1.6版,因为加了kafka-sink,下载地址 http://www.apache.org/dyn/closer.cgi/flume/1.6.0/apache-flume-1 ...

  7. C#中的var和dynamic

    在理解var和dynamic关键字之前,让我们先了解一下编程语言的类别. C#中有两类编程语言: 静态类型语言类别 动态语言类别 静态类型语言 静态类型语言也被称为强类型语言.所谓强类型语言,通俗的讲 ...

  8. Angular集成UEditor

    1.Ueditor的集成主要通过把UEditor做成一个Component来实现,先上Component代码: import { AfterContentInit, Component, Input, ...

  9. mac port 清理

    http://popozhu.github.io/2014/10/27/mac-port-%E6%B8%85%E7%90%86/ 使用 mac port 来安装一些软件,时间久了后(也有两年多),更新 ...

  10. Windows10上搭建Kinect 2 开发环境

    因为Visual Studio 2017的应用最低只能面向windows10,而Kinect SDK 2.0的系统版本要求是windows 8,所以不得不下载Visual Studio 2013 co ...