導(dǎo)語
ibco是微信后臺大規(guī)模使用的c/c++協(xié)程庫,2013年至今穩(wěn)定運(yùn)行在微信后臺的數(shù)萬臺機(jī)器上。libco在2013年的時候作為騰訊六大開源項(xiàng)目首次開源,我們最近做了一次較大的更新,同步更新在https://github.com/tencent/libco?上。libco支持后臺敏捷的同步風(fēng)格編程模式,同時提供系統(tǒng)的高并發(fā)能力。
libco支持的特性
無需侵入業(yè)務(wù)邏輯,把多進(jìn)程、多線程服務(wù)改造成協(xié)程服務(wù),并發(fā)能力得到百倍提升;
支持CGI框架,輕松構(gòu)建web服務(wù)(New);
支持gethostbyname、mysqlclient、ssl等常用第三庫(New);
可選的共享?xiàng)DJ剑瑔螜C(jī)輕松接入千萬連接(New);
完善簡潔的協(xié)程編程接口
– 類pthread接口設(shè)計(jì),通過co_create、co_resume等簡單清晰接口即可完成協(xié)程的創(chuàng)建與恢復(fù); – 類__thread的協(xié)程私有變量、協(xié)程間通信的協(xié)程信號量co_signal (New); – 非語言級別的lambda實(shí)現(xiàn),結(jié)合協(xié)程原地編寫并執(zhí)行后臺異步任務(wù) (New); – 基于epoll/kqueue實(shí)現(xiàn)的小而輕的網(wǎng)絡(luò)框架,基于時間輪盤實(shí)現(xiàn)的高性能定時器;
libco產(chǎn)生的背景
早期微信后臺因?yàn)闃I(yè)務(wù)需求復(fù)雜多變、產(chǎn)品要求快速迭代等需求,大部分模塊都采用了半同步半異步模型。接入層為異步模型,業(yè)務(wù)邏輯層則是同步的多進(jìn)程或多線程模型,業(yè)務(wù)邏輯的并發(fā)能力只有幾十到幾百。隨著微信業(yè)務(wù)的增長,系統(tǒng)規(guī)模變得越來越龐大,每個模塊很容易受到后端服務(wù)/網(wǎng)絡(luò)抖動的影響。
異步化改造的選擇
為了提升微信后臺的并發(fā)能力,一般的做法是把現(xiàn)網(wǎng)的所有服務(wù)改成異步模型。這種做法工程量巨大,從框架到業(yè)務(wù)邏輯代碼均需要做一次徹底的改造,耗時耗力而且風(fēng)險(xiǎn)巨大。于是我們開始考慮使用協(xié)程。
但使用協(xié)程會面臨以下挑戰(zhàn):
業(yè)界協(xié)程在c/c++環(huán)境下沒有大規(guī)模應(yīng)用的經(jīng)驗(yàn);
如何控制協(xié)程調(diào)度;
如何處理同步風(fēng)格的API調(diào)用,如Socket、mysqlclient等;
如何處理已有全局變量、線程私有變量的使用;
最終我們通過libco解決了上述的所有問題,實(shí)現(xiàn)了對業(yè)務(wù)邏輯非侵入的異步化改造。我們使用libco對微信后臺上百個模塊進(jìn)行了協(xié)程異步化改造,改造過程中業(yè)務(wù)邏輯代碼基本無修改。至今,微信后臺絕大部分服務(wù)都已是多進(jìn)程或多線程協(xié)程模型,并發(fā)能力相比之前有了質(zhì)的提升,而libco也成為了微信后臺框架的基石。
libco框架
libco在框架分為三層,分別是接口層、系統(tǒng)函數(shù)Hook層以及事件驅(qū)動層。
同步風(fēng)格API的處理
對于同步風(fēng)格的API,主要是同步的網(wǎng)絡(luò)調(diào)用,libco的首要任務(wù)是消除這些等待對資源的占用,提高系統(tǒng)的并發(fā)性能。一個常規(guī)的網(wǎng)絡(luò)后臺服務(wù),我們可能會經(jīng)歷connect、write、read等步驟,完成一次完整的網(wǎng)絡(luò)交互。當(dāng)同步的調(diào)用這些API的時候,整個線程會因?yàn)榈却W(wǎng)絡(luò)交互而掛起。
雖然同步編程風(fēng)格的并發(fā)性能并不好,但是它具有代碼邏輯清晰、易于編寫的優(yōu)點(diǎn),并可支持業(yè)務(wù)快速迭代敏捷開發(fā)。為了繼續(xù)保持同步編程的優(yōu)點(diǎn),并且不需修改線上已有的業(yè)務(wù)邏輯代碼,libco創(chuàng)新地接管了網(wǎng)絡(luò)調(diào)用接口(Hook),把協(xié)程的讓出與恢復(fù)作為異步網(wǎng)絡(luò)IO中的一次事件注冊與回調(diào)。當(dāng)業(yè)務(wù)處理遇到同步網(wǎng)絡(luò)請求的時候,libco層會把本次網(wǎng)絡(luò)請求注冊為異步事件,本協(xié)程讓出CPU占用,CPU交給其它協(xié)程執(zhí)行。libco會在網(wǎng)絡(luò)事件發(fā)生或者超時的時候,自動的恢復(fù)協(xié)程執(zhí)行。
大部分同步風(fēng)格的API我們都通過Hook的方法來接管了,libco會在恰當(dāng)?shù)臅r機(jī)調(diào)度協(xié)程恢復(fù)執(zhí)行。
千萬級協(xié)程支持
libco默認(rèn)是每一個協(xié)程獨(dú)享一個運(yùn)行棧,在協(xié)程創(chuàng)建的時候,從堆內(nèi)存分配一個固定大小的內(nèi)存作為該協(xié)程的運(yùn)行棧。如果我們用一個協(xié)程處理前端的一個接入連接,那對于一個海量接入服務(wù)來說,我們的服務(wù)的并發(fā)上限就很容易受限于內(nèi)存。為此,libco也提供了stackless的協(xié)程共享?xiàng)DJ?,可以設(shè)置若干個協(xié)程共享同一個運(yùn)行棧。同一個共享?xiàng)O碌膮f(xié)程間切換的時候,需要把當(dāng)前的運(yùn)行棧內(nèi)容拷貝到協(xié)程的私有內(nèi)存中。為了減少這種內(nèi)存拷貝次數(shù),共享?xiàng)5膬?nèi)存拷貝只發(fā)生在不同協(xié)程間的切換。當(dāng)共享?xiàng)5恼加谜咭恢睕]有改變的時候,則不需要拷貝運(yùn)行棧。
libco協(xié)程的共享協(xié)程棧模式使得單機(jī)很容易接入千萬連接,只需創(chuàng)建足夠多的協(xié)程即可。我們通過libco共享?xiàng)DJ絼?chuàng)建1千萬的協(xié)程(E5-2670 v3 @ 2.30GHz * 2, 128G內(nèi)存),每10萬個協(xié)程共享的使用128k內(nèi)存,整個穩(wěn)定echo服務(wù)的時候總內(nèi)存消耗大概為66G。
協(xié)程私有變量
多進(jìn)程程序改造為多線程程序時候,我們可以用__thread來對全局變量進(jìn)行快速修改,而在協(xié)程環(huán)境下,我們創(chuàng)造了協(xié)程變量ROUTINE_VAR,極大簡化了協(xié)程的改造工作量。
因?yàn)閰f(xié)程實(shí)質(zhì)上是線程內(nèi)串行執(zhí)行的,所以當(dāng)我們定義了一個線程私有變量的時候,可能會有重入的問題。比如我們定義了一個__thread的線程私有變量,原本是希望每一個執(zhí)行邏輯獨(dú)享這個變量的。但當(dāng)我們的執(zhí)行環(huán)境遷移到協(xié)程了之后,同一個線程私有變量,可能會有多個協(xié)程會操作它,這就導(dǎo)致了變量沖入的問題。為此,我們在做libco異步化改造的時候,把大部分的線程私有變量改成了協(xié)程級私有變量。協(xié)程私有變量具有這樣的特性:當(dāng)代碼運(yùn)行在多線程非協(xié)程環(huán)境下時,該變量是線程私有的;當(dāng)代碼運(yùn)行在協(xié)程環(huán)境的時候,此變量是協(xié)程私有的。底層的協(xié)程私有變量會自動完成運(yùn)行環(huán)境的判斷并正確返回所需的值。
協(xié)程私有變量對于現(xiàn)有環(huán)境同步到異步化改造起了舉足輕重的作用,同時我們定義了一個非常簡單方便的方法定義協(xié)程私有變量,簡單到只需一行聲明代碼即可。
gethostbyname的Hook方法
對于現(xiàn)網(wǎng)服務(wù),有可能需要通過系統(tǒng)的gethostbyname API接口去查詢DNS獲取真實(shí)地址。我們在協(xié)程化改造的時候,發(fā)現(xiàn)我們hook的socket族函數(shù)對gethostbyname不適用,當(dāng)一個協(xié)程調(diào)用了gethostbyname時會同步等待結(jié)果,這就導(dǎo)致了同線程內(nèi)的其它協(xié)程被延時執(zhí)行。我們對glibc的gethostbyname源碼進(jìn)行了研究,發(fā)現(xiàn)hook不生效主要是由于glibc內(nèi)部是定義了__poll方法來等待事件,而不是通用的poll方法;同時glibc還定義了一個線程私有變量,不同協(xié)程的切換可能會重入導(dǎo)致數(shù)據(jù)不準(zhǔn)確。最終gethostbyname協(xié)程異步化是通過Hook __poll方法以及定義協(xié)程私有變量解決的。
gethostbyname是glibc提供的同步查詢dns接口,業(yè)界還有很多優(yōu)秀的gethostbyname的異步化解決方案,但是這些實(shí)現(xiàn)都需要引入一個第三方庫并且要求底層提供異步回調(diào)通知機(jī)制。libco通過hook方法,在不修改glibc源碼的前提下實(shí)現(xiàn)了的gethostbyname的異步化。
協(xié)程信號量
在多線程環(huán)境下,我們會有線程間同步的需求,比如一個線程的執(zhí)行需要等待另一個線程的信號,對于這種需求,我們通常是使用pthread_signal 來解決的。在libco中,我們定義了協(xié)程信號量co_signal用于處理協(xié)程間的并發(fā)需求,一個協(xié)程可以通過co_cond_signal與co_cond_broadcast來決定通知一個等待的協(xié)程或者喚醒所有等待協(xié)程。
總結(jié)
libco是一個高效的c/c++協(xié)程庫,提供了完善的協(xié)程編程接口、常用的Socket族函數(shù)Hook等,使得業(yè)務(wù)可用同步編程模型快速迭代開發(fā)。隨著幾年來的穩(wěn)定運(yùn)行,libco作為微信后臺框架的基石發(fā)揮了舉足輕重的作用。

熱AI工具

Undress AI Tool
免費(fèi)脫衣服圖片

Undresser.AI Undress
人工智能驅(qū)動的應(yīng)用程序,用于創(chuàng)建逼真的裸體照片

AI Clothes Remover
用于從照片中去除衣服的在線人工智能工具。

Clothoff.io
AI脫衣機(jī)

Video Face Swap
使用我們完全免費(fèi)的人工智能換臉工具輕松在任何視頻中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費(fèi)的代碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
功能強(qiáng)大的PHP集成開發(fā)環(huán)境

Dreamweaver CS6
視覺化網(wǎng)頁開發(fā)工具

SublimeText3 Mac版
神級代碼編輯軟件(SublimeText3)