日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > php >内容正文

php

怎么改PHP_PHP实现RPC(简版)

發(fā)布時(shí)間:2025/3/19 php 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 怎么改PHP_PHP实现RPC(简版) 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

概述

RPC這個(gè)東西是什么? 第一次聽(tīng)說(shuō)他, 還要在它的前邊加個(gè)G, 當(dāng)時(shí)我以為GRPC是一項(xiàng)技術(shù), 后來(lái)才知道, 并不是這樣. GRPC只是RPC的谷歌實(shí)現(xiàn).

谷歌搜了一下, RPC就是一種: 遠(yuǎn)程函數(shù)調(diào)用, 看到這里, 我已經(jīng)等不及了, 不往下看了, 先自己實(shí)現(xiàn)一個(gè). 如果只給你這樣一個(gè)概念, 如何實(shí)現(xiàn)調(diào)用遠(yuǎn)程函數(shù)的功能呢?

自己實(shí)現(xiàn)

自己嘗試實(shí)現(xiàn)一個(gè)粗糙的PHP版本. (不想看可以跳過(guò)的)

思路

遠(yuǎn)程調(diào)用, 只需要解決下面問(wèn)題:

  • 通信問(wèn)題
  • 定義傳輸?shù)臄?shù)據(jù)格式
  • 如何封裝后可以達(dá)到像調(diào)用本地函數(shù)一樣的效果
  • 先來(lái)解決通信問(wèn)題, 直接粗暴的tcp socket

    傳輸?shù)臄?shù)據(jù)格式, 直接用json進(jìn)行傳輸

    調(diào)用本地函數(shù)?? 這就要借助一下PHP的魔術(shù)函數(shù)了, __call() 這個(gè)函數(shù)是一個(gè)類調(diào)用不存在的方法時(shí)會(huì)跑到這里來(lái), 所以, 我們返回一個(gè)類, 在call方法中進(jìn)行遠(yuǎn)程調(diào)用, 這樣, 在本地看來(lái)就只是在調(diào)用一個(gè)方法.

    開(kāi)始實(shí)現(xiàn)

    PHP中進(jìn)行socket連接十分簡(jiǎn)單, 直接調(diào)用系統(tǒng)函數(shù). 通信問(wèn)題解決了, 剩下的就是傳輸數(shù)據(jù)了, so easy

    經(jīng)過(guò)一番摸索, 看下結(jié)果

    服務(wù)器內(nèi)容:

    <?phpclass RpcServer{ private $port = 0; // 監(jiān)聽(tīng)端口號(hào) private $host = ''; // IP public function __construct($host, $port){ $this->host = $host; $this->port = $port; } /** * 運(yùn)行, 監(jiān)聽(tīng)端口并處理 */ public function run(){ // 創(chuàng)建socket $server = stream_socket_server("tcp://{$this->host}:{$this->port}"); if(empty($server)) throw new Exception('創(chuàng)建套接字失敗'); // 監(jiān)聽(tīng) while (true){ $client = stream_socket_accept($server); if(empty($client)) continue; // 處理請(qǐng)求 $this->disposeClient($client); fclose($client); } } private function disposeClient($client){ $buf = fread($client, 4096); $array = json_decode($buf, true); // 創(chuàng)建對(duì)象并調(diào)用方法 $class = $array['class'] ?? ''; $method = $array['method'] ?? ''; $params = $array['params'] ?? []; $instance = new $class(); $result = $instance->$method(...$params); fwrite($client, json_encode($result)); }}// 測(cè)試調(diào)用類class Test{ public function tt(){ return 'return_tt'; } public function add($a, $b){ return $a + $b; }}(new RpcServer('127.0.0.1', 8888)) ->run();

    調(diào)用方:

    <?phpclass RpcClient{ private $urlInfo = null; private $className = ''; private function __construct($url, $className){ $this->urlInfo = parse_url($url); $this->className = $className; } public static function getInstance($className){ return new RpcClient('127.0.0.1:8888', $className); } public function __call($name, $arguments){ // 創(chuàng)建客戶端 $client = stream_socket_client("tcp://{$this->urlInfo['host']}:{$this->urlInfo['port']}"); if(empty($client)) return null; // 發(fā)送數(shù)據(jù) fwrite($client, json_encode([ 'class' => $this->className, 'method' => $name, 'params' => $arguments, ])); // 接收返回 $data = fread($client, 4096); // 關(guān)閉客戶端 fclose($client); return json_decode($data, true); }}$test = RpcClient::getInstance('Test');echo $test->tt(), PHP_EOL;echo $test->add(4, 6);

    結(jié)果:

    嗯, 還闊以. 當(dāng)然, 問(wèn)題還是有很多的, 比如不能實(shí)現(xiàn)保存對(duì)象的修改狀態(tài)等等.

    其實(shí)對(duì)象可以通過(guò)序列化和反序列化來(lái)傳輸, 額, Java中, 不知道PHP有沒(méi)有這種技術(shù).

    當(dāng)然, 一個(gè)RPC中必然大量使用反射、序列化、動(dòng)態(tài)加載、代理、網(wǎng)絡(luò)請(qǐng)求等等, 這只是一個(gè)超級(jí)超級(jí)粗糙的示例.

    繼續(xù)

    nice, 自己做完了, 對(duì)RPC是個(gè)什么東西有了一個(gè)基本的概念.

    WHAT

    RPC是什么? 簡(jiǎn)單說(shuō), 就是遠(yuǎn)程函數(shù)調(diào)用. 字面意思, 很好理解.

    WHY

    看到一個(gè)技術(shù), 一定會(huì)問(wèn)的一個(gè)問(wèn)題就是: 為什么? 一個(gè)技術(shù)基本不會(huì)平白無(wú)故出現(xiàn), 都是為了解決某些問(wèn)題, 那么RPC解決了什么問(wèn)題呢? 字面含義: 遠(yuǎn)程函數(shù)調(diào)用

    為什么要進(jìn)行遠(yuǎn)程函數(shù)調(diào)用, 把函數(shù)拿過(guò)來(lái)本地調(diào)用不就好了? 還不用走網(wǎng)絡(luò)IO, 速度更快一些. 很好, 現(xiàn)在假設(shè), 你真的這樣做了, 當(dāng)項(xiàng)目變得龐大, 你想要進(jìn)行拆分, 拆分后的有: 項(xiàng)目A, 項(xiàng)目B..., 這時(shí), 你發(fā)現(xiàn)這些拆分的項(xiàng)目部分邏輯是重疊的, 比如用戶信息相關(guān), 怎么辦? 如果不抽出來(lái), 以后的維護(hù)成本會(huì)變得很高, 一處改處處改. 如果抽出來(lái), 跨項(xiàng)目如何進(jìn)行調(diào)用? 哎, 走過(guò)路過(guò)不要錯(cuò)過(guò), RPC推薦給你.

    HOW

    那么如何實(shí)現(xiàn)RPC呢?

    在剛才使用PHP簡(jiǎn)單實(shí)現(xiàn)中, 已經(jīng)發(fā)現(xiàn)了. 需要解決的問(wèn)題如下:

  • 網(wǎng)絡(luò)通信
  • 信息格式
  • 對(duì)象狀態(tài)保存
  • 1.網(wǎng)絡(luò)通信

    說(shuō)到底, 網(wǎng)絡(luò)通信不過(guò)兩種: tcp udp.

    有沒(méi)有使用udp實(shí)現(xiàn)的RPC呢? 貌似也有.

    使用tcp協(xié)議實(shí)現(xiàn)的RPC也有, 當(dāng)然, 不光傳輸層協(xié)議, 也有直接通過(guò)應(yīng)用層協(xié)議: http、websocket等等建立連接的. 當(dāng)然, 如果需要頻繁調(diào)用, 可以不斷開(kāi)tcp連接, 在一段時(shí)間內(nèi)一直保持連接, 避免頻繁握手.

    2.信息格式

    信息格式就有很多選擇了, jsonxml等等, 也可以自己定制, 只要發(fā)送端和接收端統(tǒng)一信息格式就行了.

    3.對(duì)象狀態(tài)保存

    對(duì)于一個(gè)類的調(diào)用, 通常都會(huì)有類狀態(tài)修改的操作, 比如調(diào)用setName方法, 如何保存對(duì)象的信息呢? 當(dāng)然, 可以服務(wù)端將對(duì)象在內(nèi)存中的信息直接序列化發(fā)回去, 當(dāng)客戶端下次調(diào)用時(shí)攜帶序列化信息, 服務(wù)端接收后反序列化還原對(duì)象繼續(xù)操作.

    過(guò)程

    個(gè)人理解的RPC調(diào)用過(guò)程:

  • 客戶端創(chuàng)建RPC對(duì)象
  • 客戶端調(diào)用方法
  • RPC解析方法并將對(duì)象及參數(shù)做序列化
  • RPC通過(guò)網(wǎng)絡(luò)連接發(fā)送方法調(diào)用
  • 服務(wù)端接收到方法調(diào)用, 解析對(duì)象及參數(shù)反序列化
  • 服務(wù)端執(zhí)行方法并將結(jié)果序列化返回
  • 客戶端接收到結(jié)果并進(jìn)行解析, 返回給本地調(diào)用者
  • 拿到最終結(jié)果

  • RPC適用于內(nèi)部網(wǎng)絡(luò)不同項(xiàng)目之間的通信, 如果是對(duì)外暴露的, 個(gè)人感覺(jué)還是通過(guò)接口的形式吧.

    使用RPC顯然會(huì)喪失一部分性能, 畢竟調(diào)用要走網(wǎng)絡(luò)IO, 盡管是內(nèi)網(wǎng), 仍然要比本地調(diào)用慢上一些, 但帶來(lái)了更好的可擴(kuò)展性和可維護(hù)性, 感覺(jué)還是不錯(cuò)的.

    之后如果用到的話, 拉個(gè)框架看看源碼.

    個(gè)人理解, 以上...

    總結(jié)

    以上是生活随笔為你收集整理的怎么改PHP_PHP实现RPC(简版)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。