[原]PHP -yar拓展源碼解讀一-序/packager篇

Yar是鳥哥惠新宸寫的一款并行RPC框架稿蹲,是國內(nèi)PHP圈內(nèi)主流的RPC方案選擇暴区,也是筆者公司服務(wù)化體系中的基礎(chǔ)組件紧武。為了更深入的理解其實現(xiàn)原理和熟悉該工具晌畅,抽空刷了遍源碼。整體來說閱讀難度不高<del>静袖,比看公司的legency代碼舒服</del>觉鼻。

研究之初是想寫個yar的swoole協(xié)程客戶端收尾的,但是隨著swoole4.1直接推出了Stream Hook勾徽,現(xiàn)在只能寫一下源碼分析了滑凉。話說起來這也是第二次為鳥哥的項目寫源碼分析了。讀完這個系列你應(yīng)該也能對yar的底層能有一個非常清晰的理解了喘帚。

出于模塊化的職業(yè)病拆成8章博客:

  1. PHP-yar拓展源碼解讀-packager篇
  2. PHP-yar拓展源碼解讀-protocol篇
  3. PHP-yar拓展源碼解讀-request/response篇
  4. PHP-yar拓展源碼解讀-client篇
  5. PHP-yar拓展源碼解讀-server篇
  6. PHP-yar拓展源碼解讀-transport篇
  7. PHP-yar拓展源碼解讀-concurrent_client篇

前三個章節(jié)講Yar的協(xié)議和核心數(shù)據(jù)結(jié)構(gòu)畅姊。
第四五七章講Yar PHP客戶端和服務(wù)端的實現(xiàn)。
第六七章主要講Yar網(wǎng)絡(luò)傳輸?shù)膶崿F(xiàn)細節(jié)吹由。
各個章節(jié)有一定依賴若未,篇幅也按順序增長,建議按序閱讀倾鲫。
另外為控制篇幅粗合,本系列中引用源碼會有少量刪減,特別一些非重要的條件編譯編譯分支乌昔,要研究某個功能的特定細節(jié)隙疚,建議另外直接翻源碼。

打包器

//yar_packager.h
typedef struct _yar_packager {
    const char *name;
    int  (*pack) (const struct _yar_packager *self, zval *pzval, smart_str *buf, char **msg);
    zval * (*unpack) (const struct _yar_packager *self, char *content, size_t len, char **msg, zval *rret);
} yar_packager_t;

底層使用yar_packager_t表示一個打包器磕道。打包器負責(zé)將一個IMP數(shù)組(Request章節(jié)中會提到)或的ISRPE數(shù)組(Resonse章節(jié)中會提到)進行序列化生成請求和響應(yīng)報文中載荷數(shù)據(jù)供屉。

一個yar_packager_t變量包含三個成員,name表示打包器的名字溺蕉,目前支持的三種打包方式對的name分別為MSGPACK伶丐,JSONPHP,
pack()unpack()分別為相關(guān)打包器的序列化和反序列化句柄疯特。
這是一種典型的OO風(fēng)格的C寫法哗魂,用結(jié)構(gòu)體和函數(shù)指針模擬抽象和多態(tài)。

打包器的注冊和獲取

//yar_packager.c
struct _yar_packagers_list {
    unsigned int size;
    unsigned int num;
    const yar_packager_t **packagers;
} yar_packagers_list;
PHP_YAR_API const yar_packager_t * php_yar_packager_get(char *name, int nlen) /* {{{ */ {
    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;
} /* }}} */

PHP_YAR_API int php_yar_packager_register(const yar_packager_t *packager) /* {{{ */ {

    if (!yar_packagers_list.size) {
       yar_packagers_list.size = 5;
       yar_packagers_list.packagers = (const yar_packager_t **)malloc(sizeof(yar_packager_t *) * yar_packagers_list.size);
    } else if (yar_packagers_list.num == yar_packagers_list.size) {
       yar_packagers_list.size += 5;
       yar_packagers_list.packagers = (const yar_packager_t **)realloc(yar_packagers_list.packagers, sizeof(yar_packager_t *) * yar_packagers_list.size);
    }
    yar_packagers_list.packagers[yar_packagers_list.num] = packager;

    return yar_packagers_list.num++;
} /* }}} */

