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

前言

最近在读《大型网站系统与Java中间件实践》这本书,第一次系统的简单接触分布式的东西,了解到从单机应用到分布式集群大型应用的道路上,应用的拆分与服务化对于系统的维护、各模块的解藕等都很是重要。这本书的第四章详细介绍了服务框架的各个部分的实现原理。他分别从客户端(服务调用者)以及服务端(服务发布者)的角度介绍了服务框架基本的工做原理,又在基本工做原理的基础上,介绍了服务治理等更加生产实际化的部分。
  研究生学习期间,接触过一些rpc远程调用框架的知识,例如webservice等等,关于webservice的基本知识、发布调用等等进行过简单基本的学习与实验。16年暑假在淘宝实习,接触了阿里很是重要的一个远程调用框架的中间件——HSF,当时简单的看了下源码,明白了HSF基本的工做原理(其实就是全部远程调用框架的基础原理),又在实习转正答辩的时候,了解了下webservice与hsf的不一样。但有一点能够确定的是,他们都是远程调用服务框架,基础的实现原理都是一致的。所以我想对这个通常rpc框架都会实现的原理亲身实现一波,因而便有了这篇博客。整个系列目前分为三篇博客,分别是想法与设计、实现、提高。
 其实很惭愧的是我既没有看过webservice的源码、也没有深刻仔细推敲过HSF的源码,所以准备在完成这个基础的不能再基础的RPC服务框架以后,认真分析一下HSF的源码,也为今年入职淘宝作准备。
 本人实现的这个“基础的不能再基础”的RPC服务框架就暂且命名为LCRPC吧。
  • 1
  • 2
  • 3
  • 4
  • 5

一、什么是RPC

RPC(Remote Procedure Call Protocol)——远程过程调用协议,它是一种经过网络从远程计算机程序上请求服务,而不须要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP或UDP,为通讯程序之间携带信息数据。在OSI网络通讯模型中,RPC跨越了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。 
RPC采用客户机/服务器模式。请求程序就是一个客户机,而服务提供程序就是一个服务器。首先,客户机调用进程发送一个有进程参数的调用信息到服务进程,而后等待应答信息。在服务器端,进程保持睡眠状态直到调用信息到达为止。当一个调用信息到达,服务器得到进程参数,计算结果,发送答复信息,而后等待下一个调用信息,最后,客户端调用进程接收答复信息,得到进程结果,而后调用执行继续进行。 
-------《百度百科》html

二、webservice

Web service是一个平台独立的,低耦合的,自包含的、基于可编程的web的应用程序,可以使用开放的XML(标准通用标记语言下的一个子集)标准来描述、发布、发现、协调和配置这些应用程序,用于开发分布式的互操做的应用程序。[1] 
Web Service技术, 能使得运行在不一样机器上的不一样应用无须借助附加的、专门的第三方软件或硬件, 就可相互交换数据或集成。依据Web Service规范实施的应用之间, 不管它们所使用的语言、 平台或内部协议是什么, 均可以相互交换数据。Web Service是自描述、 自包含的可用网络模块, 能够执行具体的业务功能。Web Service也很容易部署, 由于它们基于一些常规的产业标准以及已有的一些技术,诸如标准通用标记语言下的子集XML、HTTP。Web Service减小了应用接口的花费。Web Service为整个企业甚至多个组织之间的业务流程的集成提供了一个通用机制。 
-------《百度百科》web

能够参考笔者总结的webservice的相关博客: 
【webservice】关于WSDL 
【webservice】Java 发布webservice 步骤 
【webservice】如何远程调用Websevice服务 
【webservice】Java调用WebServicespring

三、阿里中间件HSF

https://wenku.baidu.com/view/1002761a6bd97f192279e908.html 能够参考这篇文章编程

四、如何实现一个简单的RPC框架

4.1 LCRPC服务框架包含的内容

LCRPC服务框架主要包含两个部分,一个是提供给用户使用的jar包,用于服务调用以及发布。另外一个是服务注册查找中心,不与用户直接打交道,用于服务信息的注册与查找。缓存

  • 一、一个应用依赖包 
    服务框架的核心部分,是服务框架开发的主要产物--应用依赖jar包。为服务调用者以及服务提供者提供:注册服务、发布服务、调用服务等功能。完成服务框架的核心功能。

