如今你們在和Java, PHP, .net寫應用程序時,都會用到一些成熟的服務框架,因此開發效率是比較高的。而在用C/C++寫服務器程序時,用的就五花八門了,有些人用ACE, 有些人用ICE(號稱比ACE強許多),等等,這類服務器框架及庫比較豐富,但入門門檻比較高,因此更多的人是本身直接寫服務器程序,初始寫時以爲比較簡 單,可時間久了,便會以爲難以擴展,性能低,容易出錯。其實,Postfix 做者爲咱們提供了一個高效、穩定、安全的服務器框架模型,雖然Postfix主要用做郵件系統的 mta,但其框架設計卻很是具備通用性。ACL(http://acl.sourceforge.net/) 的做者將Postfix的服務器框架模型抽取出來,造成了更加通用的服務器程序開發框架,使程序員在編寫服務器程序時能夠達到事半功倍的效果。本文主要介 紹了ACL中acl_master服務器程序(基於Postifx服務器程序框架)的設計及功能。git
2、框架設計圖程序員
以下圖所示:github
圖1--框架圖編程
master主進程爲控制進程,剛啓動時其負責監聽全部端口服務,當有新的客戶端鏈接到達時,master便會啓動子進程進行服務,而本身依然監控服務端 口,同時監控子進程的工做狀態;而提供對外服務的子進程在master啓動時,若沒有請求任務則不會被啓動,只有當有鏈接或任務到達時纔會被master 啓動,當該服務子進程處理完某個鏈接服務後並不當即退出,而是駐留在系統一段時間,等待可能的新鏈接到達,這樣當有新的鏈接到達時master就不會啓動 新的子進程,由於已經有處於空閒的子進程在等待下一個鏈接請求;當服務子進程空閒時間達必定閥值後,就會選擇退出,將資源所有歸還操做系統(固然,也能夠 配置成服務子進程永不退出的模式)。所以,能夠稱這種服務器框架爲協做式半駐留式服務器框架,下面將會對協做式和半駐留做進一步介紹。安全
3、協做方式服務器
Postfix服務器框架設計的很是巧妙,由於master畢竟屬於用戶空間進程,不能象操做系統那樣能夠控制每一個進程的運行時間片,因此master主進程必須與其服務子進程之間協做好,以處理好如下幾個過程:網絡
1)新鏈接到達時,master是該啓動新的子進程接管該鏈接仍是由空閒子進程直接接管多線程
2)master什麼時候應該啓動新的子進程併發
3)新鏈接到達,空閒子進程池中的子進程如何競爭接管該鏈接框架
4)子進程異常退出時,master如何處理新鏈接
5)空閒子進程如何選擇退出時間(空閒時間或服務次數應決定子進程的退出)
6)master如何知道各個子進程的工做狀態(是死了仍是活着?)
7)在不中止服務的前提下,服務子進程程序若是在線更新、如何添加新的服務、如何在線更新子進程配置
8)如何減小全部子進程與master之間的通信次數從而下降master的負載
4、流程圖
1) master主進程流程圖
圖2--master進程流程圖
Postifx 中的 master 主進程與各個子進程之間的IPC通信方式爲管道,因此管道的個數與子進程數是成正比的。若是管道中斷,則 master 認爲該管道所對應的子進程已經退出,若是是異常退出,master還須要標記該服務類子進程池以防止該類子進程異常退出頻繁而啓動也異常頻繁(若是子進程 啓動過於頻繁則會給操做系統形成巨大負載);另外,若是某類服務的子進程在服務第一個鏈接時就異常退出,則master認爲該服務有多是不可用的,因此 當有新的鏈接再到達時就會延遲啓動該服務子進程。
當服務子進程池中有空閒子進程時,master便會把該服務端口的監聽權讓出,從而該服務 的空閒子進程在該服務端口上接收新的鏈接。當某個子進程得到新的鏈接後便會當即通知master其已經處於忙狀態,master便當即查找該服務的子進程 進程池還有無空閒子進程,若是有則master依然不會接管該服務端口的監放任務;若是沒有了,則master當即接管該服務端口的監放任務,當有新的連 接到達時,master先檢查有沒有該服務的空閒進程,如有便讓出該服務端口的監聽權,若沒有便會啓動新的子進程,而後讓出監聽權。
2) 服務子進程流程圖
圖3--子進程流程圖
在master主進程剛啓動時,由於沒有任何服務請求,因此子進程是不隨master一塊兒啓動的,此時全部服務端口的監控工做是由master統一負責, 當有客戶端鏈接到達時,服務子進程才由master啓動,進而接收該新鏈接,在進一步處理客戶端請求前,子進程必須讓master進程知道它已經開始" 忙"了,好由master來決定是否再次接管該服務端口的監控任務,因此子進程首先向master發送「忙」消息,而後纔開始接收並處理該客戶端請求,當 子進程完成了對該客戶端的請求任務後,需向master發送「空閒」消息,以代表本身又能夠繼續處理新的客戶端鏈接任務了。這一「忙」一「閒」兩個消息, 便體現了服務子進程與master主進程的協做特色。
固然,服務子進程能夠選擇合適的退出時機:若是本身的服務次數達到配置的閥值,或本身空閒時間達到閥值,或與master主進程之間的IPC管道中斷(一 般是由master中止服務要求全部服務子進程退出時或master要求全部服務子進程重讀配置時而引發的),則服務子進程便應該結束運行了。這個中止過 程,一方面體現了子進程與master主進程之間的協做特色,另外一方面也體現了子進程半駐留的特色,從而體現子進程進程池的半駐留特性,這一特性的最大好 處就是:按需分配,當請求鏈接比較多時,所啓動運行的子進程就多,當請求鏈接任務較少時,只有少數的子進程在運行,其它的都退出了。
3) 小結
以上從總體上介紹了Postfix服務器框架模型中master主進程與服務子進程的邏輯流程圖,固然,其內部實現要點與細節遠比上面介紹的要複雜,只是 這些複雜層面別人已經幫咱們屏蔽了,咱們所要作的是在此基礎上寫出本身的應用服務來,下面簡要介紹了基於Postfix的服務器框架改造抽象的ACL庫中 服務器程序的開發方式,以使你們比較容易上手。
5、五種服務器框架模板簡介
要想使用ACL服務器框架庫開發服務器程序,首先介紹兩個個小名詞:acl_master服務器主進程與服務器模板。acl_master服務器主進程與 Postfix中的master主進程功能類似,它的主要做用是啓動並控制全部的服務子進程,這個程序不用咱們寫,是ACL服務器框架中已經寫好的;所謂 服務器模板,就是咱們須要根據本身的須要從ACL服務器框架中選擇一種服務器模型(即服務器模板),而後在編寫服務器時按該服務器模板的方式寫便可。爲什 麼還要選擇合適的服務器框架模板?這是由於Postfix自己就提供了三類服務器應用形式(單進程單鏈接進程池、單進程多鏈接進程池、觸發器進程池),這 三類應用形式便分別使用了Postfix中的三種服務器模板,此外,ACL中又增長了兩種服務器應用形式(多線程進程池、單進程非阻塞進程池)。下面分別 就這五種服務器框架模板一一作簡介:
1) 單進程單鏈接進程池:由 acl_master 主進程啓動多個進程組成進程池提供某類服務,但每一個進程每次只能處理一個客戶端鏈接請求
2) 單進程多鏈接進程池:由 acl_master 主進程啓動多個進程組成進程池提供某類服務,而每一個進程能夠同時處理多個客戶端鏈接請求
3) 觸發器進程池:由 acl_master 主進程啓動多個進程組成進程池提供定時器類服務(相似於UNIX中的cron),當某個定時器時間到達時,便由一個進程開始運行處理任務
4) 多線程進程池:由 acl_master 主進程啓動多個進程組成進程池提供某類服務,而每一個進程是由多個線程組成,每一個線程處理一個客戶端鏈接請求
5) 單進程非阻塞進程池:由 acl_master 主進程啓動多個進程組成進程池提供某類服務,每一個進程能夠併發處理多個鏈接(相似於Nginx, Lighttpd, Squid, Ircd),因爲採用非阻塞技術,該模型服務器的併發處理能力大大提升,同時系統資源消耗也最小;固然,該模型與單進程多鏈接進程池採用的技術都是非阻塞 技術,但該模型進行更多的應用封裝與高級處理,使編寫非阻塞程序更加容易
以上五種服務器方式中,因爲能夠根據須要配置成多個進程實例,因此能夠充分地利用多核的系統。其中,第5種的運行效率是最高的,固然其編程的複雜度要比其 它的高,而第1種是執行效率最低的,其實它也是最安全的(在Postfix中的smtpd/smtp 等進程就屬於這一類),而相對來講, 第4種在運行效率與編寫複雜度方面是一個比較好的折衷,因此在寫通常性服務器時,該服務器模型是做者推薦的方案,此外,第4種方案還有一個好處,能夠作到 對於空鏈接沒必要佔用線程,這樣也大大提供了併發度(即線程數能夠遠小於鏈接數)。
6、使用簡介
本節暫不介紹具體的編程過程,只是介紹一些配置使用過程。假設已經寫好了服務器程序:echo_server, 該程序能夠採用上面的 1), 2), 4), 5) 中的任一服務器模型來寫,假設採用了第4)種;另外,還假設:acl_master等全部文件安裝的根目錄爲 /opt/acl/, 主進程程序 acl_master 及 echo_server 安裝在 /opt/acl/libexec/, acl_master 主程序配置文件安裝在 /opt/acl/conf/,echo_server 配置文件安裝在 /opt/acl/conf/service/, 日誌文件目錄爲 /opt/acl/var/log/, 進程號文件目錄爲 /opt/acl/var/pid/。好比,你讓 echo_server 的服務端口爲 6601,服務地址爲 127.0.0.1, 它只是提供簡單的 echo 服務。
你能夠運行 /opt/acl/sh/start.sh 腳原本啓動 acl_master 主進程(用 ps -ef|grep acl_master 會看到 acl_master 進程存在 ,但 ps -ef|grep echo_server 卻沒有發現它的存在),而後你在一個Unix終端上 telnet 127.0.0.1 6601, 在另外一個終端上 ps -ef|grep echo_server 就會發現有一個 echo_server子進程了,而後在 telnet 6601 的終端上隨便輸入一些字符串,便會當即獲得回覆,關閉該 telnet 鏈接,用 ps -ef|grep echo_server 會發現該進程依然存在,當再次 telnet 127.0.0.1 6601 時,該echo_server進程又繼續爲新鏈接提供服務了。能夠試着多開幾個終端同時 telnet 127.0.0.1 6601,看看運行效果如何?
注意,服務子進程的配置文件格式須要與其所採用的模板類型一致;進程池中最大進程個數、進程中線程池最大線程個數、進程最大服務次數、空閒時間等都是能夠以配置文件中指定的。
參考文章:
1) 快速建立你的服務器程序--single進程池模型
2) 基於POSTFIX的服務器框架的服務器程序設計
3) 開發多線程進程池服務器程序---acl 服務器框架應用
4) 利用ACL庫快速建立你的網絡程序--ACL_VSTREAM 流的使用
下載:http://sourceforge.net/projects/acl/
svn:svn checkout svn://svn.code.sf.net/p/acl/code/trunk acl-code
github:https://github.com/zhengshuxin/acl