那是N年前的一天,老王在看一本讲java的技术书(可惜忘了叫啥名字了),突然看到有一章讲RMI的,立马就觉得很好奇。于是乎,就按书上所讲,写了demo程序。当时也就只知道怎么用,却不知道什么原理。直到多年以后,才知道,原来这个RMI和我们今天要聊的RPC几乎是差不多的东西。那他们到底是什么呢?

what:

先来聊聊RMI。这个玩意儿呢,是sun公司为java定义的一套分布式应用接口,全称:Remote Method Invocation。他能让java开发者很轻松的实现分布式服务间数据访问。让这个访问就像是本地的方法访问一样。

那RPC又是一个什么东东呢?他的全称:Remote Procedure Call。为了实现分布式系统内,不同服务之间数据交换而产生的东东。他使得服务间的数据访问就像是函数调用一样简单和方便,方便写服务的人能聚焦到业务本身,而不用太关注网络交互、数据格式组织、跨平台等多个方面的问题。因此,RMI可以看成是RPC的java版。那为了简化,我们接下来就不细聊RMI,而着重聊聊这个RPC。

why:

上面已经提到了一些我们使用RPC的原因,那我们就顺着上面的脉络,具体的聊聊吧~

1、RPC存在的最重要的意义,就在于,他能简化分布式系统内,不同服务间的数据交换,使得这种交换看起来像本地函数调用一样。所以,我们用他最直接的目的:简化跨机器跨进程的服务调用!

2、因为这些东西被封装简化了,所以,他顺带带来的好处就是:代码极大的简化(一会儿我们讲原理的时候就知道了)。

3、另外,一般这种调用的封装还支持多语言、多操作系统,所以,他能帮我们极好的实现跨平台跨语言。比如,我们为了执行的高效率,用c语言写了一个加密算法,做成了一个通用服务。而我们用java或者python来实现了一个http的服务,他们若要调用那个加密算法服务,就可以用RPC轻松实现。所以,有了RPC,我们可以根据不同的应用场景,使用不同的语言,来更方便的实现需求。

4、更高效的数据传输。因为RPC封装了数据交换的协议,所以可以在里面极大的优化数据传输的算法,从而压缩数据传输的量(还记得老王之前讲过一个zigzag算法嘛)。

有了以上的这些好处,当开发分布式系统的时候,作为一个有志向的程序员,有什么理由不使用RPC呢?

how:

好了,有了上述的铺垫,我们终于可以讲如何来实现这一套东西了。

1、简单的模型:

为了讲清楚这个原理,我们先从一个最简单的模型开始,讲讲怎么样实现跨系统的数据交换。

比方说,我们现在有两个系统(可能不在同一台机器上的两个进程),要交换用户信息:一方是逻辑系统(比方叫blog-system),接收用户提交的user_id,这个系统想用user_id获取用户的详细信息,并展示给用户;另一方是用户系统(比方叫user-system),存储了用户所有的数据(比如:name、nickname、email等等),可以提供根据user_id获取用户详细信息的数据。那我们可以怎么做呢?很简单,我们可以有很多做法,最简单的一种:

A、user-system启动一个socket-server,监听某个端口,用json作为传输数据的协议;

B、blog-system则用socket去connect我们的server,并按要求发送一个json数据给server;

C、server收到请求以后,取出json数据中的user_id,从数据库中查出user,并打包成一个json,返回给client;

D、blog-system收到返回的json,包装成对应的格式,展示给用户。


整个过程如上图所示。是不是觉得就是我们在计算机网络里学的那些最基本的东西?

2、变复杂的模型:

从上面的过程,我们其实可以将这个架构做一下拆分:

A、服务端-客户端通讯协议。

在我们之前的实例中,我们用的是tcp-socket来作为这样一个协议:server端建立一个server-socket,用来接收客户端的请求;客户端发起一个socket请求;

其实,我们也可以用Http、Https等协议作为通讯协议,甚至可以自定义应用层协议。

B、数据传输协议。

在上面的例子里,我们用json来封装的数据,在server和client之间进行传递。其实,我们也可以用结构体(c语言的struct)、xml等方式来作为数据传输协议来封装数据。更可以用自己定义的协议来传输。

有了上述两个东西,我们就可以把他们封装到一个函数中,让他们对外看起来就像是一个函数的访问一样,是不是就达到我们想要的效果呢?

3、更完美的模型:

A、IDL(Interface Description Language):接口定义语言。

