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
聲明:本博客文章為原創,只代表本人在工作學習中某一時間內總結的觀點或結論。轉載時請在文章頁面明顯位置給出原文鏈接