yar_packagers_list作為list儲存管理多個yar_packager_t漓雅,注冊和獲取時均需要時遍歷該list獲取yar_packagers_list->packagers录别。list實現(xiàn)簡單,而且成員反正就3個邻吞,所以O(shè)(n)也不會有任何問題庶灿,transport模塊也是同樣的使用list實現(xiàn)注冊和獲取。

//yar_packager.c
zend_string *php_yar_packager_pack(char *packager_name, zval *pzval, char **msg) /* {{{ */ {
    char header[8];
    smart_str buf = {0};
    const yar_packager_t *packager = packager_name ?
        php_yar_packager_get(packager_name, strlen(packager_name)) : YAR_G(packager);

    if (!packager) {
        php_error_docref(NULL, E_ERROR, "unsupported packager %s", packager_name);
        return 0;
    }
    memcpy(header, packager->name, 8);
    smart_str_alloc(&buf, YAR_PACKAGER_BUFFER_SIZE /* 1M */, 0);
    smart_str_appendl(&buf, header, 8);
    packager->pack(packager, pzval, &buf, msg); 

    if (buf.s) {
        smart_str_0(&buf);
        return buf.s;
    }

    smart_str_free(&buf);

    return NULL;
} /* }}} */

序列化字符串前8個字符固定用于表示packager的類型吃衅,\0表示字符串末尾,中間的字符串由具體的packager對變量進行處理生成往踢。

打包器類型

如前問所述,packager目前的實現(xiàn)有3種:

  1. MSGPACK
  2. PHP
  3. JSON

php

// packager\php.c
int php_yar_packager_php_pack(const yar_packager_t *self, zval *pzval, smart_str *buf, char **msg) /* {{{ */ {
    php_serialize_data_t var_hash;

    PHP_VAR_SERIALIZE_INIT(var_hash);
    php_var_serialize(buf, pzval, &var_hash);
    PHP_VAR_SERIALIZE_DESTROY(var_hash);

    return 1;
} /* }}} */

zval * php_yar_packager_php_unpack(const yar_packager_t *self, char *content, size_t len, char **msg, zval *rret) /* {{{ */ {
    zval *return_value;
    const unsigned char *p;
    php_unserialize_data_t var_hash;
    p = (const unsigned char*)content;

    PHP_VAR_UNSERIALIZE_INIT(var_hash);
    if (!php_var_unserialize(rret, &p, p + len,  &var_hash)) {
        zval_ptr_dtor(rret);
        PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
        spprintf(msg, 0, "unpack error at offset %ld of %ld bytes", (long)((char*)p - content), len);
        return NULL;
    }
    PHP_VAR_UNSERIALIZE_DESTROY(var_hash);

    return_value = rret;
    return return_value;
} /* }}} */

該方式實際上就是就是我們平常使用的serialize(),unserialize函數(shù)徘层。

json

//packager\json.c
int php_yar_packager_json_pack(const yar_packager_t *self, zval *pzval, smart_str *buf, char **msg) /* {{{ */ {
#if ((PHP_MAJOR_VERSION == 5) && (PHP_MINOR_VERSION < 3))
    php_json_encode(buf, pzval);
#else
    php_json_encode(buf, pzval, 0); /* options */
#endif

    return 1;
} /* }}} */

zval * php_yar_packager_json_unpack(const yar_packager_t *self, char *content, size_t len, char **msg, zval *rret) /* {{{ */ {
    zval *return_value;

    php_json_decode(rret, content, len, 1, 512);

    return_value = rret;
    return return_value;
} /* }}} */

json方式使用json拓展的json_encode()峻呕,json_decode()對變量進行序列化操作

msgpack

//packager\msgpack.c
int php_yar_packager_msgpack_pack(const yar_packager_t *self, zval *pzval, smart_str *buf, char **msg) /* {{{ */ {
    php_msgpack_serialize(buf, pzval);
    return 1;
} /* }}} */

