Dubbo中編碼和解碼的解析
(這裡做的解析不是很詳細,等到走完整個流程再來解析)Dubbo中編解碼的工作由Codec2介面的實現來處理,回想一下第一次接觸到Codec2相關的內容是在服務端暴露服務的時候,根據具體的協議去暴露服務的步驟中,在DubboProtocol的createServer方法中:
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);
:
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方法:
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);
方法:
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的邏輯開啟服務並繫結監聽服務的地方:
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方法:
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:
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方法:
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方法:
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方法:
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:
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解析資料部分:
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。
服務消費者對響應訊息的解碼
暫先不做解釋。
相關文章
- Java 8中的Base64編碼和解碼Java
- Dubbo原始碼分析(六)Dubbo通訊的編碼解碼機制原始碼
- ==和is的區別 以及編碼和解碼
- python中字串的編碼和解碼Python字串
- [java IO流]之編碼和解碼Java
- Huffman對檔案編碼和解碼
- dubbo原始碼解析-spi(五)原始碼
- Dubbo原始碼解析之SPI原始碼
- Dubbo服務暴露原始碼解析②原始碼
- PHP安全的URL字串base64編碼和解碼PHP字串
- 簡述小資料池,編碼和解碼
- 【興百放】Asp.Net 編碼和解碼ASP.NET
- js實現的字串簡單編碼和解碼程式碼例項JS字串
- dubbo原始碼解析之負載均衡原始碼負載
- Dubbo原始碼解析之SPI機制原始碼
- dubbo原始碼解析之基礎篇原始碼
- js encode64編碼和解碼程式碼例項JS
- 結合例項學習|字元編碼和解碼字元
- 聊聊Dubbo – Dubbo可擴充套件機制原始碼解析套件原始碼
- springweb開發中編碼亂碼問題解析SpringWeb
- js自定義實現的簡單編碼和解碼程式碼例項JS
- Dubbo原始碼之動態編譯原始碼編譯
- Dubbo原始碼解析之服務叢集原始碼
- Dubbo原始碼解析之負載均衡策略原始碼負載
- dubbo原始碼解析之ExtensionLoader類(二)原始碼
- Dubbo服務呼叫過程原始碼解析④原始碼
- android Java BASE64編碼和解碼一:基礎AndroidJava
- Netty 中的訊息解析和編解碼器Netty
- LevelDB 原始碼解析之 Varint 編碼原始碼
- Dubbo原始碼解析之服務呼叫過程原始碼
- Dubbo原始碼解析之服務引入過程原始碼
- Dubbo原理和原始碼解析之服務引用原始碼
- netty系列之:自定義編碼和解碼器要注意的問題Netty
- Dubbo2.7.3版本原始碼學習系列六: Dubbo服務匯出原始碼解析原始碼
- python中的編碼&解碼Python
- Netty原始碼深度解析(九)-編碼Netty原始碼
- Dubbo原始碼解析之服務匯出過程原始碼
- dubbo原始碼解析(三十)遠端呼叫——rest協議原始碼REST協議