学习
实践
活动
工具
TVP
写文章
专栏首页MyJavaRPC项目记录二期 - Netty替换socket,实现网络传输,解编码器,序列化器

RPC项目记录二期 - Netty替换socket,实现网络传输,解编码器,序列化器

内容

使用netty替代原先的socket网络传输,使效率更高

优点

netty是nio,socket是bio。

其实就是nio比bio好在哪。

出现的问题

基础

需要实现commonEncoder,CommonDecoder,NettyClientHandler。

这里用的是责任链模式,要层层处理后才可以给下一层,这里需要实现解码器,编码器,数据处理器。

发送rpc请求

这里我们是使用channel进行发送的,因为这是非阻塞的,所以结果会直接返回,导致接受不到结果。

这里我们需要用到attributeKey,netty常用解决粘包的代码。

AttributeKey<RpcResponse> key = AttributeKey.valueOf("rpcResponse");
RpcResponse rpcResponse = channel.attr(key).get();

通过这种方式获得全局可见的返回结果,在获得返回结果 RpcResponse 后,将这个对象以 key 为 rpcResponse 放入 ChannelHandlerContext 中,这里就可以立刻获得结果并返回,我们会在 NettyClientHandler 中看到放入的过程。

自定义协议和解编码器

自定义协议

首先是 4 字节魔数,表识一个协议包。接着是 Package Type,标明这是一个调用请求还是调用响应,Serializer Type 标明了实际数据使用的序列化器,这个服务端和客户端应当使用统一标准;Data Length 就是实际数据的长度,设置这个字段主要防止粘包,最后就是经过序列化后的实际数据,可能是 RpcRequest 也可能是 RpcResponse 经过序列化后的字节,取决于 Package Type。

解编码器

1,编码器 commonEncoder

public class CommonEncoder extends MessageToByteEncoder {

    private static final int MAGIC_NUMBER = 0xCAFEBABE;

    private final CommonSerializer serializer;

    public CommonEncoder(CommonSerializer serializer) {
        this.serializer = serializer;
    }

    @Override
    protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception {
        out.writeInt(MAGIC_NUMBER);
        if(msg instanceof RpcRequest) {
            out.writeInt(PackageType.REQUEST_PACK.getCode());
        } else {
            out.writeInt(PackageType.RESPONSE_PACK.getCode());
        }
        out.writeInt(serializer.getCode());
        byte[] bytes = serializer.serialize(msg);
        out.writeInt(bytes.length);
        out.writeBytes(bytes);
    }
}

MessageToByteEncoder:就是将数据(要发送的对象)转化成字节。

encode:按照协议,将字段一个个传入管道里。

这里要注意的:实现编码器,必须要传入一个选定的序列化容器。

2,解码器 commonDecoder

public class CommonDecoder extends ReplayingDecoder {

    private static final Logger logger = LoggerFactory.getLogger(CommonDecoder.class);
    private static final int MAGIC_NUMBER = 0xCAFEBABE;

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        int magic = in.readInt();
        if(magic != MAGIC_NUMBER) {
            logger.error("不识别的协议包: {}", magic);
            throw new RpcException(RpcError.UNKNOWN_PROTOCOL);
        }
        int packageCode = in.readInt();
        Class<?> packageClass;
        if(packageCode == PackageType.REQUEST_PACK.getCode()) {
            packageClass = RpcRequest.class;
        } else if(packageCode == PackageType.RESPONSE_PACK.getCode()) {
            packageClass = RpcResponse.class;
        } else {
            logger.error("不识别的数据包: {}", packageCode);
            throw new RpcException(RpcError.UNKNOWN_PACKAGE_TYPE);
        }
        int serializerCode = in.readInt();
        CommonSerializer serializer = CommonSerializer.getByCode(serializerCode);
        if(serializer == null) {
            logger.error("不识别的反序列化器: {}", serializerCode);
            throw new RpcException(RpcError.UNKNOWN_SERIALIZER);
        }
        int length = in.readInt();
        byte[] bytes = new byte[length];
        in.readBytes(bytes);
        Object obj = serializer.deserialize(bytes, packageClass);
        out.add(obj);
    }

ReplayingDecoder :需要实现这个,也就是将序列化的字节码转换成对象。

这里逻辑很简单:从管道里取出每个字段,然后判断是否合理或存在(比如序列化器没有对应的反序列化器的话,会抛出异常)。

细节就是:先判断协议头是否合理,全部合理则把序列化数据反序列化完事。

序列化器

kyro不是线程安全的!所以我采用ThreadLocal方式的kyro。

json序列化器:

这里使用Jackson作为json序列化工具。

json反序列化因为是object,容易出现错误,所以需要写个新函数来一一对照。

/*
        这里由于使用JSON序列化和反序列化Object数组,无法保证反序列化后仍然为原实例类型
        需要重新判断处理
     */
    private Object handleRequest(Object obj) throws IOException {
        RpcRequest rpcRequest = (RpcRequest) obj;
        for(int i = 0; i < rpcRequest.getParamTypes().length; i ++) {
            Class<?> clazz = rpcRequest.getParamTypes()[i];
            if(!clazz.isAssignableFrom(rpcRequest.getParameters()[i].getClass())) {
                byte[] bytes = objectMapper.writeValueAsBytes(rpcRequest.getParameters()[i]);
                rpcRequest.getParameters()[i] = objectMapper.readValue(bytes, clazz);
            }
        }
        return rpcRequest;
    }

