我对RPC的理解

Posted 那一叶随风

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了我对RPC的理解相关的知识,希望对你有一定的参考价值。

1、简介

  这篇文章将会用最直白的方式介绍RPC,以及实现RPC客户端的Ajax跨域调用的例子。

  RPC(Remote Procedure Call Protocol)--远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI网络通信模型中,RPC跨越了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。

  RPC采用客户机/服务器模式。请求程序就是一个客户机,而服务提供程序就是一个服务器。首先,客户机调用进程发送一个有进程参数的调用信息到服务进程,然后等待应答信息。在服务器端,进程保持睡眠状态直到调用信息的到达为止。当一个调用信息到达,服务器获得进程参数,计算结果,发送答复信息,然后等待下一个调用信息,最后,客户端调用进程接收答复信息,获得进程结果,然后调用执行继续进行。

  以上来自度娘!看完上面明白什么是RPC么,在心中能将RPC整个服务过程构造出来么?当然不能啦,对于我们这种小白来说最好是用最直白的语言进行描述。

  从字面上我们是大概了解到是从一个服务器中调用另一个服务器中的方法,使用它提供的功能。在我最开始接触RPC的时候,是在这本书中《php精粹:编写高效PHP代码》【(美)Lorna Mitchell,(美)Davey Shafik,(美)Matthew Turland著;彭冲,胡琳译】。是的,我所从事的语言就是世界上最好的语言--PHP,222333哈哈。

  在这本书中所介绍的RPC的实现方式是通过HTTP协议进行的。但是当我在寻找相关资料的时候,已看其他语言的例子,咋不一样的咧,难道PHP就是独特的?RPC即是远程调用,一般来说是不关语言层面的呀!

  的确,RPC=Remote Produce Call 是一种技术的概念名词它可以通过不同的方式实现。http是rpc实现的一种方式,RPC还可以通过Socket自己实现一套协议来实现。当然啦,不同的实现方式有不同的特点,长短连接、数据的传输方式、灵活性等等。

  RPC的核心并不在于使用什么协议。RPC的目的是让你在本地调用远程的方法,而对你来说这个调用是透明的,你并不知道这个调用的方法是部署哪里。通过RPC能解耦服务,这才是使用RPC的真正目的。

RPC(远程过程调用)是什么

  • 简单的说,RPC就是从一台机器(客户端)上通过参数传递的方式调用另一台机器(服务器)上的一个函数或方法(可以统称为服务)并得到返回的结果。
  • RPC 会隐藏底层的通讯细节(不需要直接处理Socket通讯或Http通讯)
  • RPC 是一个请求响应模型。客户端发起请求,服务器返回响应(类似于Http的工作方式)
  • RPC 在使用形式上像调用本地函数(或方法)一样去调用远程的函数(或方法)

  即能够调用远程规定好的接口就可称之为RPC!在我上一篇文章中所讲的Web service(SOAP)也是RPC的一种实现方式。

  Thrift ,这是我最近学习的一个RPC框架,它很强大,数据是通过二进制格式进行传输,相对 XML 和 JSON 体积更小,对于高并发、大数据量和多语言的环境更有优势。当然啦,这里不讲这个RPC框架,毕竟刚接触,对于Thrift这方面的知识还是菜鸟级别。能懂一丢丢但是距离将它写成博客文章还是差很远的。

  接下来我将采用HTTP方式来实现一个RPC,并且在客户端中能够在Ajax下进行跨域访问。

