新聞中心
底層實(shí)現(xiàn)篇(chassis)

創(chuàng)新互聯(lián)是一家集網(wǎng)站建設(shè),賈汪企業(yè)網(wǎng)站建設(shè),賈汪品牌網(wǎng)站建設(shè),網(wǎng)站定制,賈汪網(wǎng)站建設(shè)報(bào)價(jià),網(wǎng)絡(luò)營(yíng)銷(xiāo),網(wǎng)絡(luò)優(yōu)化,賈汪網(wǎng)站推廣為一體的創(chuàng)新建站企業(yè),幫助傳統(tǒng)企業(yè)提升企業(yè)形象加強(qiáng)企業(yè)競(jìng)爭(zhēng)力??沙浞譂M(mǎn)足這一群體相比中小企業(yè)更為豐富、高端、多元的互聯(lián)網(wǎng)需求。同時(shí)我們時(shí)刻保持專(zhuān)業(yè)、時(shí)尚、前沿,時(shí)刻以成就客戶(hù)成長(zhǎng)自我,堅(jiān)持不斷學(xué)習(xí)、思考、沉淀、凈化自己,讓我們?yōu)楦嗟钠髽I(yè)打造出實(shí)用型網(wǎng)站。
【Configfile and Commandline Options】
glib2提供了config-file 解析和command-line option 解析功能。 其提供了將option 以相同方式暴露給調(diào)用者的方法,以及從Configfile 和Commandline獲取option 的功能。
所有option的解析過(guò)程都可以分為三步:
1. 提取 command-line 上的 basic option
- --help
- --version
- --defaults-file
2. 處理 defaults-file 文件
3. 處理其余 command-line option 并覆蓋 defaults-file 文件中的相同內(nèi)容
【 Plugin Interface 】
chassis 為 plugin 接口調(diào)用提供了基礎(chǔ)結(jié)構(gòu)。值得注意的是,其不是專(zhuān)門(mén)用于 MySQL 的,而是可以用于任何符合其接口要求的 plugin 。提供的功能包括:
- 解析 plugin 所在路徑
- 對(duì) plugin 的加載
- 對(duì) plugin 進(jìn)行版本檢查
- 提供 init 和 shutdown 函數(shù)
- 向 plugin 暴露配置選項(xiàng)
- 基于線程的 i/o
由于 chassis 不是僅針對(duì)于 MySQL 設(shè)計(jì)的,所以其可以用于加載任何種類(lèi)的 plugin ,只要該 plugin 提供了符合 chassis 要求的 init 和 shutdown 函數(shù)。
就 MySQL Proxy 本身而言,一般情況下加載的 plugin 為:
- plugin-proxy
- plugin-admin
【Threaded IO 】
從 MySQL Proxy 0.8 版本開(kāi)始,已經(jīng)添加了基于線程的 network-io 以使 proxy 能夠按照可用 CPU 和網(wǎng)卡的數(shù)量進(jìn)行線性擴(kuò)展。
使能 network-threading 功能只需要在啟動(dòng) proxy 時(shí)加入下面的參數(shù):
- --event-threads={2 * no-of-cores} (default: 0)
每一個(gè) event-thread 都通過(guò) "event_base_dispatch()" 進(jìn)行 loop ,并針對(duì) network-event 或者 time-event 執(zhí)行相關(guān)函數(shù)。這些線程只具有兩種狀態(tài):執(zhí)行函數(shù)狀態(tài)和 idle 狀態(tài)。如果其處于 idle 狀態(tài),則其能夠從 event-queue 中獲取要進(jìn)行等待的新 event ,然后將其添加到自身的等待列表中。
connection 是可以在多個(gè) event-thread 之間“跳躍”的:因?yàn)橹灰?idle 狀態(tài)的 event-thread 就能夠獲取到 wait-for-event request - 即具體的事件 - 并進(jìn)行等待,觸發(fā)后執(zhí)行相關(guān)代碼。無(wú)論何時(shí),只要當(dāng)前 connection 需要重新等待事件(也就是之前事件所對(duì)應(yīng)的操作已經(jīng)完成),其就會(huì)將自身從所在線程中 unregister ,之后重新向全局 event-queue 發(fā)送 wait-for-event request 以獲取新事件。
一直到 MySQL Proxy 0.8 版本,腳本代碼的執(zhí)行都是單線程方式:通過(guò)一個(gè)全局 mutex 來(lái)保護(hù) plugin 的接口操作。因?yàn)?connection 或者是處于發(fā)送包的狀態(tài),或者是處于調(diào)用 plugin 函數(shù)的狀態(tài),所以網(wǎng)絡(luò)事件將會(huì)按照并行方式被處理,僅在多個(gè) connection 需要調(diào)用同一個(gè) plugin 函數(shù)的時(shí)候才會(huì)無(wú)法并行。
chassis_event_thread_loop() 函數(shù)就是 event-thread 的主循環(huán)實(shí)體(其中調(diào)用 event_base_dispatch() 函數(shù)),而函數(shù) chassis_event_threads_init_thread() 用于設(shè)置要監(jiān)聽(tīng)的事件和對(duì)應(yīng)的回調(diào)。
下面的描述的是一種典型控制流(不包含連接池的情況)
涉及到的實(shí)體:EventRequestQueue, MainThread, WorkerThread1, WorkerThread2;
- --- [ label = "Accepting new connection "];
- MainThread -> MainThread [ label = "network_mysqld_con_accept()" ];
- MainThread -> MainThread [ label = "network_mysqld_con_handle()" ];
- MainThread -> EventRequestQueue [ label = "Add wait-for-event request" ];
- WorkerThread1 <- EventRequestQueue [ label = "Retrieve Event request" ];
- WorkerThread1 -> WorkerThread1 [ label = "event_base_dispatch()" ];
- ...;
- WorkerThread1 -> WorkerThread1 [ label = "network_mysqld_con_handle()" ];
- WorkerThread1 -> EventRequestQueue [ label = "Add wait-for-event request" ];
- WorkerThread2 <- EventRequestQueue [ label = "Retrieve Event request" ];
- WorkerThread2 -> WorkerThread2 [ label = "event_base_dispatch()" ];
- ...;
- WorkerThread2 -> WorkerThread2 [ label = "network_mysqld_con_handle()" ];
- WorkerThread2 -> EventRequestQueue [ label = "Add wait-for-event request" ];
- ...;
在上面的例子中,存在兩個(gè)用于處理 event 的工作線程(設(shè)置 --event-threads=2 ),每個(gè)線程都有自己的 event_base 。以 Proxy plugin 為例,首先將 network_mysqld_con_accept() 函數(shù)設(shè)置為被監(jiān)聽(tīng) socket 的回調(diào),當(dāng)有新連接發(fā)生時(shí)被觸發(fā)。該回調(diào)函數(shù)是注冊(cè)在主線程的 event_base 上的(同時(shí)也是全局 chassis 的 event_base)。在設(shè)置了連接相關(guān)結(jié)構(gòu) network_mysqld_con 后,程序?qū)⑦M(jìn)入到狀態(tài)機(jī)處理函數(shù) network_mysqld_con_handle() 中,此時(shí)仍然處于主線程中。
狀態(tài)機(jī)將進(jìn)行入起始狀態(tài):CON_STATE_INIT ,在當(dāng)前代碼實(shí)現(xiàn)中該狀態(tài)是主線程所必進(jìn)入的***個(gè)狀態(tài)。接下來(lái) MySQL Proxy 要做的事,要么是和 client 交互,要么是和 server 進(jìn)行交互(即或者等待 socket 可讀,或者主動(dòng)向 backend server 建立連接),而狀態(tài)機(jī)函數(shù) network_mysqld_con_handle() 將設(shè)置等待處理事件(對(duì)應(yīng)結(jié)構(gòu)體為 chassis_event_op_t)。簡(jiǎn)單來(lái)說(shuō)就是將 event 結(jié)構(gòu)添加到異步隊(duì)列中,具體講,就是通過(guò)向之前創(chuàng)建的 wakeup-pipe 的寫(xiě)文件描述符寫(xiě)入一個(gè)字節(jié),以產(chǎn)生一個(gè)文件描述符事件。這樣就可以向所有線程通知有新事件請(qǐng)求需要處理。
該 pipe 的實(shí)現(xiàn)是 libevent 對(duì)應(yīng)實(shí)現(xiàn)的一個(gè)翻版,其將各種事件與基于文件描述符的 event-handler 建立了對(duì)應(yīng)關(guān)系,采用的輪詢(xún)方式進(jìn)行處理:
- 工作線程中的 event_base_dispatch() 函數(shù)在其監(jiān)聽(tīng)的 fd 被觸發(fā)前處于阻塞監(jiān)聽(tīng)狀態(tài)(在具體實(shí)現(xiàn)中是有定時(shí)喚醒機(jī)制的)。
- 定時(shí)器事件,信號(hào)事件等都不能直接中斷 event_base_dispatch() 的運(yùn)行。
- 上述事件均是通過(guò) write(pipe_fd, ".", 1); 來(lái)觸發(fā) fd-event 的可讀,從而通過(guò)回調(diào)來(lái)進(jìn)行處理。
在文件 chassis-event-thread.c 中可以看到,通過(guò) pipe 實(shí)現(xiàn)了向工作線程通知:在全局 event-queue 中有東東需要處理。從函數(shù) chassis_event_handle() 可以看出,所有處于 idle 狀態(tài)的線程都有平等機(jī)會(huì)進(jìn)行事件處理,所以這些線程就能夠“并行的”從全局事件隊(duì)列中拉取 event ,并將其添加到自身的監(jiān)聽(tīng)事件列表中。
通過(guò)調(diào)用 chassis_event_add() 或者 chassis_event_add_local() 函數(shù)可以將 event 添加到 event-queue 中。一般情況下,所有事件都由全局 event_base 負(fù)責(zé)處理。只有在使用 connection pool 的情況下,才會(huì)強(qiáng)制將與特定 server connection 對(duì)應(yīng)的 events 投遞到特定線程,即將當(dāng)前 connection 加入到 connection pool 中的那個(gè)線程。
如果event 被投遞到全局 event_base 中,那么不同的線程都可以獲取這個(gè)事件,并可以對(duì)無(wú)保護(hù)的 connection pool 數(shù)據(jù)結(jié)構(gòu)進(jìn)行修改,可能會(huì)導(dǎo)致競(jìng)爭(zhēng)冒險(xiǎn)和崩潰。令這個(gè)內(nèi)部數(shù)據(jù)結(jié)構(gòu)成為具有線程安全性質(zhì)是 0.9 release 版本的工作,當(dāng)前只提供了最小限度的線程安全性。
典型情況是,某個(gè)線程會(huì)從 event queue 中獲取 request 信息(理論上,發(fā)送 wait request 的線程很可能也是處理這個(gè) request 的線程),并將其添加到自身以 thread-local-store 方式保存的event_base 中,并在對(duì)應(yīng) fd 有事件觸發(fā)時(shí)獲得通知。
該處理過(guò)程將一直持續(xù)到當(dāng)前 connection 被 client 或者 server 關(guān)閉,或者發(fā)生了導(dǎo)致的 socket 關(guān)閉的網(wǎng)絡(luò)錯(cuò)誤。此后將無(wú)法處理任何新的 request 。
單獨(dú)一個(gè)線程就足以處理任何添加到其 thread-local 的 event_base 上面的 event 。只有在一個(gè)新的 blocking I/O 操作發(fā)生時(shí)(一般來(lái)說(shuō)也就是重新進(jìn)入 event_base_dispatch() 阻塞時(shí)),event 才會(huì)在不同線程間被“跳躍著”處理,除此外沒(méi)有其他例外。所以理論上講,可能會(huì)出現(xiàn)一個(gè)線程處理了所有活躍的 socket 事件,而另一個(gè)線程一直處于 idle 狀態(tài)。
然而,由于等待網(wǎng)絡(luò)事件的發(fā)生的狀態(tài)是常態(tài)(意思就是實(shí)際處理的速度都很快),所以(從概率上講)活躍 connection 在所有線程中的分布必定是很均勻的,也就會(huì)減輕單個(gè)線程處理活躍 connection 的壓力。
值得注意的是,盡管在下面的說(shuō)明中沒(méi)有具體指出,主線程當(dāng)前會(huì)在 accept 狀態(tài)后參與到對(duì)后續(xù) event 的處理中。這不是一個(gè)非常理想的實(shí)現(xiàn)方式,因?yàn)樗?accept 動(dòng)作本身就需要在主線程中完成。但從另一方面講,這個(gè)問(wèn)題暫時(shí)也沒(méi)成為實(shí)際工作中的瓶頸顯現(xiàn)出來(lái):
涉及到的實(shí)體:Plugin, MainThread, MainThreadEventBase, EventRequestQueue, WorkerThread1, WorkerThread1EventBase, WorkerThread2, WorkerThread2EventBase;
- --- [ label = "Accepting new connection "];
- Plugin -> MainThread [ label = "network_mysqld_con_accept()" ];
- MainThread -> MainThread [ label = "network_mysqld_con_handle()" ];
- MainThread -> EventRequestQueue [ label = "Add wait-for-event request" ];
- WorkerThread1 <- EventRequestQueue [ label = "Retrieve Event request" ];
- WorkerThread1 -> WorkerThread1EventBase [ label = "Wait for event on local event base" ];
- ...;
- WorkerThread1EventBase >> WorkerThread1 [ label = "Process event" ];
- WorkerThread1 -> EventRequestQueue [ label = "Add wait-for-event request" ];
- WorkerThread2 <- EventRequestQueue [ label = "Retrieve Event request" ];
- WorkerThread2 -> WorkerThread2EventBase [ label = "Wait for event on local event base" ];
- ...;
- WorkerThread2EventBase >> WorkerThread2 [ label = "Process event" ];
- WorkerThread2 -> EventRequestQueue [ label = "Add wait-for-event request" ];
- ...;
網(wǎng)站題目:MySQLProxy:底層實(shí)現(xiàn)篇
標(biāo)題路徑:http://m.fisionsoft.com.cn/article/dhceipg.html


咨詢(xún)
建站咨詢(xún)