注意: 
- (1)一个服务对应一个接口,接口中包含多个方法 
- (2)服务由接口的全限定名+版本号做为惟一标识tomcat

  • 二、服务注册查找中心 
    服务框架经过服务注册查找中心完成服务信息的管理。LCRPC在发布服务的同时,将服务的信息(服务的惟一标识、地址等)注册到服务注册查找中心。这一,服务在调用时根据服务名称进行地址等其余信息的查找,使得调用端和服务提供端能够经过地址来直接进行交互。服务注册查找中心不与用户直接交互。

4.2 客户端与服务端工做原理

从服务调用者以及服务提供者的角度,分别说明该服务框架的基本工做原理。以下图所示: 
这里写图片描述服务器

(看不清能够访问:远程调用框架工做原理) 
对于服务调用者来讲: 
- (1)服务框架得到服务调用者提供的服务信息(服务惟一标识:接口全限定名+版本号;方法;调用参数); 
- (2)框架根据服务信息经过服务注册查找中心查找到该服务提供者的地址列表; 
- (3)可根据(服务、接口、方法、参数)进行路由,肯定服务提供者的地址; 
- (4)拼装请求参数对象Request,并序列化成二进制流; 
- (5)与服务端创建链接,发送序列化二进制结果; 
- (6)获得服务端响应,反序列化,获得最终调用结果网络

对于服务提供者来讲: 
- (1)发布服务,监听端口; 
- (2)服务发布成功后,将服务信息(服务惟一标识:接口全限定名+版本号;服务实现类全限定名)注册到服务注册查找中心; 
- (3)接收客户端请求,将请求数据反序列化为Request对象; 
- (4)解析Request对象,根据服务标识从服务注册查找中心获取该服务信息,例如服务接口的实现类; 
- (5)利用反射建立类实例对象,调用方法(多采用线程池的方式); 
- (6)将调用结果序列化成二进制数据; 
-(7)发送响应数据到客户端;数据结构

注意:服务发布者须要提供给服务调用者一个二方包,包中函数接口全部方法调用的参数以及返回类型类。其实这个二方包最大的用处应该体如今:让用户像本地调用同样使用服务框架完成远程调用,可是第一个版本咱们先不实现这个功能,后面能够进行优化。mvc

4.3 设计

4.3.1 涉及到的Java编程知识

  • (1)序列化与反序列化:请求的信息须要拼装成Request对象,而且序列化成二进制信息发送给服务发布者;服务发布者对于接收到的二进制信息,须要反序列化为Request对象,解析该对象进行服务的调用,而且将调用的结果也要序列化为二进制对象返回给服务调用者。所以,在整个服务框架中,序列化/反序列化的做用在于调用端与发布端数据的发送。
  • (2)反射:服务发布端收到调用端的请求后,根据服务惟一标识能够得到该接口的实现类信息,经过反射建立实现类实例,进行方法的调用;
  • (3)路由:因为在分布式集群环境中,同一个服务的发布者可能有不少,也就意味着从服务注册查找中心查找到的服务地址可能多于一个,咱们能够根据接口/方法/参数的粒度进行路由,同时还有一些其余规则,例如同机房原则、归组规则等等。在LCRPC第一个版本的实现中,咱们先采起最简单的方式得到服务的地址,例如随机选择等方式;
  • (4)网络通讯实现选择:可参考博客《Java BIO NIO AIO 总结》。在LCRPC服务框架版本一中,咱们先采用BIO的方式,即采用Java Socket编程的方式,对每个链接创建一个新的线程维护;第二个版本使用Java NIO的通讯方式;然后能够再用netty等现成的框架实现AIO。

4.3.2 设计

(一)服务注册查找中心 
服务注册查找中心最终应是一个web应用,提供服务接口给LCRPC服务框架使用,负责服务信息的管理,例如查找与注册。 
- 一、使用开发框架:springmvc+maven+tomcat 
- 二、接口设计: 
(1)服务注册接口:对服务信息进行存储,服务对应接口全限定名+版本号做为服务的惟一标识; 
(2)服务地址列表查询接口:根据服务惟一标识,返回服务地址列表; 
(3)服务信息查询接口:根据服务惟一标识,返回该服务全部信息(包括地址列表) 
- 三、数据结构设计 
(1)服务信息描述结构:ServiceInfoDO 
(2)注册服务列表单例类:ServicesSingle 
(3)服务操做接口:IServiceAccess 
(4)服务操做接口实现类:ServiceAccessImpl 
(5)servlet:ServiceCenterServer 
类结构示意图:(后面补充)

