资讯专栏INFORMATION COLUMN

如何实现一个分布式RPC框架

Vultr / 653人阅读

摘要:趁实习前的这段业余时间,我实现了一个轻量级的分布式框架,名字叫做,代码量不大,但是麻雀虽小却五脏俱全。目前支持和两种序列化框架。使用实现服务注册与发现功能。代码实现是我学习验证过程中诞生的一个轻量级分布式框架,代码放在了。

远程过程调用(Remote Procedure CallRPC)是一个计算机通信协议。该协议允许运行于一台计算机的程序调用另一台计算机的子程序,而程序员无需额外地为这个交互作用编程。RPC的主要目标是让构建分布式应用更加容易,在提供强大的远程调用能力的同时不损失本地调用的语义的简洁性。

趁实习前的这段业余时间,我实现了一个轻量级的分布式RPC框架,名字叫做 buddha,代码量不大,但是麻雀虽小却五脏俱全。本篇文章将一步步阐明buddha的设计、框架组件的拆解以及需要考虑的因素。

序列化与反序列化

在网络中,所有的数据都将会被转化为字节进行传送,所以在代码层面上,一个RPC框架需要实现特定格式的数据与字节数组之间的相互转化。像Java已经提供了默认的序列化方式,但是如果是在高并发的场景下,使用Java原生的序列化方式可能会遇到性能瓶颈。于是,出现了许多开源的、高效的序列化框架:如KryofastjsonProtobuf等。buddha目前支持Kryofastjson两种序列化框架。

TCP拆包、粘包

由于TCP只关心字节流,并不知晓上层的数据格式。如果客户端应用层一次要发送的数据过大时,TCP会将该数据进行分解传送,因此在服务端需要进行粘包处理(由TCP来保证数据的有序性);如果客户端一次要发送的数据量很小时,TCP并不会马上把数据发送出去,而是将其存储在缓冲区,当达到某个阈值的时候再发送出去,因此在服务端需要进行拆包的工作。

通过以上分析,我们了解了TCP粘包或者拆包的原因,解决这个问题的关键在于向数据包添加边界信息,常用的方法有如下三个。

发送端给每个数据包添加包首部,首部中至少包含数据包的长度,这样在接收端接收到数据时,通过读取首部的长度信息得到该数据包有效数据的长度。

发送端将每个数据包封装为固定长度(多余用0填充),这样接收端在接收到数据后根据约定好的固定长度读取每个数据包的数据。

使用特殊符号将每个数据包区分开来,接收端也是通过该特殊符号的划分数据包的边界。

buddha采用第一种方式来解决TCP拆包、粘包的问题。

BIO与NIO

BIO往往用于经典的每连接每线程模型,之所以使用多线程,是因为像accept()read()write()等函数都是同步阻塞的,这意味着当应用为单线程且进行IO操作时,如果线程阻塞那么该应用必然会进入挂死状态,但是实际上此时CPU是处于空闲状态的。开启多线程,就可以让CPU去为更多的线程服务,提高CPU的利用率。但是在活跃线程数较多的情况下,采用多线程模型回带来如下几个问题。

线程的创建和销毁代价颇高,在Linux操作系统中,线程本质上就是一个进程,创建和销毁线程属于重量级的操作。

JVM中,每个线程会占用固定大小的栈空间,而JVM的内存空间是有限的,因此如果线程数量过多那么线程本身就会占据过多的资源。

线程的切换成本较高,每次线程切换需要涉及上下文的保存、恢复以及用户态和内核态的切换。如果线程数过多,那么会有较大比例的CPU时间花费在线程切换上。

使用线程池的方式解决前两个问题,但是线程切换带来的开销还是存在。所以在高并发的场景下,传统的BIO是无能为力的。而NIO的重要特点是:读、写、注册和接收函数,在等待就绪阶段都是非阻塞的,可以立即返回,这就允许我们不使用多线程充分利用CPU。如果一个连接不能读写,可以把这个事件记录下来,然后切换到别的就绪的连接进行数据读写。在buddha中,Netty被用来编写结构更加清晰的NIO程序。

服务注册与发现

在实际应用中,RPC服务的提供者往往需要使用集群来保证服务的稳定性与可靠性。因此需要实现一个服务注册中心,服务提供者将当前可用的服务地址信息注册至注册中心,而客户端在进行远程调用时,先通过服务注册中心获取当前可用的服务列表,然后获取具体的服务提供者的地址信息(该阶段可以进行负载均衡),根据地址信息向服务提供者发起调用。客户端可以缓存可用服务列表,当注册中心的服务列表发生变更时需要通知客户端。同时,当服务提供者变为不可用状态时也需要通知注册中心服务不可用。buddha使用ZooKeeper实现服务注册与发现功能。

代码实现

buddha是我学习验证RPC过程中诞生的一个轻量级分布式RPC框架,代码放在了 GitHub。

参考

RPC 的概念模型与实现解析

NettyRpc

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/67289.html

相关文章

  • 布式下的远程通信技术(RPC)的一些理解

    摘要:都是分开部署,单独上线的。序列化毕竟是远程通信,需要将对象转化成二进制流进行传输。服务化架构的演进架构当业务规模很小时,将所有功能都不熟在同一个进程中,通过双机或者负载均衡器实现负债分流此时,分离前后台逻辑的架构是关键。 showImg(https://segmentfault.com/img/bVbiI2F?w=2250&h=1500); 前言 为什么需要RPC,而不是简单的HTTP...

    EastWoodYang 评论0 收藏0
  • 布式服务框架之远程通讯技术及原理分析

    摘要:微软的虽然引入了事件机制,可以在队列收到消息时触发事件,通知订阅者。由微软作为主要贡献者的,则对以及做了进一层包装,并能够很好地实现这一模式。 在分布式服务框架中,一个最基础的问题就是远程服务是怎么通讯的,在Java领域中有很多可实现远程通讯的技术,例如:RMI、MINA、ESB、Burlap、Hessian、SOAP、EJB和JMS等,这些名词之间到底是些什么关系呢,它们背后到底是基...

    sorra 评论0 收藏0
  • 布式服务框架之远程通讯技术及原理分析

    摘要:微软的虽然引入了事件机制,可以在队列收到消息时触发事件,通知订阅者。由微软作为主要贡献者的,则对以及做了进一层包装,并能够很好地实现这一模式。 在分布式服务框架中,一个最基础的问题就是远程服务是怎么通讯的,在Java领域中有很多可实现远程通讯的技术,例如:RMI、MINA、ESB、Burlap、Hessian、SOAP、EJB和JMS等,这些名词之间到底是些什么关系呢,它们背后到底是基...

    0xE7A38A 评论0 收藏0
  • 后端必备——数据通信知识(RPC、消息队列)一站式总结

    摘要:具体可以参考消息队列之具体可以参考实战之快速入门十分钟入门阿里中间件团队博客是一个分布式的可分区的可复制的基于发布订阅的消息系统主要用于大数据领域当然在分布式系统中也有应用。目前市面上流行的消息队列就是阿里借鉴的原理用开发而得。 我自己总结的Java学习的系统知识点以及面试问题,目前已经开源,会一直完善下去,欢迎建议和指导欢迎Star: https://github.com/Snail...

    Kahn 评论0 收藏0
  • SOA面向服务基础

    摘要:面向服务面向服务的基础面向服务的三层应用层,服务层,数据层应用层用于给用户展示,,,,安卓。在服务器端,进程保持睡眠状态直到调用信息到达为止。编译完成,提示我们已经在下了。 面向服务 面向服务的基础 面向服务的三层:应用层,服务层,数据层 * 应用层:用于给用户展示,PC,H5,IOS,安卓。 * 服务层:业务逻辑,提供接口(商品,订单,支付,用户,物流)。 * 数据层:提供数据支持(...

    songze 评论0 收藏0

发表评论

0条评论

Vultr

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<