• sofa-bolt源码阅读(4)-Protocol


    5. Protocol

    5.1 序列化

    SerializerManager负责管理序列化类,默认起作用的是HessianSerializer

    HessianSerializer实现了序列化接口Serializer,有两个方法

    1. 序列化

      byte[] serialize(final Object obj) throws CodecException;
      
    2. 反序列化

      <T> T deserialize(final byte[] data, String classOfT) throws CodecException;
      

    直接看实现

    public class HessianSerializer implements Serializer {
    
            private SerializerFactory serializerFactory = new SerializerFactory();
    
        /** 
         * @see com.alipay.remoting.serialization.Serializer#serialize(java.lang.Object)
         */
        @Override
        public byte[] serialize(Object obj) throws CodecException {
            ByteArrayOutputStream byteArray = new ByteArrayOutputStream();
            Hessian2Output output = new Hessian2Output(byteArray);
            output.setSerializerFactory(serializerFactory);
            try {
                output.writeObject(obj);
                output.close();
            } catch (IOException e) {
                throw new CodecException("IOException occurred when Hessian serializer encode!", e);
            }
    
            return byteArray.toByteArray();
        }
    
        /**
         * 
         * @see com.alipay.remoting.serialization.Serializer#deserialize(byte[], java.lang.String)
         */
        @SuppressWarnings("unchecked")
        @Override
        public <T> T deserialize(byte[] data, String classOfT) throws CodecException {
            Hessian2Input input = new Hessian2Input(new ByteArrayInputStream(data));
            input.setSerializerFactory(serializerFactory);
            Object resultObject;
            try {
                resultObject = input.readObject();
                input.close();
            } catch (IOException e) {
                throw new CodecException("IOException occurred when Hessian serializer decode!", e);
            }
            return (T) resultObject;
        }
    
    }
    

    Hessian的文档 http://hessian.caucho.com/doc/hessian-serialization.html

    例如

    对象RequestBody

    序列化结果为

    5.2 编码

    Bolt的编码处理器是ProtocolCodeBasedEncoder,当发送数据时,会调用encode方法

    protected void encode(ChannelHandlerContext ctx, Serializable msg, ByteBuf out)
                                                                                   throws Exception {
        Attribute<ProtocolCode> att = ctx.channel().attr(Connection.PROTOCOL);
        ProtocolCode protocolCode;
        if (att == null || att.get() == null) {
            protocolCode = this.defaultProtocolCode;
        } else {
            protocolCode = att.get();
        }
        Protocol protocol = ProtocolManager.getProtocol(protocolCode);
        protocol.getEncoder().encode(ctx, msg, out);
    }  
    

    该编码器并没有对数据进行直接处理,而是交给Protocol类处理

    Protocol有两个实现类,可以在配置项或者连接的URL中指定协议的版本号。上面两个实现类的protocolCode分别是1和2。协议可以创建编码器和解码器。默认的协议号是1,因此默认的编码器是RpcCommandEncoder。

    编码的代码如下:

    public void encode(ChannelHandlerContext ctx, Serializable msg, ByteBuf out) throws Exception {
        try {
            if (msg instanceof RpcCommand) {
                /*
                 * ver: version for protocol
                 * type: request/response/request oneway
                 * cmdcode: code for remoting command
                 * ver2:version for remoting command
                 * requestId: id of request
                 * codec: code for codec
                 * (req)timeout: request timeout.
                 * (resp)respStatus: response status
                 * classLen: length of request or response class name
                 * headerLen: length of header
                 * cotentLen: length of content
                 * className
                 * header
                 * content
                 */
                RpcCommand cmd = (RpcCommand) msg;
                out.writeByte(RpcProtocol.PROTOCOL_CODE);
                out.writeByte(cmd.getType());
                out.writeShort(((RpcCommand) msg).getCmdCode().value());
                out.writeByte(cmd.getVersion());
                out.writeInt(cmd.getId());
                out.writeByte(cmd.getSerializer());
                if (cmd instanceof RequestCommand) {
                    //timeout
                    out.writeInt(((RequestCommand) cmd).getTimeout());
                }
                if (cmd instanceof ResponseCommand) {
                    //response status
                    ResponseCommand response = (ResponseCommand) cmd;
                    out.writeShort(response.getResponseStatus().getValue());
                }
                out.writeShort(cmd.getClazzLength());
                out.writeShort(cmd.getHeaderLength());
                out.writeInt(cmd.getContentLength());
                if (cmd.getClazzLength() > 0) {
                    out.writeBytes(cmd.getClazz());
                }
                if (cmd.getHeaderLength() > 0) {
                    out.writeBytes(cmd.getHeader());
                }
                if (cmd.getContentLength() > 0) {
                    out.writeBytes(cmd.getContent());
                }
            } else {
                String warnMsg = "msg type [" + msg.getClass() + "] is not subclass of RpcCommand";
                logger.warn(warnMsg);
            }
        } catch (Exception e) {
            logger.error("Exception caught!", e);
            throw e;
        }
    }
    

    抓包结果分析

    01 --ver

    01 --request,表示请求

    00 01 -- cmdcode

    01 --ver2

    00 00 00 01 -- requestId

    01 -- codec

    00 00 0b b8 -- timeout (=3000)

    00 2a -- classLen(=42)

    00 00 -- headerLen(=0)

    00 00 00 cc -- cotentLen(=204)

    63 6f 6d 2e 61 6c 69 70 61 79 2e 72 65 6d 6f 74 69 6e 67 2e

    72 70 63 2e 63 6f 6d 6d 6f 6e 2e 52 65 71 75 65 73 74 42 6f

    64 79 -- className(com.alipay.remoting.rpc.common.RequestBody)

    4f ba ... -- content (序列化的对象)

    5.3 解码

    Bolt解码处理器是ProtocolCodeBasedDecoder

    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        in.markReaderIndex();
        //获取协议号ver
        ProtocolCode protocolCode = decodeProtocolCode(in);
        if (null != protocolCode) {
            byte protocolVersion = decodeProtocolVersion(in);
            if (ctx.channel().attr(Connection.PROTOCOL).get() == null) {
                ctx.channel().attr(Connection.PROTOCOL).set(protocolCode);
                if (DEFAULT_ILLEGAL_PROTOCOL_VERSION_LENGTH != protocolVersion) {
                    ctx.channel().attr(Connection.VERSION).set(protocolVersion);
                }
            }
            Protocol protocol = ProtocolManager.getProtocol(protocolCode);
            if (null != protocol) {
                in.resetReaderIndex();
                protocol.getDecoder().decode(ctx, in, out);
            } else {
                throw new CodecException("Unknown protocol code: [" + protocolCode
                                         + "] while decode in ProtocolDecoder.");
            }
        }
    }
    

    与编码一样交给Protocol来处理

    直接看解码的实现类RpcCommandDecoder的decode方法

    @Override
    public void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        // the less length between response header and request header
        if (in.readableBytes() >= lessLen) {
            in.markReaderIndex();
            byte protocol = in.readByte();
            in.resetReaderIndex();
            if (protocol == RpcProtocol.PROTOCOL_CODE) {
                /*
                 * ver: version for protocol
                 * type: request/response/request oneway
                 * cmdcode: code for remoting command
                 * ver2:version for remoting command
                 * requestId: id of request
                 * codec: code for codec
                 * (req)timeout: request timeout
                 * (resp)respStatus: response status
                 * classLen: length of request or response class name
                 * headerLen: length of header
                 * contentLen: length of content
                 * className
                 * header
                 * content
                 */
                if (in.readableBytes() > 2) {
                    in.markReaderIndex();
                    in.readByte(); //version
                    byte type = in.readByte(); //type
                    if (type == RpcCommandType.REQUEST || type == RpcCommandType.REQUEST_ONEWAY) {
                        //decode request
                        if (in.readableBytes() >= RpcProtocol.getRequestHeaderLength() - 2) {
                            short cmdCode = in.readShort();
                            byte ver2 = in.readByte();
                            int requestId = in.readInt();
                            byte serializer = in.readByte();
                            int timeout = in.readInt();
                            short classLen = in.readShort();
                            short headerLen = in.readShort();
                            int contentLen = in.readInt();
                            byte[] clazz = null;
                            byte[] header = null;
                            byte[] content = null;
                            if (in.readableBytes() >= classLen + headerLen + contentLen) {
                                if (classLen > 0) {
                                    clazz = new byte[classLen];
                                    in.readBytes(clazz);
                                }
                                if (headerLen > 0) {
                                    header = new byte[headerLen];
                                    in.readBytes(header);
                                }
                                if (contentLen > 0) {
                                    content = new byte[contentLen];
                                    in.readBytes(content);
                                }
                            } else {// not enough data
                                in.resetReaderIndex();
                                return;
                            }
                            RequestCommand command;
                            if (cmdCode == CommandCode.HEARTBEAT_VALUE) {
                                command = new HeartbeatCommand();
                            } else {
                                command = createRequestCommand(cmdCode);
                            }
                            command.setType(type);
                            command.setVersion(ver2);
                            command.setId(requestId);
                            command.setSerializer(serializer);
                            command.setTimeout(timeout);
                            command.setClazz(clazz);
                            command.setHeader(header);
                            command.setContent(content);
                            out.add(command);
    
                        } else {
                            in.resetReaderIndex();
                        }
                    } else if (type == RpcCommandType.RESPONSE) {
                        //decode response
                        if (in.readableBytes() >= RpcProtocol.getResponseHeaderLength() - 2) {
                            short cmdCode = in.readShort();
                            byte ver2 = in.readByte();
                            int requestId = in.readInt();
                            byte serializer = in.readByte();
                            short status = in.readShort();
                            short classLen = in.readShort();
                            short headerLen = in.readShort();
                            int contentLen = in.readInt();
                            byte[] clazz = null;
                            byte[] header = null;
                            byte[] content = null;
                            if (in.readableBytes() >= classLen + headerLen + contentLen) {
                                if (classLen > 0) {
                                    clazz = new byte[classLen];
                                    in.readBytes(clazz);
                                }
                                if (headerLen > 0) {
                                    header = new byte[headerLen];
                                    in.readBytes(header);
                                }
                                if (contentLen > 0) {
                                    content = new byte[contentLen];
                                    in.readBytes(content);
                                }
                            } else {// not enough data
                                in.resetReaderIndex();
                                return;
                            }
                            ResponseCommand command;
                            if (cmdCode == CommandCode.HEARTBEAT_VALUE) {
    
                                command = new HeartbeatAckCommand();
                            } else {
                                command = createResponseCommand(cmdCode);
                            }
                            command.setType(type);
                            command.setVersion(ver2);
                            command.setId(requestId);
                            command.setSerializer(serializer);
                            command.setResponseStatus(ResponseStatus.valueOf(status));
                            command.setClazz(clazz);
                            command.setHeader(header);
                            command.setContent(content);
                            command.setResponseTimeMillis(System.currentTimeMillis());
                            command.setResponseHost((InetSocketAddress) ctx.channel()
                                .remoteAddress());
                            out.add(command);
                        } else {
                            in.resetReaderIndex();
                        }
                    } else {
                        String emsg = "Unknown command type: " + type;
                        logger.error(emsg);
                        throw new RuntimeException(emsg);
                    }
                }
    
            } else {
                String emsg = "Unknown protocol: " + protocol;
                logger.error(emsg);
                throw new RuntimeException(emsg);
            }
    
        }
    }
    

    解码时根据type的类型,将字节流分装成RequestCommand或ResponseCommand的对象。

    5.4 流程分析

    服务器的数据流转过程总结一下:

    1. decoder将字节流解码成RequestCommand或ResponseCommand的对象
    2. idleStateHandler更新read时间
    3. serverIdleHandler未接收到空闲事件,pass
    4. connectionEventHandler,pass
    5. handler根据协议号获取commandHandler
    6. commandHandler根据msg(第1步保证肯定是RpcCommand的实现类)的cmdCode获取具体的命令处理器RemotingProcessor。
      • 心跳 HeartbeatCommand -> RpcHeartBeatProcessor
      • 请求 RpcRequestCommand-> RpcRequestProcessor
      • 响应 RpcResponseCommand -> RpcResponseProcessor
    7. 以请求RpcRequestCommand为例,RpcRequestProcessor根据requestClass获取具体的UserProcessor,处理自定义业务后封装响应为RpcResponseCommand 并写入到channel中
    8. connectionEventHandler,pass
    9. serverIdleHandler未接收到空闲事件,pass
    10. idleStateHandler更新write时间
    11. encoder将对象编码成字节流发送给客户端
  • 相关阅读:
    [转] Akka实战:构建REST风格的微服务
    [转] Node.js的线程和进程
    [转] Spring Integration 系统集成
    NodeJS使用SSL证书
    Tomcat SSL证书安装配置
    [转]【NODE】用WS模块创建加密的WS服务(WSS)
    [转] Spring Boot实战之Filter实现使用JWT进行接口认证
    [转] 前后端分离之JWT用户认证
    [转] 使用 Java8 Optional 的正确姿势
    [转] SpringBoot RESTful 应用中的异常处理小结
  • 原文地址:https://www.cnblogs.com/huiyao/p/12424421.html
Copyright © 2020-2023  润新知