RPC 定義
RPC(Remote Procedure Call)即遠(yuǎn)程過(guò)程調(diào)用,指被調(diào)用方法的具體實(shí)現(xiàn)不在程序運(yùn)行本地,而是在別的某個(gè)地方。主要應(yīng)用于不同的系統(tǒng)之間的遠(yuǎn)程通信和相互調(diào)用。
如 A 調(diào)用 B 提供的 remoteAdd 方法:
- 首先A與B之間建立一個(gè)TCP連接;
- 然后A把需要調(diào)用的方法名(這里是remoteAdd)以及方法參數(shù)(10, 20)序列化成字節(jié)流發(fā)送出去;
- B接受A發(fā)送過(guò)來(lái)的字節(jié)流,然后反序列化得到目標(biāo)方法名,方法參數(shù),接著執(zhí)行相應(yīng)的方法調(diào)用(可能是localAdd)并把結(jié)果30返回;
- A接受遠(yuǎn)程調(diào)用結(jié)果
有些遠(yuǎn)程調(diào)用選擇比較底層的 socket 協(xié)議,有些遠(yuǎn)程調(diào)用選擇比較上層的 HTTP 協(xié)議。
遠(yuǎn)程調(diào)用的好處:
- 解耦:當(dāng)方法提供者需要對(duì)方法內(nèi)實(shí)現(xiàn)修改時(shí),調(diào)用者完全感知不到,不用做任何變更;這種方式在跨部門(mén),跨公司合作的時(shí)候經(jīng)常用到,并且方法的提供者我們通常稱(chēng)為:服務(wù)的暴露方
這里使用 PHP Socket 來(lái)創(chuàng)建一個(gè)服務(wù)端和客戶(hù)端,目錄結(jié)構(gòu)如下:
![](/d/20211017/296930b0d4b94a171634fef6e0f5c359.gif)
服務(wù)端
?php
class RpcServer {
protected $server = null;
public function __construct($host, $port, $path)
{
// 創(chuàng)建一個(gè) Socket 服務(wù)
if(($this->server = socket_create(AF_INET,SOCK_STREAM,SOL_TCP)) 0) {
exit("socket_create() 失敗的原因是:".socket_strerror($this->server)."\n");
}
if(($ret = socket_bind($this->server,$host,$port)) 0) {
exit("socket_bind() 失敗的原因是:".socket_strerror($ret)."\n");
}
if(($ret = socket_listen($this->server,3)) 0) {
exit("socket_listen() 失敗的原因是:".socket_strerror($ret)."\n");
}
// 判斷 RPC 服務(wù)目錄是否存在
$realPath = realpath(__DIR__ . $path);
if ($realPath === false || !file_exists($realPath)) {
exit("{$path} error \n");
}
do {
$client = socket_accept($this->server);
if($client) {
// 一次性讀取
$buf = socket_read($client, 8024);
echo $buf;
//解析客戶(hù)端發(fā)送過(guò)來(lái)的協(xié)議
$classRet = preg_match('/Rpc-Class:\s(.*);\r\n/i', $buf, $class);
$methodRet = preg_match('/Rpc-Method:\s(.*);\r\n/i', $buf, $method);
$paramsRet = preg_match('/Rpc-Params:\s(.*);\r\n/i', $buf, $params);
if($classRet $methodRet) {
$class = ucfirst($class[1]);
$method = $method[1];
$params = json_decode($params[1], true);
$file = $realPath . '/' . $class . '.php'; // 類(lèi)文件需要和類(lèi)名一致
$data = ''; // 執(zhí)行結(jié)果
// 判斷類(lèi)文件是否存在
if(file_exists($file)) {
// 引入類(lèi)文件
require_once $file;
// 實(shí)例化類(lèi)
$rfc_obj = new ReflectionClass($class);
// 判斷該類(lèi)指定方法是否存在
if($rfc_obj->hasMethod($method)) {
// 執(zhí)行類(lèi)方法
$rfc_method = $rfc_obj->getMethod($method);
$data = $rfc_method->invokeArgs($rfc_obj->newInstance(), [$params]);
} else {
socket_write($client, 'method error');
}
//把運(yùn)行后的結(jié)果返回給客戶(hù)端
socket_write($client, $data);
}
} else {
socket_write($client, 'class or method error');
}
// 關(guān)閉客戶(hù)端
socket_close($client);
}
}while(true);
}
public function __destruct()
{
socket_close($this->server);
}
}
new RpcServer('127.0.0.1',8080,'./service');
客戶(hù)端
?php
class RpcClient {
protected $client = null;
protected $url_info = []; // 遠(yuǎn)程調(diào)用 URL 組成部分
public function __construct($url)
{
// 解析 URL
$this->url_info = parse_url($url);
}
public function __call($name, $arguments)
{
// 創(chuàng)建一個(gè)客戶(hù)端
$this->client = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if(!$this->client) {
exit('socket_create() 失敗');
}
socket_connect($this->client, $this->url_info['host'], $this->url_info['port']);
// 傳遞調(diào)用的類(lèi)名
$class = basename($this->url_info['path']);
// 傳遞調(diào)用的參數(shù)
$args = '';
if(isset($arguments[0])) {
$args = json_encode($arguments[0]);
}
// 向服務(wù)端發(fā)送我們自定義的協(xié)議數(shù)據(jù)
$proto = "Rpc-Class: {$class};".PHP_EOL
."Rpc-Method: {$name};".PHP_EOL
."Rpc-Params: {$args};".PHP_EOL;
socket_write($this->client, $proto);
// 讀取服務(wù)端傳來(lái)的數(shù)據(jù)
$buf = socket_read($this->client, 8024);
socket_close($this->client);
return $buf;
}
}
$rpcClient = new RpcClient('http://127.0.0.1:8080/news');
echo $rpcClient->display(['title'=>'txl']);
echo $rpcClient->display(['title'=>'hello world']);
服務(wù)類(lèi) News
?php
class News {
public function display($data)
{
return json_encode(['result'=>"News display(), title is {$data['title']}"]);
}
}
運(yùn)行測(cè)試:
Client
![](/d/20211017/9f5780e829f5533aa206fa61f0280705.gif)
Server
![](/d/20211017/6e20c00371b3964af9153293a36edd1d.gif)
到此這篇關(guān)于PHP創(chuàng)建簡(jiǎn)單RPC服務(wù)案例詳解的文章就介紹到這了,更多相關(guān)PHP創(chuàng)建簡(jiǎn)單RPC服務(wù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
您可能感興趣的文章:- 詳解php中流行的rpc框架
- php實(shí)現(xiàn)的一個(gè)簡(jiǎn)單json rpc框架實(shí)例
- PHP采用XML-RPC構(gòu)造Web Service實(shí)例教程
- AMFPHP php遠(yuǎn)程調(diào)用(RPC, Remote Procedure Call)工具 快速入門(mén)教程
- php xml-rpc遠(yuǎn)程調(diào)用