如果,我们能将远程调用函数的定义,用一种通用的、跨平台的语言来编写,然后,再用编译器生成不同语言的代码(比如:java、c、c++、php等),这样,我们是否就可以轻松的做到跨平台、跨语言的支持了呢?

比如,以下就是facebook开源的rpc框架thrift的idl定义方式:

service TUserService

{

struct.TUser getUser (1: required i32 uid);

}

看起来是不是跟我们平时写的语言代码很相似呢?

有了这个东东,我们就可以用一个编译工具,生成不同语言的代码了。

B、高承载能力的server模型。

当我们要启动一个server的时候,最简单的就是按我们之前的那种方式,启动一个server-socket。但是这个模型太简单,以至于只要轻轻的给一些压力,服务就可能崩塌。因此,我们需要一个高承载能力的server模型。

现在在linux平台上,大家基本都使用epoll模型。这个是非阻塞的事件驱动模型,能够根据请求事件来触动业务逻辑。所以具有很强的负载能力。现在主流的服务器,比如:nginx、mina、netty等等,都基本采用这种底层模型。

有了这个server做基础,我们才能保证服务的压力能抗的住,才能使得rpc具有相对稳定的调用。

C、简单准确的通讯协议。

这个协议就跟我们tcp、http协议一样,能够描述整个包有多大,哪个地方是协议头、哪个地方是数据包、哪个地方是结束符等等。这个协议越简单越好,使得我们传输的数据量越小。

比如上图就是tcp协议,他定义了数据的大小、数据的校验、序列号等,就是为了保证数据能准确传输到对端。我们也可以仿照这样的协议,来定制我们的协议,比如我们协议包含:版本号、数据包的大小、数据包的校验、数据包等这样的数据。

D、高压缩比 && 快速序列化与反序列化的数据协议。

这个就是我们的数据包的定义。我们可以用文本的json、xml作为数据协议,当然也可以用结构体、java序列化对象等二进制协议作为数据协议。更可以我们定义自己的数据协议。但是这个协议要尽量的小:以保证我们每次传输的数据量尽可能的少;也要保证序列化和反序列化尽量的快:以保证我们的调用时间尽可能的短。

所以,在thrift中,定义TBinaryProtocol、TCompactProtocol等这样的二进制压缩协议,来保证我们的数据传输的速度、打包和解包的速度。

好了,讲了这么多空虚的概念。我们就来看看现有的这些rpc工具是怎么做的吧。

google的protobuf:

这个是一个非常牛逼的rpc工具。不过他只提供了IDL和数据协议。就是只管生成不同语言的实现代码、将一个对象打包成二进制的数据;数据到达对端,他负责解包。然而具体怎么样提供服务、怎么样传输数据,他并不负责。

如果我们要使用这个东东,就需要再弄一个server来接收服务,还要弄一个传输协议来传递数据。常见的,在java下,大家喜欢用netty+protobuf来实现RPC的功能。四要素:

IDL:protobuf

Server: netty

传输协议:TCP

数据协议:protobuf

facebook的thrift:

这个相比于protobuf,提供了server和传输协议的支持。比如,java的实现代码中,就有TThreadPoolServer、TThreadedSelectorServer等多个server模型,便于我们根据业务规模选择不同的模型。

同时,他还提供了TSocket、TSaslTransport、THttpClient等多种传输协议,来实现数据的传输功能。

所以,他集成RPC的四个要素,用起来更简单方便一些。

其他的,诸如以前的CORBA、RMI、Web-Service等等,都是类似的原理。所以,听起来高大上的RPC,实际上都是我们常规的技术,然后进行了组合和封装,让使用者更方便易用(也可以在面试的时候去吓唬人,哈哈哈~)。

好了,关于RPC理论的技术,大体就讲这么多。如果大家想深入的去了解,建议去阅读thrift源代码,很不错的实践代码。老王之前也总结过thrift的代码架构,后面找时间整理出来,分享给大家。

