• 12.2 服务端请求解码


    服务端请求解码总体流程

     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&timestamp=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。

    到此为止,服务端请求解码就结束了。

  • 相关阅读:
    webRTC中语音降噪模块ANS细节详解(四)
    基于MCRAOMLSA的语音降噪(三):实现(续)
    基于MCRAOMLSA的语音降噪(一):原理
    VoIP语音处理流程和知识点梳理
    linux I/O内存访问
    十六、输入子系统驱动模型
    十三、【ADC】ADC读取S5p6818电源值
    put_user()和get_user()用户空间传递数据
    十四、【watchdog】看门狗
    十七、内核中的锁机制
  • 原文地址:https://www.cnblogs.com/java-zhao/p/8179119.html
Copyright © 2020-2023  润新知