12-netty基础-手写rpc-编解码-04

netty系列文章:

|------------------------------------------------------------------------------------------------------------------------------------|
| 01-netty基础-socket |
| 02-netty基础-java四种IO模型 |
| 03-netty基础-多路复用select、poll、epoll |
| 04-netty基础-Reactor三种模型 |
| 05-netty基础-ByteBuf数据结构 |
| 06-netty基础-编码解码 |
| 07-netty基础-自定义编解码器 |
| 08-netty基础-自定义序列化和反序列化 |
| 09-netty基础-手写rpc-原理-01 |
| 10-netty基础-手写rpc-定义协议头-02 |
| 11-netty基础-手写rpc-支持多序列化协议-03 |
| 12-netty基础-手写rpc-编解码-04 |
| 13-netty基础-手写rpc-消费方生成代理-05 |
| 14-netty基础-手写rpc-提供方(服务端)-06 |

1 自定义编辑码

编解码都采用原生的ByteBuf,分别为MessageToByteEncoder、ByteToMessageDecoder;解决了拆包、粘包问题

编码:将需要发送的数据封装成RpcProtocol 形式进行发送

解码:将接收到的数据解释成RpcProtocol形式然后处理相应的业务逻辑

2 代码

2.1 编码

复制代码
package com.bonnie.protocol.code;

import com.alibaba.fastjson.JSONObject;
import com.bonnie.protocol.core.Header;
import com.bonnie.protocol.core.RpcProtocol;
import com.bonnie.protocol.serializer.ISerializer;
import com.bonnie.protocol.serializer.SerializerManager;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
import lombok.extern.slf4j.Slf4j;

/**
 * 编码
 */
@Slf4j
public class BonnieEncoder extends MessageToByteEncoder<RpcProtocol<Object>> {

    @Override
    protected void encode(ChannelHandlerContext channelHandlerContext, RpcProtocol<Object> msg, ByteBuf out) throws Exception {
        log.info("============begin BonnieEncoder=========");

        Header header = msg.getHeader();
        // 魔数
        out.writeShort(header.getMagic());
        // 序列化类型
        out.writeByte(header.getSerialType());
        // 消息类型
        out.writeByte(header.getReqType());
        // 请求id
        out.writeLong(header.getRequestId());

        // 消息体序列化
        ISerializer serializer = SerializerManager.getSerializer(header.getSerialType());
        byte[] contentByteArray = serializer.serialize(msg.getContent());

        System.out.println("body长度"+contentByteArray.length);
        // 消息体长度,4个字节
        out.writeInt(contentByteArray.length);
        System.out.println("发送数据:"+JSONObject.toJSONString(msg));
        // 写入消息体
        out.writeBytes(contentByteArray);
    }

}

2.2 解码

复制代码
package com.bonnie.protocol.code;

import com.bonnie.protocol.core.Header;
import com.bonnie.protocol.core.RpcProtocol;
import com.bonnie.protocol.core.RpcRequest;
import com.bonnie.protocol.core.RpcResponse;
import com.bonnie.protocol.enums.ReqTypeEnum;
import com.bonnie.protocol.enums.RpcConstant;
import com.bonnie.protocol.serializer.ISerializer;
import com.bonnie.protocol.serializer.SerializerManager;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import lombok.extern.slf4j.Slf4j;

import java.util.List;
import java.util.Objects;

/**
 * 解码
 */
@Slf4j
public class BonnieDecoder extends ByteToMessageDecoder {

    @Override
    protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf in, List<Object> out) throws Exception {
        log.info("========begin BonnieDecoder==========");

        // 首先判断可读的字节是否小于头的长度,如果小于,说明没有body数据,甚至数据有问题,不解码
        if (in.readableBytes()<= RpcConstant.HEAD_TOTOAL_LEN) {
            return;
        }

        // 标记读取开始索引
        in.markReaderIndex();
        // 魔数
        short magic = in.readShort();
        if (!Objects.equals(magic, RpcConstant.MAGIC)) {
            throw new IllegalArgumentException("Illegal request parameter 'magic',"+magic);
        }
        // 序列化类型
        byte serialType = in.readByte();
        // 消息类型
        byte reqType = in.readByte();
        // 请求id
        long requestId = in.readLong();
        // 报文长度
        int dataLength = in.readInt();

        // 可读字节是否小于body的长度,如果小于,则不读取,并且重置到读指针的地方,等下一次读
        if(in.readableBytes()<dataLength) {
            in.resetReaderIndex();
            return;
        }

        // 消息体
        byte[] bodyByteArray = new byte[dataLength];
        // body内容读取到body中
        in.readBytes(bodyByteArray);

        // 封装头信息
        Header header = new Header();
        header.setMagic(magic);
        header.setSerialType(serialType);
        header.setReqType(reqType);
        header.setRequestId(requestId);
        header.setLength(dataLength);

        // 拿到对应的序列化
        ISerializer serializer = SerializerManager.getSerializer(serialType);
        /**
         * 根据请求类型,比如客户端发送数据,就是REQUWST,服务端给客户端回复数据就是RESPONSE,当然都是
         * 相对的,每一段都会发送REQUEST请求,每一段也会发送RESPONSE请求
         */
        ReqTypeEnum reqTypeEnum = ReqTypeEnum.findByCode(reqType);
        switch (reqTypeEnum) {
            // 如果是请求报文  反序列化得到数据,封装数据,继续传递
            case REQUEST:
                RpcProtocol rpcProtocol = dealRequest(bodyByteArray, serializer, header);
                out.add(rpcProtocol);
                break;
            case RESPONSE:
                RpcProtocol rpcProtocolResponse = dealResponse(bodyByteArray, serializer, header);
                out.add(rpcProtocolResponse);
                break;
            case HEARTBEAT:
                // TODO
                break;

        }

    }

    private RpcProtocol dealResponse(byte[] bodyByteArray, ISerializer serializer, Header header) {
        RpcResponse rpcResponse = serializer.deserialize(bodyByteArray, RpcResponse.class);
        RpcProtocol<RpcResponse> rpcProtocol = new RpcProtocol<>();
        rpcProtocol.setHeader(header);
        rpcProtocol.setContent(rpcResponse);
        return rpcProtocol;
    }

    private RpcProtocol dealRequest(byte[] bodyByteArray, ISerializer serializer, Header header) {
        RpcRequest rpcRequest = serializer.deserialize(bodyByteArray, RpcRequest.class);
        RpcProtocol<RpcRequest> rpcProtocol = new RpcProtocol<>();
        rpcProtocol.setHeader(header);
        rpcProtocol.setContent(rpcRequest);
        return rpcProtocol;
    }

}