架構設計:進程仍是線程?是一個問題!

對於「進程仍是線程?」這個問題,也常常困擾着那些進行軟件架構設計的傢伙。因此今天打算聊一下我對這個問題的體會。假如你還搞不清楚線程和進程的區別,請先找本操做系統原理的書好好拜讀一下,再回來看帖。程序員

因爲這個問題很容易引起口水戰,事先聲明以下:多進程和多線程,沒法一律而論地說誰比誰好。所以本帖主要描述特定場景(與我所負責的產品相關)下,進程和線程的權衡經驗,僅供大夥兒參考。數據庫

因爲特定場景是本帖討論的前提,先說說我目前負責的產品的特色:業務邏輯比較複雜、業務數據量比較大、對數據實時處理的性能要求比較高、對健壯性和安全性要求比較高、要求跨平臺(包括操做系統、數據庫)、某些狀況下須要分佈部署。編程

上面說了一大堆,其實有很多的應用系統符合上述特色,好比:某些網絡遊戲服務器、某些金融行業的業務系統、某些電子商務的交易系統等等。若是你正在從事的是相似的應用系統的設計,但願我下面介紹的經驗對你有幫助。 安全


★進程【顆粒度】問題 服務器

大夥兒應該明白,進程和線程都是處理併發(concurrency)的手段。對於上述這種比較複雜的系統,若是你企圖所有用進程(見「注1」)或者所有用線程(見「注2」)來處理併發,估計會死得很難看。因此,關鍵問題就是如何在進程和線程之間進行平衡(也就是肯定進程顆粒度的問題)。注1網絡

所謂「所有用進程」,就是全部的併發都使用進程實現(所以每一個進程只有一個線程)。這種設計在某些平臺上(好比 Windows)會致使嚴重的性能問題。

注2多線程

所謂「所有用線程」,就是全部的併發都使用線程實現(所以整個系統只有一個進程)。這種設計的健壯性極差(一個致命錯會致使整個系統崩潰),並且更別提分佈部署了。

 我我的建議,儘可能以業務邏輯的單元來劃分進程。這樣作的好處有以下幾點:架構

 

 ★「以業務邏輯爲單元」劃分進程的好處併發

 ◇避免扯皮
通常來講,某個固定業務邏輯的開發人員也是相對固定的。若是業務邏輯對應的某個進程崩潰了,測試人員容易快速定位肇事者,而後直接提交Bug給他/她。反之,一個進程搞得太龐大,N 多人摻和在裏面,一旦進程崩潰了,相關編程人員之間很容易互相扯皮,不利於維護安定團結的局面;另外,因爲測試人員常常搞不清楚 Bug 屬於誰,常常給錯 Bug,也容易製造人民內部矛盾。由此能夠看出,【相對細】的進程顆粒度可以避免一些管理上的麻煩。因爲 XXX 常常教導咱們:「穩定壓倒一切」,因此該優勢列第一條。
 ◇健壯性、容錯性 編程語言

通常來講,開發人員的水平良莠不齊,優秀的畢竟是少數(具體參見「二八原理系列」的帖子)。因此不免會有菜鳥程序員搞出低級錯誤,而有些低級錯誤是致命的,會致使進程的崩潰。若是你是以業務邏輯劃分進程,一個業務邏輯的進程崩潰,對其它業務邏輯的影響不大(除非是該業務邏輯的依賴方);所以就不會出現「所有用線程」致使的弊端。 

◇分佈式 

我常遇見的分佈式部署需求,通常都是按照業務邏輯的維度來劃分。好比系統中有一個認證模塊,裏面包含有敏感的用戶認證信息。這時候客戶就會要求把該模塊單獨部署在一臺通過安全加固的主機中(以防階級敵人搞破壞)。若是是以業務邏輯爲單位劃分進程,要知足上述的部署需求就相對容易了(只要再配合恰當的進程間通信機制,下面會提到)。另外,支持分佈式部署還能夠順帶解決性能問題。好比某個業務邏輯模塊特別消耗硬件資源(好比:內存、CPU、硬盤、帶寬),就能夠把它拿出去單獨放一臺機器上跑。

 ◇跨編程語言 

這個好處可能不少人容易忽略。通常來講,每一個編程語言都有各自的優缺點。若是你經過業務邏輯劃分進程,就能夠根據不一樣的業務邏輯的特色來選擇合適的編程語言。

 

 ★進程間通信(如下簡稱 IPC)問題 

既然不可能把整個系統放入一個進程,那就必然會碰到 IPC 的問題。下面就來講一下該如何選擇 IPC。
各類操做系統裏面,有不少稀奇古怪的 IPC 類型。因爲要考慮跨平臺,首先砍掉一批。剩下的 IPC 類型中,可以進行數據傳輸的 IPC 就很少了,主要有以下幾種:套接字(如下簡稱 Socket)、共享內存、管道、文件。
其中 Socket 是俺強烈推薦的 IPC 方式,理由以下:使用 Socket 能夠自然地支持分佈式部署;使用 Socket 能夠比較容易地實現多種編程語言的混合;使用 Socket 還能夠省掉了一大坨「鎖操做」的代碼。
列位看官中,或許有人在擔憂 Socket 的性能問題,其實大可沒必要多慮。當兩個進程在【本機】上進行 Socket 通信時,因爲可使用 localhost 環回地址,數據不用通過物理網卡,操做系統內核還能夠進行某些優化。這種狀況下,Socket 相對其它幾種IPC機制,不會有太大的性能誤差。
最後再補充一下,Socket 方式也能夠有效防止扯皮問題。舉個例子:張三寫了一個進程 A,李四寫了一個進程 B,進程 A 經過 Socket 方式發數據給進程B。忽然有一天,兩個進程的通信出故障了。而後張三就說是李四接收數據出錯;李四就說張三發送數據出錯。這時候怎麼辦捏?很簡單,隨便找個 Sniffer 軟件當場抓一下數據包並 Dump 出來看,問題就水落石出了。  


★爲啥還要線程? 

上面說了這麼多進程的好處,有同窗要問了:「那線程有什麼用捏?」總的來講,使用線程出於兩方面的考慮:性能因素和編碼方便。 
◇性能因素 

因爲某些操做系統(好比 Windows)中的進程比較重型,若是【頻繁】建立進程或者建立大量進程,會致使操做系統的負載太高。舉例以下:

假設你要開發一個相似 Web Server 的應用。你針對每個客戶端請求建立一個對應的進程用於進行數據交互(是否是想起了古老的 CGI)。一旦這個系統擴容,用戶的併發鏈接數一增長,你的應用立馬死翹翹。

上面的例子代表,跨平臺軟件系統的進程數要保持相對穩定。若是你的進程數會隨着某些環境因素呈線性增加,那就至關不妙了(順帶說一下,若是線程數會隨着環境因素呈線性增加,也至關不妙)。而根據業務邏輯的單元劃分進程,順便能達到「進程數的相對穩定」的效果。

 ◇編碼方面

 因爲業務邏輯內部的數據耦合比較緊密。若是業務邏輯內部的併發也用進程來實現,可能會致使大量的 IPC 編碼(任意兩個進程之間只要有數據交互,就得寫一坨 IPC 代碼)。這或許會讓相關的編程人員怨聲載道。固然,編碼方面的問題也不是絕對的。假如你的系統有很成熟且方便易用的IPC庫,能夠比較透明地封裝IPC相關操做,那這方面的問題也就不存在了。

相關文章
相關標籤/搜索