PhpRpc 從 0 到 0.7

1.什麼是RPC

RPC全稱Remote Procedure Call,中文譯爲遠程過程調用,簡單理解就是 一種解決方案。php

業務場景:
舉一個大部分phper都接觸過的商城開發,通常商城都有如下幾個模塊git

  • 商品模塊
  • 訂單模塊
  • 會員模塊
  • XX模塊

在常見架構中的體現是:github

rpc11

那麼在RPC架構中每一個模塊就是一個服務提供者,架構體現:服務器

在這套架構中業務機的職責就是把一個請求 ,拆分紅N個小請求,分發到各個服務裏面,再整合各個服務的結果,返回給用戶。
rpc12網絡

例如在某次下單請求中,那麼大概 發送的邏輯以下:架構

1. 業務機接受請求
2. 業務機提取用戶參數,請求用戶服務,獲取用戶餘額等信息,等待結果
3. 業務機提取商品參數,請求商品服務,獲取商品剩餘庫存和價格等信息,等待結果。
4. 業務機融合用戶服務、商品服務的返回結果,進行下一步調用(假設知足購買條件)
5. 業務機調用用戶服務進行扣款,調用商品服務進行庫存扣減,調用訂單服務進行下單(事務邏輯和撤回能夠用請求id保證,或者本身實現其餘邏輯調度)
6. 業務機根據處理響應用戶socket

而在以上發生的行爲,就稱爲遠程過程調用。而調用過程實現的通信協議能夠有不少,好比常見的HTTP、TCP協議。tcp

服務熔斷

某個服務故障或者異常時直接熔斷整個服務,而不是一直等到此服務超時spa

服務降級

當某個服務熔斷以後,服務器將再也不被調用,此時客戶端能夠本身準備一個本地的fallback回掉,返回一個缺省值 ,這樣作,雖然服務水平降低,但好歹,比直接掛掉要強。 服務降級處理是在客戶端實現完成的,與服務端沒有關係操作系統

服務限流

例如某個服務器最多同時僅能處理100個請求, 或者是cpu負載達到百分之80的時候, 爲了保護服務的穩定性,則不在但願繼續收到 新的鏈接。那麼此時就要求客戶端再也不對其發起請求,例如 你能夠以任何的形式來監控你的服務,當觸發某個條件時(CPU負載80%)下線此服務,業務機動態獲取服務節點時就能夠知道此服務已限流則響應用戶[網絡繁忙,請稍後再試] 或者此服務有多臺機提供則其餘機可繼續提供服務,等被下線的機子恢復後又上線

2.Php Tcp通信

源碼

https://github.com/ar414-com/...

開發環境要求

  • 保證 PHP 版本大於等於 7.2
  • 保證 Swoole 拓展版本大於等於 4.3.5
  • 使用 Linux / FreeBSD / MacOS 這三類操做系統

做者開發環境

  • PHP 7.2
  • Swoole 4.3.5
  • CentOS 7.2

rpc21

建立一個最基本的TCP服務器

<?php

//建立Server對象,監聽 0.0.0.0:20001端口
$serv = new Swoole\Server("0.0.0.0", 20001);

$serv->on('Start', function ($serv) {
 echo "服務已啓動,主進程PID:{$serv->master_pid}\n";
});

//監聽鏈接進入事件
$serv->on('Connect', function ($serv, $fd) {
 echo "Client: Connect.\n";});

//監聽數據接收事件
$serv->on('Receive', function ($serv, $fd, $from_id, $data) {
 echo "接收客戶端數據:{$data}\n";
  $serv->send($fd, "Server: ".$data);
});

//監聽鏈接關閉事件
$serv->on('Close', function ($serv, $fd) {
 echo "Client: Close.\n";});

//啓動服務器
$serv->start();

rpc22

<?php

//創建鏈接
$fp = stream_socket_client('tcp://127.0.0.1:20001');

//發送數據
fwrite($fp, 'Test');

//主動獲取響應
$data = fread($fp, 65533);

echo "服務端響應數據:{$data}\n";

//斷開鏈接
fclose($fp);

客戶端

rpc23

服務端

rpc24

3.客戶端調用與服務端處理(提供思路)

客戶端與服務器的數據傳輸約定

客戶端請求Rpc服務(如下並不是完整代碼)
  • 場景:例如在一個商場系統中,咱們將商品庫和用戶庫兩個服務切分開到不一樣的服務器當中
  • 當用戶打開商場首頁的時候, 咱們但願App向某個網關發起請求,
  • 該網關能夠自動的幫咱們請求商品列表和用戶信息等數據
//商品列表
$data = [
 'service' => 'Goods',  //服務名稱
 'action'  => 'getList', //具體方法
 'arg'     => ['page' => 1] //請求參數
];
//用戶信息
$data = [
 'service' => 'User',  //服務名稱
 'action'  => 'getUserInfoForToken', //具體方法
 'arg'     => ['token' => '6aa62603ef82b70597a90d93af04b542'] //請求參數
];
//打包數據
$dataStr = serialize($data); 
$dataStr = pack('N', strlen($str)).$str;

請求API網關 API網關自動根據Service參數查詢出對應服務IP、PORT並進行調用返回
本示例爲了方便將Rpc服務配置寫入.env文件 例:

//.env
RPC_GOODS_HOST=10.0.0.1
RPC_GOODS_PORT=8899
RPC_USER_HOST=10.0.0.2
RPC_USER_PORT=8899
服務端處理請求( 完整代碼
//接受請求數據並解包
$data = substr($request,'4');
$data = unserialize($data);
//TODO 檢測必須參數 service action
//檢測服務是否存在
//$controllerNameSpace是你的控制器命名空間
$service = ucfirst($data['service']);
$class   = "{$controllerNameSpace}\\{$service}";
if(!class_exists($class))
{
 //TODO 服務不存在
 //設置響應狀態錯誤碼(需自行封裝)
 $response->setStatus(Response::STATUS_SERVICE_SERVICE_NOT_FOUND); //響應客戶端(需自行封裝)
 goto response;}

//檢測方法是否存在
$class  = new \ReflectionClass($class);
$action = $data['action'];
if(!$class->hasMethod($action))
{
 //action不存在
 //從新組裝參數
 //若是方法則調用魔術方法 好比調用一些PDO方法,若是無則調用時返回方法不存在
 $request->proxyActionAssemblyArg(); $method = $class->getMethod('__call');}
else
{
 $method = $class->getMethod($action);}

//調用
$instance = $class->newInstance($request,$response);
$ret = $method->invokeArgs($instance,$request->getArg());
$response->setMessage($ret);
//響應客戶端(需自行封裝)
goto response;

//做者的響應封裝(僅供參考):
response:{
    if ($server->exist($fd))
    {
        $message = $response->getMessage();
        $responseData = [
            'status' => $response->getStatus(),
            'data'   => $message
        ];
        $responseData = serialize($responseData);
        $responseData = Request::pack($responseData);
        $server->send($fd,$responseData);
        //判斷客戶端是否須要長鏈接
        if(!$request->getIsKeep())
        {
            $server->close($fd);
        }
    }
}
相關文章
相關標籤/搜索