(二)LCRPC包 
提供给服务调用者和服务发布者使用,应提供的功能包括:服务发布(包含服务注册)、服务调用等; 
- 一、使用开发框架:maven 
- 二、数据结构设计: 
(0)服务信息结构体:ServiceInfoDO 
(1)请求调用参数结构:LCRPCRequestDO 
(2)服务调用对外接口:ILCRPCConsumer LCRPCConsumerImpl 对外提供服务调用功能 
(3)服务发布对外接口:ILCRPCProvider LCRPCProviderImpl 对外提供服务发布功能(包含服务注册) 
(4)服务调用帮助类接口:IConsumerService ConsumerServiceImpl 
(5)服务发布帮助类接口:IProviderService ProviderServiceImpl 
(7)线程类一:socket服务端监听线程 
(8)线程类二:socket服务端接收到链接后的处理线程 
(9)自定义异常类:LCRPCRemoteCallException、LCRPCServiceIDIsIllegal、LCRPCServiceMethodIsIllegal、LCRPCServiceNotFound、LCRPCServiceInfoNotComplete、LCRPCServiceListenFailed、LCRPCServiceRegistryFailed 
(10)常量类:Constant 
类结构示意图:(后面补充)

(三)测试应用 
利用LCRPC服务框架提供的包,进行服务发布和服务调用的测试。注意服务发布者须要提供给服务调用者一个调用二方包,包中至少含有该服务多个方法的参数以及返回类型对应的类信息。

  • 一、服务发布测试 
    这里咱们设计一个计算器的服务,提供加减乘除四个功能方法,同时采用本身设计的方法参数以及方法返回值类型。
  • (1)使用开发框架:spring+maven
  • (2)数据结构 
    a、方法参数:MyParamDO 
    b、方法返回值:MyResultDO 
    c、服务对应接口:ICaculator 
    d、服务对应接口的实现类:CaculatorImpl 
    e、服务发布测试类(主运行类):ProviderTest 
    f、常量类:Constant 
    最终服务发布方须要生成一个二方包给服务调用者使用。

  • 二、服务调用测试 
    这里咱们利用计算器服务提供的二方包,对该服务进行远程调用

  • (1)使用开发框架:spring+maven
  • (2)数据结构 
    a、服务发布者提供的二方包 
    b、测试类(主运行类):ConsumerTest

4.3.3 有可能存在的问题

  • (1)链接愈来愈多:IO模式的改变 
    在LCRPC第一个版本的实现中,咱们采用的是Java中BIO的通讯模式,即对每个链接都采用一个线程进行维护,随着请求的增多,这会形成大量线程的建立维护以及资源的浪费。这一点可参考博客《Java BIO NIO AIO 总结》。所以为了解决这个问题,咱们能够尝试改变通讯方式,例如采用BIO的模式,以及AIO的模式。

  • (2)如何让用户像本地调用同样使用服务框架:面向接口编程+动态代理 
    在第一个版本中,服务调用者可以调用服务的必要基础是:LCRPC服务框架提供的依赖、服务发布者提供的二方包、以及要调用服务的相关信息(该服务的惟一标识、该服务方法名称等等)。而其实咱们是否能够帮助用户像在本地使用方法同样的去调用远程服务。用户只须要上述必要基础的前两样,利用服务发布者提供的二方包,该二方包中应当有服务对应的接口,用户直接在本地调用该接口方法,就完成远程调用的内容,没必要在依靠(该服务的惟一标识、该服务方法名称)等信息进行不那么形象化的调用。而上述功能,能够采用动态代理的方式实现。

  • (3)服务注册查找中心:服务信息的持久化  在服务框架第一个版本的实现中,服务注册查找中心对于全部已经注册的信息缓存在内存中,这在实际生产中会产生的问题是:若是服务注册查找服务须要重启,则会丢失所有已经注册服务的信息,所以须要考虑服务信息的持久化。

相关文章
相关标签/搜索