PHP 教父鳥哥 Yar 的原理分析

各位老鐵在點贊、收藏的時候敢不敢報名小弟的直播分享,絕對有乾貨,絕對有驚喜!
一次早餐錢的投入,多是薪資的翻倍,多是視野的拓展!php

模塊愈來愈多,業務愈來愈複雜,RPC 就上場了,在 PHP 的世界裏,鳥哥的做品一直備受廣大網友的青睞。下面一塊兒學習下鳥哥的 PRC 框架 Yar 。segmentfault

揭開 Yar 神祕面紗

RPC 採用客戶端/服務器模式。首先,客戶機調用進程發送一個有進程參數的調用信息到服務進程,而後等待應答信息。在服務器端,進程保持睡眠狀態直到調用信息的到達爲止。當一個調用信息到達,服務器得到進程參數,計算結果,發送答覆信息,而後等待下一個調用信息,最後,客戶端調用進程接收答覆信息,得到進程結果,而後調用執行繼續進行。
這和咱們外網 api 的原理不都一個樣麼?那麼咱們一塊兒看看高大上的 Yar 是怎麼在玩。後端

Yar 功能演示

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

<?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 協議分析

1449906349384302

在 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, 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,
};

整理了總體的執行流程以下圖

1449981251375331

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;
    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...

相關文章
相關標籤/搜索