PHP 教父鳥哥 Yar 的原理分析


模塊越來越多,業務越來越復雜,RPC 就上場了,在 PHP 的世界里,鳥哥的作品一直備受廣大網友的青睞。下面一起學習下鳥哥的 PRC 框架 Yar 。

揭開 Yar 神秘面紗

RPC 采用客戶端/服務器模式。首先,客戶機調用進程發送一個有進程參數的調用信息到服務進程,然后等待應答信息。在服務器端,進程保持睡眠狀態直到調用信息的到達為止。當一個調用信息到達,服務器獲得進程參數,計算結果,發送答復信息,然后等待下一個調用信息,最后,客戶端調用進程接收答復信息,獲得進程結果,然后調用執行繼續進行。 這和我們外網 api 的原理不都一個樣么?那么我們一起神馬影院看看高大上的 Yar 是怎么在玩。

Yar 功能演示

客戶端代碼,假設該服務設在局域網10.211.55.4

<?php class RpcClient { // RPC 服務地址映射表 public static $rpcConfig = array( "RewardScoreService" => "http://10.211.55.4/yar/server/RewardScoreService.class.php", ); public static function init($server){ if (array_key_exists($server, self::$rpcConfig)) { $uri = self::$rpcConfig[$server]; return new Yar_Client($uri); } } } $RewardScoreService = RpcClient::init("RewardScoreService"); var_dump($RewardScoreService->support(1, 2));

服務器端代碼

<?php class RewardScoreService { /** * $uid 給 $feedId 點贊 * @param $feedId interge * @param $uid interge * @return void */ public function support($uid,$feedId){ return "uid = ".$uid.", feedId = ".$feedId; } } $yar_server = new Yar_server(new RewardScoreService()); $yar_server->handle();

訪問結果如下

uid = 1, feedId = 2

Yar 遠程調用的實現原理

實際呢,yar client 是通過__call這個魔術方法來實現遠程調用的,在Yar_client類里面並沒有任何方法,當我們在調用一個不存在的方式的時候,就會執行__call方法,這個在框架中非常常見。

Yar 協議分析

在 yar 中規定的傳輸協議如下圖所示,請求體為82個字節的yar_header_t和8字節的打包名稱和請求實體yar_request_t,在yar_header_t里面用body_len記錄8字節的打包名稱+請求實體的長度;返回體類似,只是實體內容的結構體稍微不同,第九影院reval里面才是實際最后客戶端需要的結果。

整個傳輸以二進制流的形式傳送。

Yar 數據傳輸的整體流程分析

yar_transport.h中,定義了yar_transport_t結構體,先不考慮並行處理的接口,以socket傳輸協議為例子學習,代碼簡化一些如下:

typedef struct _yar_transport_interface { void *data; int (*open)(struct _yar_transport_interface *self, char *address, uint len, long options, www.10rz.net/char **msg TSRMLS_DC); int (*send)(struct _yar_transport_interface *self, struct _yar_request *request, char **msg TSRMLS_DC); struct _yar_response * (*exec)(struct _yar_transport_interface *self, struct _yar_request *request TSRMLS_DC); int (*setopt)(struct _yar_transport_interface *self, long type, void *value, void *addition TSRMLS_DC); int (*calldata)(struct _yar_transport_interface *self, yar_call_data_t *calldata TSRMLS_DC); void (*close)(struct _yar_transport_interface *self TSRMLS_DC); } yar_transport_interface_t; typedef struct _yar_transport { const char *name; struct _yar_transport_interface * (*init)(TSRMLS_D); void (*destroy)(yar_transport_interface_t *self TSRMLS_DC); yar_transport_multi_t *multi; } yar_transport_t;

然后在transports/socket.c中定義了yar_transport_socket

yar_transport_t yar_transport_socket = { "sock", php_yar_socket_init, php_yar_socket_destroy, };

整理了整體的執行流程如下圖

Yar 數據的打包和解包

鳥哥在yar_packager.c中首先定義了一個結構體,初始化的時候會把各個yar_packager_t注冊到**packagers數組中。

struct _yar_packagers_list { unsigned int size; unsigned int num; yar_packager_t **packagers; } yar_packagers_list;
typedef struct _yar_packager { const char *name; int (*pack) (struct _yar_packager *self, zval *pzval, smart_str *buf, char **msg TSRMLS_DC); zval * (*unpack) (struct _yar_packager *self, char *content, size_t len, char **msg TSRMLS_DC); } yar_packager_t;

然后通過傳入的nameyar_packager_tname做比較,相同則返回該實例

PHP_YAR_API yar_packager_t * php_yar_packager_get(char *name, int nlen TSRMLS_DC) /* {{{ */ { int i = 0;www.10rz.net for (;i<yar_packagers_list.num;i++) { if (strncasecmp(yar_packagers_list.packagers[i]->name, name, nlen) == 0) { return yar_packagers_list.packagers[i]; } } return NULL; } /* }}} */

親密接觸完畢。紙上得來終覺淺,絕知此事要躬行。這篇博客只能是輔助大家在看源碼時一起分析,覺不能拋開源碼僅僅看這篇博客。

怎么樣才能對這個內容真正的掌握呢,所以我有折騰了一個Java 版本的客戶端,這樣總算有所收獲,這份代碼也和我們平常寫的業務邏輯還是有些區別,二進制的東西居多,整個過程下來對網絡數據的傳輸有了更深刻的理解和學習哈。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM