中間件是一個軟件集合的名字,這些軟件位於操做系統和高層次分佈式編程平臺之間。中間件有時被分爲面向消息的中間件和麪向對象的中間件。而然現有的大多數中間件都是這兩種類型的混合體。固然,如今也有一種趨勢是由傳統的操做系統直接支持。操做系統老是包含了對通訊協議的支持。WEB服務的推動和程序世界從以城市爲中心到以協議爲中心的轉變,致使兩種中間件的價值觀:支持合適的協議或者提供簡化本地服務構造的結構。程序員
獨立的中間件產品,如消息隊列系統、事務處理監控系統或者集線器,已經慢慢地消失了。取而代之的是結合了中間件功能和某個特定構件框架的特殊的服務器。應用服務器結合了應用管理、數據事務、負載均衡和其餘的功能。集成服務器結合了協議轉換、數據變換、路由和其餘功能。工做流和複雜交互服務器結合了事件路由、決策和其餘功能。面試
當前的構建互連標準大多使用某種形式的事件傳播機制做爲實現構件實例裝配的手段。其思想是相對簡單的:構件實例在被指望監聽的狀態發生變化時發佈出特定的事件對象;事件分發機制負責接收這些事件對象,並把它們發送給對其感興趣的其餘構件實例;構件實例則須要對它們感興趣的事件進行註冊,由於它們可能需根據事件對象所標誌的變化改變其自身的狀態。ajax
「多線程會使你寢食難安。」Swaine在後來的著做中解釋,他的一些與此類似的論斷具備明顯的煽動性,可是他並不認爲這些論斷是錯誤的。多線程是指在同一個狀態空間內支持併發地進行多個順序活動的概念。相對於順序編程,多線程的引入爲編程帶來了至關大的複雜性。特別是,須要避免對多個線程共享的變量進行併發的讀寫操做。線程的同步使用某種形式的加鎖機制來解決此類問題,但這又帶來了一個新的問題:過於保守的加鎖或錯誤的加鎖順序均可能致使死鎖。redis
多線程主要關注於對程序執行進行更好的分配,發送併發請求的客戶端可以很好地觀察到這種分配。然而,獲取性能最大化的手段卻根本不依賴於多線程,而是儘可能在第一時間內以最快的速度處理用戶的請求。即便可以避免死鎖,同步也可能致使必定程度的性能損失。必須避免對常用的共享資源進行沒必要要的加鎖。跨線程的異常傳播也會致使處理非同步的異常變得更加困難。並且,使用多線程和複雜的互鎖機制將使得代碼調試變得異常困難。數據庫
顯然,在真正併發的環境下,這些問題無一不須要考慮。例如,若是構件實例運行在獨立的處理器上,就須要考慮併發請求的問題。能夠在處理一個請求時對某個構件實例進行徹底的加鎖,但這樣作可能會致使死鎖或者糟糕的響應時間。編程
回首看5年前代碼,我很喜歡寫多線程,線程池一類的程序。也是當時技術圈子裏對多線程的吹捧,又看了多線程方面的書籍,當時多線程、併發、消息隊列、分佈式事務的都寫進一個項目裏去了,一用多線程,發現有併發問題,因而各類加鎖,而後發現服務器斷電或者中途殺進程後再重啓的問題,因而各類記錄日誌、異常停止回滾操做,還有適應各類配置的服務器,加入了看門狗機制,最後項目是很是很是地複雜了,這無疑增長了程序的維護成本。性能優化
能夠經過入口的.NET線程池的代碼片斷感覺一下:服務器
while (CommonQueue.TaskQueue.Count > 0 && ThreadState.CurrTaskThread < ThreadState.MaxTaskThread) { WatchDog.FeedDog(); GenTask item = CommonQueue.TaskQueue.DeQueue(); ThreadState.CurrTaskThread++;//當前線程+1 genTaskServ.HandleTask(item);//處理任務,任務狀態到1 ThreadPool.QueueUserWorkItem(o => ProcExecute(item)); }
這裏有點炫技的成分。WatchDog.FeedDog();是給看門狗餵食物,每隔一分鐘看門狗的飢餓程度會增加,當看門狗快餓死的時候會強制同步線程數和實際線程池中的任務數。多線程
任務隊列代碼片斷:併發
Loggers.WriteLog("正在隊列初始化..."); CommonQueue.TaskQueue.Clear(); //先獲取須要重作的任務加入隊列 GenTaskService taskServ = new GenTaskService(); IList<GenTask> redoList = taskServ.GetRedoTask(); foreach (GenTask redo in redoList) { CommonQueue.TaskQueue.EnQueue(redo); } //獲取等待任務加入隊列 IList<GenTask> taskList = taskServ.GetTask(); foreach (GenTask task in taskList) { CommonQueue.TaskQueue.EnQueue(task); }
與消息隊列的通訊本就是一種異步通訊,又何須玩多線程脫了褲子放屁呢?乍一看又是炫技,實際上並非。這裏本能夠用一些redis、kafka等消息中間件,而且用它們自帶的數據持久化到磁盤來實現服務器斷電或重啓後的問題。而實際上,爲了下降實施和運維成本,改用了數據庫輪詢的原始方案替代消息隊列,而且本身編碼對系統故障與恢復進行處理,另外一個緣由則是oracle這類的商業數據庫的故障與恢復的處理顯然要比這些開源中間件要靠譜得多。
如今已經大道至簡,沒事儘可能不碰多線程了。前陣子我在搞一個服務作數據清洗,而後發現清洗的效率並不高,沒法在短期內跑出幾個月的數據,由於每條數據都調閱了兩家第三方公司的服務後還須要一些處理,的確單進程性能到了瓶頸。因而阿里項目經理來催,歷史數據怎麼辦?我說:「當前數據沒問題,歷史數據讓我性能優化一下便可。」
我發現你們對性能問題都很是熱心,項目經理想拉上阿里的技術人員來協助,主程同事更是直接提出用多線程解決。因而我很爲難地告訴項目經理說:「我只是反饋了一下項目的狀況,至於性能優化我本身會搞定,不要再找來技術外援了,會增長溝通成本。」聽完項目經理慌的一批。而後耐心地跟主程同事解說爲什麼不用多線程,當主程同事瞭解到多線程須要修改不少現有代碼並考慮併發和去重的問題後,意識到用多線程的話,這兩天就要通宵了,想着單就爲了之後面試上寫一條多線程的經驗而如此折磨本身何須呢,就放棄了多線程的念頭。
最後,用多進程的方式解決問題。最終以不修改現有代碼和邏輯,複製N份程序的jar包,每份jar包配置文件指定跑每月的數據,再經過縱向業務劃分,再拆一套,跑完歷史數據再關閉這些多餘進程,完美。
如今多線程和異步的使用,主要是在客戶端使用異步來減小未響應、提升用戶體驗。好比Web通常使用ajax的異步請求,而桌面應用程序開個線程異步請求一下,可是不少時候都是能夠用一個「正在加載」的窗口解決問題,每每也是考慮成本、可維護性後的最優解。
程序員的生活,每每就是這麼樸實無華,且枯燥。
當今的軟件圈子氛圍下,我仍是建議程序員在項目裏炫技,以上面oracle輪詢代替消息隊列爲例,假如遇到如上問題你炫技了,你就學會了一種消息隊列和多線程,別學我那樣到處爲公司各部門着想,還構建本身的可替代性。另外對於面試會多線程和消息隊列也是一個加分項,而後核心項目各類華而不實的炫技,讓項目難以移交和難以維護,還能構建本身的不可替代性。