各位老鐵在點贊、收藏的時候敢不敢報名小弟的直播分享,絕對有乾貨,絕對有驚喜!
一次早餐錢的投入,多是薪資的翻倍,多是視野的拓展!php
模塊愈來愈多,業務愈來愈複雜,RPC 就上場了,在 PHP 的世界裏,鳥哥的做品一直備受廣大網友的青睞。下面一塊兒學習下鳥哥的 PRC 框架 Yar 。segmentfault
RPC 採用客戶端/服務器模式。首先,客戶機調用進程發送一個有進程參數的調用信息到服務進程,而後等待應答信息。在服務器端,進程保持睡眠狀態直到調用信息的到達爲止。當一個調用信息到達,服務器得到進程參數,計算結果,發送答覆信息,而後等待下一個調用信息,最後,客戶端調用進程接收答覆信息,得到進程結果,而後調用執行繼續進行。
這和咱們外網 api 的原理不都一個樣麼?那麼咱們一塊兒看看高大上的 Yar 是怎麼在玩。後端
客戶端代碼,假設該服務設在局域網10.211.55.4
上api
<?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 client 是經過__call
這個魔術方法來實現遠程調用的,在Yar_client類裏面並無任何方法,當咱們在調用一個不存在的方式的時候,就會執行__call
方法,這個在框架中很是常見。網絡
在 yar 中規定的傳輸協議以下圖所示,請求體爲82個字節的yar_header_t
和8字節的打包名稱和請求實體yar_request_t
,在yar_header_t
裏面用body_len
記錄8字節的打包名稱+請求實體的長度;返回體相似,只是實體內容的結構體稍微不一樣,在reval
裏面纔是實際最後客戶端須要的結果。
整個傳輸以二進制流的形式傳送。
在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, 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_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;
而後經過傳入的name
和yar_packager_t
的name
作比較,相同則返回該實例
PHP_YAR_API yar_packager_t * php_yar_packager_get(char *name, int nlen TSRMLS_DC) /* {{{ */ { int i = 0; 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 版本的客戶端,這樣總算有所收穫,這份代碼也和咱們日常寫的業務邏輯仍是有些區別,二進制的東西居多,整個過程下來對網絡數據的傳輸有了更深入的理解和學習哈。
Github 項目地址: https://github.com/zhoumengka...