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

转载 2017年09月18日 14:34:27

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

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

序列化与反序列化

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

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。

参考


转载自: http://www.importnew.com/26604.html

一个分布式rpc框架的实现方案(二)

之前实现了一个点对点的rpc功能框架,使用简单的协议将调用接口和参数传给服务端。由于最近在看zk相关的内容,于是准备引入zk进行分布式管理。 先看一下前一个简单版本的实现原理: 这是一个简...
  • Scythe666
  • Scythe666
  • 2016年07月19日 11:29
  • 1879

RPC之——轻量级分布式RPC框架实战

今天,我们一起来实现一个轻量级的RPC框架。 RPC,即 Remote Procedure Call(远程过程调用),说得通俗一点就是:调用远程计算机上的服务,就像调用本地服务一样。 RPC 可基于 ...
  • l1028386804
  • l1028386804
  • 2017年03月16日 00:25
  • 1817

从0开始写一个基于注解的轻量级分布式RPC框架(1)RPC原理和准备工作

1.原理RPC(Remote Procedure Call Protocol)远程过程调用,是分布式的基础。具体源码已经上传GIT 基于注解的RPC源码 RPC就是调用远程服务就像调用本地接口一样。...
  • kkgbn
  • kkgbn
  • 2017年08月17日 00:13
  • 457

【远程调用框架】如何实现一个简单的RPC框架(一)想法与设计

【如何实现一个简单的RPC框架】系列文章:【远程调用框架】如何实现一个简单的RPC框架(一)想法与设计 【远程调用框架】如何实现一个简单的RPC框架(二)实现与使用 【远程调用框架】如何实现一个简...
  • u013177446
  • u013177446
  • 2017年03月26日 11:34
  • 2409

轻量级分布式RPC框架实现

RPC,即 Remote Procedure Call(远程过程调用),说得通俗一点就是:调用远程计算机上的服务,就像调用本地服务一样。 RPC 可基于 HTTP 或 TCP 协议,Web Servi...
  • zmx729618
  • zmx729618
  • 2016年10月08日 17:29
  • 1216

分布式学习笔记1通过Java自己实现简单的HTTP RPC框架

什么是RPC? RPC(Remote Procedure Call Protocol)——远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。 RPC协议假定...
  • a67474506
  • a67474506
  • 2017年05月01日 14:50
  • 966

分布式RPC框架性能大比拼

Dubbo 是阿里巴巴公司开源的一个Java高性能优秀的服务框架,使得应用可通过高性能的 RPC 实现服务的输出和输入功能,可以和 Spring框架无缝集成。不过,略有遗憾的是,据说在淘宝内部,dub...
  • jek123456
  • jek123456
  • 2017年04月17日 10:11
  • 1300

分布式架构核心RPC原理

在应用的迭代演进过程中,随着系统访问量提高,业务复杂度提高,代码复杂度提高,应用逐渐从单体式架构向面向服务的分布式架构转变。RPC(Remote Procedure Call Protocol远程过程...
  • dongnaosenlu
  • dongnaosenlu
  • 2017年08月09日 15:46
  • 534

轻量级分布式 RPC 框架 netty+protostuff+zk +Spring

目录[-] 第一步:编写服务接口第二步:编写服务接口的实现类第三步:配置服务端第四步:启动服务器并发布服务第五步:实现服务注册第六步:实现 RPC 服务器第七步:配置客户端第八步:实现服务发现第...
  • luqiaolong
  • luqiaolong
  • 2015年10月31日 12:34
  • 4780

RCF:一个相当不错的C++分布式RPC框架

    RCF(远程调用框架)是一个可以移植的C++进程间通信框架,使用C++语言特性,提供了一个简单高效的编写分布式C++软件的途径。RCF利用编译时多态清晰分开了接口和实现. 和传统的RPC框架如...
  • seebit
  • seebit
  • 2010年12月21日 11:14
  • 9749
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:如何实现一个分布式 RPC 框架
举报原因:
原因补充:

(最多只允许输入30个字)