2、PRC实例

  环境介绍:www.test88.com作为服务主机、www.test99.com作为客户端主机

  一个好的api可以支持不同的格式输出、大多RPC采用post方式提交数据!接下来我们将采用json格式输出、POST提交数据

  2.1、先建立具体服务功能的逻辑程序

  WebServer.class.php【www.test88.com】

 1 <?php
 2 class WebServer
 3 {
 4     public static function test($name,$age,$action)
 5     {
 6         return $name.\',今年年龄\'.$age.\',最喜欢做的事情是\'.$action;
 7     }
 8     public static function look()
 9     {
10         return $_POST;
11     }
12     public static function nono()
13     {
14         return \'啥鸡毛都没有\';
15     }
16 }
17 ?>

 

  2.2、服务端提供相应的入口

  WebServer.php【www.test88.com】

  返回数据使用json格式!一个最基本的RPC服务已经建成!

 1 <?php
 2 require \'./WebServer.class.php\';
 3 if(isset($_POST[\'method\']))
 4 {
 5     $post=$_POST;
 6     switch ($_POST[\'method\']) {
 7         case \'test\':
 8             $respond=WebServer::test($post[\'name\'],$post[\'age\'],$post[\'action\']);
 9             break;
10         case \'look\':
11             $respond=WebServer::look();
12             break;
13         default:
14             $respond=WebServer::nono();
15             break;
16     }
17 }
18 else
19 {
20     $respond=\'木有!\';
21 }
22 header(\'content-type:application/json\');
23 echo json_encode($respond);
24 #echo $respond;
25 ?>

 

  2.3、建立跨域代理脚本

  跨域请求解决:为避免同源策略,可使用服务器端代理即写个代理脚本放入自己的域中,使用ajax来访问代理脚本,脚本远程访问api接收数据,再将数据返回给需要的地方(好处:可是在代理的时候将接收回来的数据进行相应的数据类型结构处理,再返回需要的地方)

  Agency.class.php【www.test99.com】

 1 <?php
 2 class Agency
 3 {
 4     #允许访问的api域名、返回的数据类型
 5     public $allowHost=array(\'test88.com\'=>array(\'mimetype\'=>\'aplication/json\'));
 6     public $host=\'\';
 7 
 8     public function __construct()
 9     {
10         $this->host=parse_url("http:/".$_SERVER[\'PATH_INFO\'],PHP_URL_HOST); #提取域名参数
11     }
12 
13     /**
14     * @desc 设置允许去访问的主机域名
15     *
16     * @param $host   string 域名
17     * @param $array  array  数组,放回的数据类型
18     */    
19     public function setAllowHost($host, $array=array(\'mimetype\'=>\'aplication/json\'))
20     {
21         $this->allowHost[$host]=$array;
22     }
23 
24     /**
25     * @desc 判断域名
26     *
27     * @param $host string 域名
28     */
29     private function decide($host)
30     {
31         if(!isset($this->allowHost[$host])) #判断host是否允许代理访问
32         {
33             header("Status:403 Forbidden");
34             exit;
35         }
36         return true;
37     }
38 
39     public function requestPost()
40     {
41         $host=$this->host;
42         $this->decide($host);   #判断
43         $host=\'www.\'.$host;  //拼接host
44         $uri=strrchr($_SERVER[\'PATH_INFO\'],\'/\'); #提取具体URI
45         $port=80;
46         $link=fsockopen($host,$port);
47         //请求行
48         #$request_data="POST /WebServer.php HTTP/1.1\\r\\n";
49         $request_data="POST $uri HTTP/1.1\\r\\n";
50         //请求头
51         #$request_data.="Host: www.test88.com\\r\\n";
52         $request_data.="Host: $host\\r\\n";
53         $request_data.="User-Agent: Mozilla/5.0 (Windows NT 6.2; WOW64; rv:10.0) Gecko/20100101 Firefox/10.0\\r\\n";
54         $request_data.="Connection: keep-alive\\r\\n";
55         #post数据
56         #$post_data=array(\'name\'=>$_POST[\'name\'],\'ff\'=>\'bbb\');
57         $post_content=http_build_query($_POST);
58         $len=strlen($post_content);
59         $request_data.="Content-Length: ".$len."\\r\\n";
60         $request_data.="Content-Type: application/x-www-form-urlencoded\\r\\n";
61         
62         $request_data.="\\r\\n"; //空行表示头结束
63         
64         //请求主体
65         $request_data.=$post_content;
66         
67         //发送数据
68         fwrite($link,$request_data);
69         
70         //接收数据
71         $inheader=1;
72         while(!feof($link))
73         {
74             #echo fgets($link,1024);
75             //除去请求头,只显示返回数据
76             $data=fgets($link,1024);
77             if($inheader && ($data=="\\n" || $data=="\\r\\n"))
78             {
79                 $inheader=0;
80             }
81             if($inheader==0)
82             {
83                 var_dump(json_decode($data));
84                 echo ($data);  #用于测试
85             }
86         }
87         //关闭请求
88         fclose($link);
89     }
90 
91 
92 }

 

  实例化脚本:agency.php【www.test99.com】

1 <?php
2 include "./Agency.class.php";
3 $a=new Agency();
4 $a->requestPost();
5 ?>

  2.4、Ajax访问

  建立相应的html文件

  button.html【www.test99.com】

 1 <!DOCTYPE html>
 2 <html>
 3 <head>
 4     <meta charset="utf-8">
 5     <title>button</title>
 6 </head>
 7 <body>
 8 <button id=\'b1\'>按钮1</button>
 9 </body>
10 <script type="text/javascript">
11 var b1=document.getElementById(\'b1\');
12 b1.onclick=function()
13 {
14     var xhr=new XMLHttpRequest();
15     xhr.onreadystatechange=function()
16     {
17         if(xhr.readyState==4)
18         {
19             //document.body.innerHTML+=xhr.responseText;
20             alert(xhr.responseText)
21         }
22     }
23     //代理文件+(需要访问的api的域名+api具体的某个接口)
24     xhr.open(\'post\',\'./agency.php/test88.com/WebServer.php\');   
25         xhr.setRequestHeader(\'content-type\',\'application/x-www-form-urlencoded\');
26         //api接口参数使用info来传递
27         var info=\'method=look&name=小明&age=20&action=打架\';   //相应参数
28         xhr.send(info);
29 }
30 </script>
31 </html>

  2.5、开始测试

  访问www.test99.com/button.html

  并点击按钮1

  

    

  更改访问方法method=test,继续访问

大功告成!

3、总结

  总的来说,我所写的这个例子是非常非常简单的,仅仅只是用来参考哈。当然啦,性能上肯定是鸡肋。在我自己做简单测试的时候,Ajax刷新返回数据都非常缓慢。对于小白的我们来说,结合一个简单实用的例子来学习了解一门技术还是不错的!希望大家对RPC的学习不要止步于此哈,毕竟我这篇博客是入门级的,更多相关的RPC知识还等着大家去挖掘呢!

 

(以上是自己的一些见解,若有不足或者错误的地方请各位指出)

 作者:那一叶随风   http://www.cnblogs.com/phpstudy2015-6/

 原文地址:http://www.cnblogs.com/phpstudy2015-6/p/6850658.html 

 声明:本博客文章为原创,只代表本人在工作学习中某一时间内总结的观点或结论。转载时请在文章页面明显位置给出原文链接

 

以上是关于我对RPC的理解的主要内容,如果未能解决你的问题,请参考以下文章

通过几段 Java 代码带你理解 RPC

几个关于js数组方法reduce的经典片段

SOAP、RPC 和项目间依赖关系

手写RPC框架,理解更透彻,代码已上传Github!

金蝶handler中 collection 代码片段理解

Seata RPC 模块的重构之路