zval * php_yar_packager_msgpack_unpack(const yar_packager_t *self, char *content, size_t len, char **msg, zval *rret) /* {{{ */ {
    zval *return_value;
    ZVAL_NULL(rret);
    php_msgpack_unserialize(rret, content, len);
    return_value = rret;
    return return_value;
} /* }}} */

yar_packager_msgpack使用msgpack拓展的php_msgpack_serialize()php_msgpack_unserialize()函數(shù)對變量進行序列化。3種序列化方式中該種傳輸效率最高趣效,空間使用最小瘦癌, 使用該方式需要自行安裝msgpack-php拓展并在編譯yar時候使用--enable-msgpack參數(shù)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末跷敬,一起剝皮案震驚了整個濱河市讯私,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖斤寇,帶你破解...
    沈念sama閱讀 221,635評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件桶癣,死亡現(xiàn)場離奇詭異,居然都是意外死亡娘锁,警方通過查閱死者的電腦和手機牙寞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來莫秆,“玉大人间雀,你說我怎么就攤上這事∧魇海” “怎么了惹挟?”我有些...
    開封第一講書人閱讀 168,083評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長缝驳。 經(jīng)常有香客問我匪煌,道長,這世上最難降的妖魔是什么党巾? 我笑而不...
    開封第一講書人閱讀 59,640評論 1 296
  • 正文 為了忘掉前任萎庭,我火速辦了婚禮,結(jié)果婚禮上齿拂,老公的妹妹穿的比我還像新娘驳规。我一直安慰自己,他們只是感情好署海,可當(dāng)我...
    茶點故事閱讀 68,640評論 6 397
  • 文/花漫 我一把揭開白布吗购。 她就那樣靜靜地躺著,像睡著了一般砸狞。 火紅的嫁衣襯著肌膚如雪捻勉。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,262評論 1 308
  • 那天刀森,我揣著相機與錄音踱启,去河邊找鬼。 笑死研底,一個胖子當(dāng)著我的面吹牛埠偿,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播榜晦,決...
    沈念sama閱讀 40,833評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼冠蒋,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了乾胶?” 一聲冷哼從身側(cè)響起抖剿,我...
    開封第一講書人閱讀 39,736評論 0 276
  • 序言:老撾萬榮一對情侶失蹤朽寞,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后斩郎,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體脑融,經(jīng)...
    沈念sama閱讀 46,280評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,369評論 3 340
  • 正文 我和宋清朗相戀三年孽拷,在試婚紗的時候發(fā)現(xiàn)自己被綠了吨掌。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片半抱。...
    茶點故事閱讀 40,503評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡脓恕,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出窿侈,到底是詐尸還是另有隱情炼幔,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布史简,位于F島的核電站乃秀,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏圆兵。R本人自食惡果不足惜跺讯,卻給世界環(huán)境...
    茶點故事閱讀 41,870評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望殉农。 院中可真熱鬧刀脏,春花似錦、人聲如沸超凳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽轮傍。三九已至暂雹,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間创夜,已是汗流浹背杭跪。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留驰吓,地道東北人揍魂。 一個月前我還...
    沈念sama閱讀 48,909評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像棚瘟,于是被迫代替她去往敵國和親现斋。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,512評論 2 359

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,283評論 25 707
  • 朝辭故里暮難眠 海角紅塵踏遍 春風(fēng)一笑如溫玉 平波掠海燕 暗礁戲蒼天 蹣跚夜雨孤舟去 渡客杯中殘月 粼粼斜影不自哀...
    雪月寒竹幽夢影閱讀 147評論 6 1
  • 今天學(xué)習(xí)物品變身的三訣竅之一:動筆前要先觀察偎蘸。 書上舉了電風(fēng)扇的例子庄蹋。怎么我印象中電風(fēng)扇大多是四片葉子呢瞬内? 憑著印...
    春風(fēng)和煦2020閱讀 189評論 0 0