kryo序列化器:

kyro不是线程安全的!所以我采用ThreadLocal方式的kyro。

kyro不是线程安全的!所以我采用ThreadLocal方式的kyro。

kyro不是线程安全的!所以我采用ThreadLocal方式的kyro。

实现方法就是正常注册kryo就行了。

NettyServerHandler 和 NettyClientHandler

这里是netty的最顶部和最尾部,不需要和序列化接触,直接处理rpcrequest和rpcresponse就可以了。

NettyClientHandler把结果传入编码器就行。

NettyServerHandler把结果传出去就行。

本文参与 腾讯云自媒体分享计划 ,欢迎热爱写作的你一起参与!
本文分享自作者个人站点/博客:https://blog.syjhxy.ltd/复制
如有侵权,请联系 cloudcommunity@tencent.com 删除。
登录 后参与评论
0 条评论

相关文章

  • lagou 爪哇 3-1 分布式理论、架构设计(自定义RPC)笔记

    分布式系统概念 分布式系统是一个硬件或软件组件分布在不同的网络计算机上,彼此之间仅仅通过消息传递进行通信和协调的系统。俗的理解,所谓分布式系统,就是一个业务拆...

    acc8226
  • 【Netty】03-实战之序列化与反序列化协议

    客户端与服务器端通讯,不能将对象进行直接传输的。通讯的本质为流传输,所以,我们可以将对象序列化成流进行传输。

    envoke
  • 如何手撸一个较为完整的RPC框架?

    点击上方“芋道源码”,选择“设为星标” 管她前浪,还是后浪? 能浪的浪,才是好浪! 每天 10:33 更新文章,每天掉亿点点头发... 源码精品专栏 原创 |...

    芋道源码
  • 一篇文章了解RPC框架原理

    RPC(Remote Procedure Call)–远程过程调用,通过网络通信调用不同的服务,共同支撑一个软件系统,微服务实现的基石技术。使用RPC可以解耦系...

    烂猪皮
  • Netty-整合kryo高性能数据传输

    前言 本篇文章是Netty专题的第三篇,前面2篇文章如下: 高性能NIO框架Netty入门篇 高性能NIO框架Netty-对象传输 Netty 是 开源的基于j...

    猿天地
  • 一篇文章了解RPC框架原理

    RPC(Remote Procedure Call)–远程过程调用,通过网络通信调用不同的服务,共同支撑一个软件系统,微服务实现的基石技术。使用RPC可以解耦系...

    哲洛不闹
  • 七个步骤,带你快速读懂 RPC 框架原理

    RPC(Remote Procedure Call)–远程过程调用,通过网络通信调用不同的服务,共同支撑一个软件系统,微服务实现的基石技术。使用RPC可以解耦系...

    芋道源码
  • 一篇文章了解RPC框架原理

    Java高级架构
  • Go语言入门篇-gRPC基于golang & java简单实现

    server端stub又被称为skeleton(骨架)。可以理解为代理类。而实际上基于Java的RPC框架stub基本上也都是使用动态代理。

    用户7798898
  • 造个轮子之基于 Netty 实现自己的 RPC 框架

    服务端开发都会或多或少的涉及到 RPC 的使用,当然如果止步于会用,对自己的成长很是不利,所以楼主今天本着知其然,且知其所以然的精神来探讨一下 RPC 这个东西...

    haifeiWu
  • 深入理解RPC之传输篇

    RPC 被称为“远程过程调用”,表明了一个方法调用会跨越网络,跨越进程,所以传输层是不可或缺的。一说到网络传输,一堆名词就蹦了出来:TCP、UDP、HTTP,同...

    kirito-moe
  • 真的够可以的,基于Netty实现了PRC框架

    RPC全称Remote Procedure Call,即远程过程调用,对于调用者无感知这是一个远程调用功能。目前流行的开源RPC 框架有阿里的Dubbo、Goo...

    Java程序猿阿谷
  • 花了一个星期,我终于把RPC框架整明白了!

    RPC(Remote Procedure Call):远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的思想。

    范蠡
  • 花了快2个月!Guide自己动手写了一个简单的RPC框架!

    大概2个月前,我说过要利用业余时间写一个简单的 RPC 框架,今天总算将其开源出来,希望对小伙伴们有帮助。

    Guide哥
  • nio与netty编程(二)

    Netty 是由 JBOSS 提供的一个 Java 开源框架。Netty 提供异步的、基于事件驱动的网络应用程序框架,用以快速开发高性能、高可靠性的网络 IO ...

    周杰伦本人
  • 17-跨语言调用 Google ProtoBuf

    定义其他复杂类型参考:https://blog.csdn.net/lijingjingchn/article/details/89466437

    彼岸舞
  • RPC 实战总结与进阶延伸

    ByteBuf 是必须要掌握的核心工具类,并且能够理解 ByteBuf 的内部构造。ByteBuf 包含三个指针:读指针 readerIndex、写指针 wri...

    MickyInvQ
  • 如何实现一个简单的rpc

    为了实现一个自定义的rpc,如果想实现一个rpc,其本质是将远程调用可以和本地调用一样。而要实现这样的功能,首先我们需要一个解码器Decoder和一个编码器En...

    路行的亚洲
  • 五分钟学后端技术:如何学习Java工程师必须掌握的RPC

    本文转自https://developer.51cto.com/art/201906/597963.htm

    公众号_程序员黄小斜

扫码关注腾讯云开发者

领取腾讯云代金券