RPC的更多相关文章

  1. 从RPC开始(一)

    这是一篇关于纯C++RPC框架的文章.所以,我们先看看,我们有什么? 1.一个什么都能干的C++.(前提是,你什么都干了) 2.原始的Socket接口,还是C API.还得自己去二次封装... 3.C ...

  2. RPC 使用中的一些注意点

    最近线上碰到一点小问题,分析其原因发现是出在对 RPC 使用上的一些细节掌握不够清晰导致.很多时候我们做业务开发会把 RPC 当作黑盒机制来使用,但若不对黑盒的工作原理有个基本掌握,也容易犯一些误用的 ...

  3. 谈谈如何使用Netty开发实现高性能的RPC服务器

    RPC(Remote Procedure Call Protocol)远程过程调用协议,它是一种通过网络,从远程计算机程序上请求服务,而不必了解底层网络技术的协议.说的再直白一点,就是客户端在不必知道 ...

  4. 游戏编程系列[1]--游戏编程中RPC协议的使用[3]--体验

    运行环境,客户端一般编译为.Net 3.5 Unity兼容,服务端因为用了一些库,所以一般为4.0 或往上.同一份代码,建立拥有2个项目.客户端引用: WindNet.Client服务端引用: OpL ...

  5. python通过protobuf实现rpc

    由于项目组现在用的rpc是基于google protobuf rpc协议实现的,所以花了点时间了解下protobuf rpc.rpc对于做分布式系统的人来说肯定不陌生,对于rpc不了解的童鞋可以自行g ...

  6. spider RPC入门指南

    本部分将介绍使用spider RPC开发分布式应用的客户端和服务端. spider RPC中间件基于J2SE 8开发,因此需要确保服务器上安装了JDK 8及以上版本,不依赖于任何额外需要独立安装和配置 ...

  7. Netty实现高性能RPC服务器优化篇之消息序列化

    在本人写的前一篇文章中,谈及有关如何利用Netty开发实现,高性能RPC服务器的一些设计思路.设计原理,以及具体的实现方案(具体参见:谈谈如何使用Netty开发实现高性能的RPC服务器).在文章的最后 ...

  8. 基于Netty打造RPC服务器设计经验谈

    自从在园子里,发表了两篇如何基于Netty构建RPC服务器的文章:谈谈如何使用Netty开发实现高性能的RPC服务器.Netty实现高性能RPC服务器优化篇之消息序列化 之后,收到了很多同行.园友们热 ...

  9. Redola.Rpc 的一个小目标

    Redola.Rpc 的一个小目标 Redola.Rpc 的一个小目标:20000 tps. Concurrency level: 8 threads Complete requests: 20000 ...

  10. 闲话RPC调用

    原创文章转载请注明出处:@协思, http://zeeman.cnblogs.com 自SOA架构理念提出以来,应用程序间如何以最低耦合度通信的问题便呈现在所有架构师面前. 互联网系统的复杂度让我们不 ...

随机推荐

  1. WebView的使用

    1.首先修改activity.xml中的代码: 2.然后MainActivity中的代码: 3.最后设置权限: <uses-permission android:name="andro ...

  2. tableView简单的动画效果

    tableView 中一些动画效果通常都是实现willDisplayCell的方法来展示出一些动画的效果 (1).带有3D效果的小型动态展示 -(void)tableView:(UITableView ...

  3. Mindjet MindManager思维导图工具的使用 - 项目管理系列文章

    在项目管理过程中,不可避免的要使用到各种各样的项目管理工具.本文就对我自己使用的Mindjet Manager进行一下介绍,推荐给大家使用. 1.  打开软件 这里使用了2012版 2.  软件的界面 ...

  4. 利用Maven把项目生成jar包供其他项目使用

    每当搭建框架时,第一步就是为系统整理一个接一个的jar包.用多了就开始深思,如何把自己的项目也整成jar包,供他人使用呢? 近期一直在看徐晓斌所著:<Maven实战>.因自己学识不够,只是 ...

  5. ansible使用文档

    假设A机器上安装ansible yum install ansible vim /etc/ansible/hosts 对每个主机加key认证ssh-copy-id -i ~/.ssh/id_rsa.p ...

  6. MapRedue开发实例

    一些例子,所用版本为hadoop 2.6.5 1.统计字数 数据格式如下(单词,频数,以tab分开): A 100 B 97 C 98A 98 package com.mr.test; import ...

  7. 再探banana

    在Solr图形化界面:除Hue之外的选择中列出了banana的如下一些不足,今天再次研究这些地方是否有方案可以解决. 1.sunburst图功能没法用. 2.中文有些地方会显示%2B%4C之类的一串字 ...

  8. linux c++编译问题和虚拟机网络通信

    1.gcc main.cpp -lstdc++ -o test5 2. service network stop service NetworkManager restart

  9. GL.IssuePluginEvent 发布插件事件

    Description 描述 Send a user-defined event to a native code plugin. 发送一个用户定义的事件到一个本地代码插件. Rendering in ...

  10. js中数组遍历for与for in区别(强烈建议不要使用for in遍历数组)

    js中遍历数组的有两种方式 var array=['a'] //标准的for循环 for(var i=1;i<array.length;i++){ alert(array[i]) } //for ...