服务端请求解码总体流程:
1 NettyCodecAdapter$InternalDecoder.decode(ChannelHandlerContext ctx, ByteBuf input, List<Object> out) 2 -->new NettyBackedChannelBuffer(ByteBuf buffer) // 创建一个buffer 3 -->NettyChannel.getOrAddChannel(io.netty.channel.Channel ch, URL url, ChannelHandler handler) 4 -->DubboCountCodec.decode(Channel channel, ChannelBuffer buffer) 5 -->ExchangeCodec.decode(Channel channel, ChannelBuffer buffer) 6 -->buffer.readBytes(header); //读取header byte[] 7 -->decode(Channel channel, ChannelBuffer buffer, int readable, byte[] header) 8 -->检查魔数、检查总长度是否大于等于16 9 -->获取请求体长度 10 -->new ChannelBufferInputStream(buffer, len) 11 -->DubboCodec.decodeBody(Channel channel, InputStream is, byte[] header) 12 -->CodecSupport.getSerialization(URL url, Byte id) //解析出请求头header[2]中的序列化ID,根据该ID获取与请求编码相同的序列化协议 13 -->Bytes.bytes2long(header, 4) //获取requestID 14 <!-- 之后创建一个新的Request对象,将requestID及后续解析出来的各种request属性塞入该对象中 --> 15 -->new DecodeableRpcInvocation(channel, req, is, proto) 16 -->DecodeableRpcInvocation.decode() 17 -->decode(Channel channel, InputStream input) //解析请求体参数并将其构造为一个DecodeableRpcInvocation,最终塞到Request对象的data属性中 18 -->new Hessian2ObjectInput(InputStream is) 19 -->反序列化:in.readObject()
总体流程:
- 包装请求传过来的ByteBuf为NettyBackedChannelBuffer(简称buffer)
- 从buffer中读取header
- 之后检查魔数、检查header+请求体body总长度是否大于等于16
- 获取请求体body长度
- 解析出请求头header[2]中的序列化ID,根据该ID获取与请求编码相同的序列化协议
- 获取requestID
- 创建Request对象,将requestID及后续解析出来的各种request属性塞入该对象中
- 反序列化请求体body,并将其设在DecodeableRpcInvocation中,最后该对象设在Request对象的data属性中
解码还是在NettyCodecAdapter中:
1 private class InternalDecoder extends ByteToMessageDecoder { 2 protected void decode(ChannelHandlerContext ctx, ByteBuf input, List<Object> out) throws Exception { 3 //获取ChannelBuffer 4 ChannelBuffer message = new NettyBackedChannelBuffer(input); 5 //获取NettyChannel 6 NettyChannel channel = NettyChannel.getOrAddChannel(ctx.channel(), url, handler); 7 Object msg; 8 int saveReaderIndex; 9 10 try { 11 do { 12 saveReaderIndex = message.readerIndex(); 13 try { 14 //解码message 15 msg = codec.decode(channel, message); 16 } catch (IOException e) { 17 throw e; 18 } 19 // 如果接收到的消息发生了拆包,则仅仅设置message的readerIndex为当前的saveReaderIndex 20 if (msg == Codec2.DecodeResult.NEED_MORE_INPUT) { 21 message.readerIndex(saveReaderIndex); 22 break; 23 } else { 24 //is it possible to go here ?(没有读到任何数据) 25 if (saveReaderIndex == message.readerIndex()) { 26 throw new IOException("Decode without read data."); 27 } 28 // 如果读到了正常的消息,写入List<Object> out 29 if (msg != null) { 30 out.add(msg); 31 } 32 } 33 } while (message.readable()); 34 } finally { 35 NettyChannel.removeChannelIfDisconnected(ctx.channel()); 36 } 37 } 38 }
一、创建ChannelBuffer
1 ChannelBuffer message = new NettyBackedChannelBuffer(input);
与客户端请求编码类似,最终的得到的message:
1 NettyBackedChannelBuffer 2 -->ByteBuf buffer = SimpleLeakAwareByteBuf 3 -->ByteBuf buf = PooledUnsafeDirectByteBuf
二、获取NettyChannel
之后从获取io.netty.channel实例,然后包装在NettyChannel中。
1 NettyChannel channel = NettyChannel.getOrAddChannel(ctx.channel(), url, handler);
与服务端请求解码类似,最终的得到的channel:
1 -->Channel channel = NioSocketChannel 2 -->ChannelHandler handler = NettyServer 3 -->URL url =dubbo://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&bind.ip=10.10.10.10&bind.port=20880&channel.readonly.sent=true&codec=dubbo&default.server=netty4&dubbo=2.0.0&generic=false&heartbeat=60000&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=7294&side=provider×tamp=1515031737563
三、进行解码
这里的codec是:
1 Codec2 codec = 2 DubboCountCodec 3 -->DubboCodec codec = new DubboCodec()
DubboCountCodec:
1 public Object decode(Channel channel, ChannelBuffer buffer) throws IOException { 2 int save = buffer.readerIndex(); 3 MultiMessage result = MultiMessage.create(); 4 do { 5 Object obj = codec.decode(channel, buffer); 6 if (Codec2.DecodeResult.NEED_MORE_INPUT == obj) {// 如果发生了拆包,则跳出,在下一次依旧从save处读取 7 buffer.readerIndex(save); 8 break; 9 } else { 10 result.addMessage(obj);// 如果消息正常,添加消息到MultiMessage的List messages中 11 logMessageLength(obj, buffer.readerIndex() - save); 12 save = buffer.readerIndex(); 13 } 14 } while (true); 15 if (result.isEmpty()) { 16 return Codec2.DecodeResult.NEED_MORE_INPUT; 17 } 18 if (result.size() == 1) { 19 return result.get(0); 20 } 21 return result; 22 }
MultiMessage:
1 private final List messages = new ArrayList(); 2 3 public void addMessage(Object msg) { 4 messages.add(msg); 5 }
DubboCodec的父类ExchangeCodec:
1 public Object decode(Channel channel, ChannelBuffer buffer) throws IOException { 2 int readable = buffer.readableBytes();// 获取buffer所有的可读字节(header + body) 3 byte[] header = new byte[Math.min(readable, HEADER_LENGTH)]; 4 buffer.readBytes(header);// 将buffer中的前16个字节读入header 5 return decode(channel, buffer, readable, header);// 反序列化请求体body,构造成DecodeableRpcResult,塞入Request的data属性中 6 }
1 protected Object decode(Channel channel, ChannelBuffer buffer, int readable, byte[] header) throws IOException { 2 // check magic number. 3 if (readable > 0 && header[0] != MAGIC_HIGH 4 || readable > 1 && header[1] != MAGIC_LOW) {//魔数不匹配 5 int length = header.length; 6 if (header.length < readable) { 7 header = Bytes.copyOf(header, readable); 8 buffer.readBytes(header, length, readable - length); 9 } 10 for (int i = 1; i < header.length - 1; i++) { 11 if (header[i] == MAGIC_HIGH && header[i + 1] == MAGIC_LOW) { 12 buffer.readerIndex(buffer.readerIndex() - header.length + i); 13 header = Bytes.copyOf(header, i); 14 break; 15 } 16 } 17 return super.decode(channel, buffer, readable, header); 18 } 19 // check length. 20 if (readable < HEADER_LENGTH) {//header+body的总可读数据<16 21 return DecodeResult.NEED_MORE_INPUT; 22 } 23 24 // 从header中获取body长度 25 int len = Bytes.bytes2int(header, 12); 26 checkPayload(channel, len);//检测body是否超8M了 27 28 int tt = len + HEADER_LENGTH; 29 if (readable < tt) {// 如果当前可读的消息<header+body总长度(说明发生了拆包) 30 return DecodeResult.NEED_MORE_INPUT; 31 } 32 33 // limit input stream. 34 ChannelBufferInputStream is = new ChannelBufferInputStream(buffer, len); 35 36 try { 37 return decodeBody(channel, is, header);//解码body 38 } finally { 39 if (is.available() > 0) { 40 try { 41 if (logger.isWarnEnabled()) { 42 logger.warn("Skip input stream " + is.available()); 43 } 44 StreamUtils.skipUnusedStream(is); 45 } catch (IOException e) { 46 logger.warn(e.getMessage(), e); 47 } 48 } 49 } 50 }
DubboCodec:
1 protected Object decodeBody(Channel channel, InputStream is, byte[] header) throws IOException { 2 byte flag = header[2], proto = (byte) (flag & SERIALIZATION_MASK);// proto:序列化方式ID 3 Serialization s = CodecSupport.getSerialization(channel.getUrl(), proto);// 根据序列化方式ID获取序列化方式 4 // get request id. 5 long id = Bytes.bytes2long(header, 4);// 获取reqID 6 if ((flag & FLAG_REQUEST) == 0) { 7 ...... 8 return res; 9 } else { 10 // decode request. 11 Request req = new Request(id); 12 req.setVersion("2.0.0"); 13 req.setTwoWay((flag & FLAG_TWOWAY) != 0); 14 if ((flag & FLAG_EVENT) != 0) { 15 req.setEvent(Request.HEARTBEAT_EVENT); 16 } 17 try { 18 Object data; 19 if (req.isHeartbeat()) { 20 data = decodeHeartbeatData(channel, deserialize(s, channel.getUrl(), is)); 21 } else if (req.isEvent()) { 22 data = decodeEventData(channel, deserialize(s, channel.getUrl(), is)); 23 } else { 24 DecodeableRpcInvocation inv; 25 if (channel.getUrl().getParameter( 26 Constants.DECODE_IN_IO_THREAD_KEY, 27 Constants.DEFAULT_DECODE_IN_IO_THREAD)) { 28 inv = new DecodeableRpcInvocation(channel, req, is, proto); 29 inv.decode();// 解码请求体 30 } else { 31 inv = new DecodeableRpcInvocation(channel, req, 32 new UnsafeByteArrayInputStream(readMessageData(is)), proto); 33 } 34 data = inv; 35 } 36 req.setData(data); 37 } catch (Throwable t) { 38 if (log.isWarnEnabled()) { 39 log.warn("Decode request failed: " + t.getMessage(), t); 40 } 41 // bad request 42 req.setBroken(true); 43 req.setData(t); 44 } 45 return req; 46 } 47 }
就是构造Request参数,重点构造其中的data属性(实际上是一个DecodeableRpcInvocation实例)
DecodeableRpcInvocation:
1 public void decode() throws Exception { 2 if (!hasDecoded && channel != null && inputStream != null) { 3 try { 4 decode(channel, inputStream); 5 } catch (Throwable e) { 6 if (log.isWarnEnabled()) { 7 log.warn("Decode rpc invocation failed: " + e.getMessage(), e); 8 } 9 request.setBroken(true); 10 request.setData(e); 11 } finally { 12 hasDecoded = true; 13 } 14 } 15 }
1 public Object decode(Channel channel, InputStream input) throws IOException { 2 ObjectInput in = CodecSupport.getSerialization(channel.getUrl(), serializationType) 3 .deserialize(channel.getUrl(), input);// 创建Hessian2ObjectInput 4 //下边的读取顺序与序列化时的必须一模一样(我们反序列化"dubbo"=2.0.0时,offset=0, 反序列化"path"=xxx时,offset=6) 5 setAttachment(Constants.DUBBO_VERSION_KEY, in.readUTF()); 6 setAttachment(Constants.PATH_KEY, in.readUTF()); 7 setAttachment(Constants.VERSION_KEY, in.readUTF()); 8 9 setMethodName(in.readUTF()); 10 try { 11 Object[] args; 12 Class<?>[] pts; 13 String desc = in.readUTF(); 14 if (desc.length() == 0) { 15 pts = DubboCodec.EMPTY_CLASS_ARRAY; 16 args = DubboCodec.EMPTY_OBJECT_ARRAY; 17 } else { 18 pts = ReflectUtils.desc2classArray(desc); 19 args = new Object[pts.length]; 20 for (int i = 0; i < args.length; i++) { 21 try { 22 args[i] = in.readObject(pts[i]); 23 } catch (Exception e) { 24 if (log.isWarnEnabled()) { 25 log.warn("Decode argument failed: " + e.getMessage(), e); 26 } 27 } 28 } 29 } 30 setParameterTypes(pts); 31 32 Map<String, String> map = (Map<String, String>) in.readObject(Map.class); 33 if (map != null && map.size() > 0) { 34 Map<String, String> attachment = getAttachments(); 35 if (attachment == null) { 36 attachment = new HashMap<String, String>(); 37 } 38 attachment.putAll(map); 39 setAttachments(attachment); 40 } 41 //decode argument ,may be callback 42 for (int i = 0; i < args.length; i++) { 43 args[i] = decodeInvocationArgument(channel, this, pts, i, args[i]); 44 } 45 46 setArguments(args); 47 48 } catch (ClassNotFoundException e) { 49 throw new IOException(StringUtils.toString("Read invocation data failed.", e)); 50 } 51 return this; 52 }
上述的setXXX方法,实际上就是为当前的DecodeableRpcInvocation设置各种属性,in.readUTF()和in.readobject都是反序列化的方法,前者将byte[]反序列化为String,后者将byte[]反序列化为Object。
到此为止,服务端请求解码就结束了。