哦別誤會……我真的很喜歡 Node,而且我覺得它提出的概念和模式將在很長一段時間內,對服務端 Web 編程產生深遠的影響。即使隨著時間的推移 Node 過氣了,我們肯定可以從下一個牛逼玩意身上或多或少的感覺到它的影響(不管好的和/或壞的)。而在這期間,我們中很多人都會選擇它,幸福的在一起,生產。 不
哦別誤會……我真的很喜歡 Node,而且我覺得它提出的概念和模式將在很長一段時間內,對服務端 Web 編程產生深遠的影響。即使隨著時間的推移 Node 過氣了,我們肯定可以從下一個牛逼玩意身上或多或少的感覺到它的影響(不管好的和/或壞的)。而在這期間,我們中很多人都會選擇它,幸福的在一起,生產。
不過條條大路通羅馬,雖然現在 Node 可能是"當紅炸子雞",不過這不意味著在Web 伺服器上沒它不行。每天都有大批的實際有效的產品交貨,用那些巨無聊的老框架,比如說 ASP.NET, Java EE, Rails, PHP(!)還有數不清的各種。好吧,甚至還有些瘋嘿用 COBOL 搭起了 HTTP 服務! COBOL 的 MVC … diao炸了。我給320個贊(不過千萬別讓我去乾這事)。
對於在這個行業呆了近二十年有點看破紅塵的我來說(攤手),好處就是… Node 提出了一個比較新的有意思的方式來解決那些比較老的無聊的問題。這不是貶低Node。只是說,它不是唯一,昨天不是今天不是明天也不會是。
綜上……如果你是個 ASP.NET 程式員然後辭了職找 Node 的工作,來啊!給我發 email 或者 tweet,讓我知道你能幹啥 (要麼乾脆直接發簡歷……我們有在招聘哦!)。不過你如果還很迷茫,糾結於那些 Node-fanboy-love 的反對言論的話……繼續讀下去。
1. Node最好的功能(非同步I/O)在.NET中已經存在了
對Node最大的論據之一:Node通過使用非同步非阻塞模型來處理那些潛在的長時間運行的I/O操作。這意味著大多數在你web應用的伺服器端運行的工作(資料庫查詢、外部請求web service和其他的網路資源、訪問文件等等)不會在你處理請求的主線程中發生,可以放心的繼續處理其他對本站的HTTP請求。出於這個目的,Node會維護一個線程池,那些工作將被專門調度給一個可用的線程。你可以定義一個回調函數,當其中一個需要長時間運行的操作完成並返回時,Node將為你調用這個函數。這裡有一個使用mongoose.js API來查詢MongoDB的例子。註意回調函數的參數以及調用‘findOne’時缺乏返回值分配。
var query = Kitten.where({ color: 'white' }); // findOne()唯一的參數是一個當findOne()執行完成後被調用的函數 // 回調函數中包括錯誤處理以及查詢結果(可以為null)的處理 query.findOne(function (err, kitten) { if (err) return handleError(err); if (kitten) { console.log(kitten); } });
這類“連續傳遞”的編程風格有的時候對缺乏經驗的人來說很難把握,特別是當你的代碼嵌套了很多層的時候。 但對於我們熟悉的順序風格(同步)來說還是有很大優勢的:它為你的執行框架提供了一個自然點來做其他事情,而不是(在這個例子里)等待query.findOne()返回 。對Node來說,總有一些別的東西在處理髮來的請求。確實,Node最初的設計目的就是因為典型web伺服器端的生產瓶頸就是等待I/O的完成。Node的設計正是根據對這一點的觀察,提供了很多性能的優勢和擴展能力。
這裡是對於Node的高級設計的描述:
所以如果你正在用Node,這很棒。不過有趣的是你在今天的ASP.NET中也可以使用相同的模式!可能許多人熟悉最初由C#5.0引入的async和await關鍵字。在.NET的web應用中使用它們再配合其他輔助框架比如ASP.NET,Entity Framework等同樣可以取得像Node一樣的非阻塞執行風格。
這裡有一個簡單的ASP.NET MVC控制器向Entity Framework進行查詢的實現(這個模式換成Web API同樣可行):
private DataContext db = new DataContext(); // GET: /Employee/ public async Task<ActionResult> Index() { return View(await db.Employees.ToListAsync()); } // GET: /Employee/Details/5 public async Task<ActionResult> Details(int? id) { if (id == null) { return new HttpStatusCodeResult(HttpStatusCode.BadRequest); } Employee employee = await db.Employees.FindAsync(id); if (employee == null) { return HttpNotFound(); } return View(employee); }
有沒有發現每個方法是如何使用async/await來返回Task<ActionResult>而不是ActionResult?這個模式在語義上確保了和Node同樣的連續執行風格。await關鍵字表明瞭執行暫停的位置(並且當前線程可以先去處理其他請求),此時EF查詢在ASP.NET進程外執行。當EF查詢返回時,另一個線程用於立即恢複位於'await'後面代碼的執行。在ASP.NET中暫停/恢復給了我們如同Node中回調函數般的語義。實現雖然有差異,但基本原理是相同的,寶貴的伺服器端資源不能浪費在等待昂貴I/O操作的完成。
一個警告:Node的支持者會告訴你Node的優勢在於它的設計帶領你直接進入“成功的坑”。非同步是Node的預設模式,這對於Node來說非常容易,而同時你也可以在像ASP.NET這樣的框架中使用非同步,許多.NET開發者不這麼認為,但這無疑是正確的。 雖然Node應用和ASP.NET應用比起來可能會更加“非同步”,但不能理解成自身擁有更好的吞吐量或者更好的擴展性。應用的擴展性並不取決於你使用的框架,而在於你的應用本身是否有良好的擴展性。如果你現在在做APS.NET,在你覺得“ASP.NET缺乏擴展性”之前,花一些時間來學習並應用非同步模型 。
2. Node簡化的應用模型同樣在.NET中可用
另一個Node帶來的好處是它簡化的編程模型以及自主的步調,你可以瞭解並選擇一些更強大、更複雜的功能並將其引入。要構建一個“真正”的應用,Node需要掌握比ASP.NET更少的概念,這其實並不一定正確,但它確實給人這種感覺。這是主觀的理解,但許多Node的初學者認為這是正確的。
與之相比,ASP.NET MVC需要一系列連鎖的概念:HttpApplication、global.asax、web.config、模型、視圖、控制器、路由、行為、束等等。作為一名有經驗的ASP.NET開發者,我們認為理所當然 。但想象一下19歲的人準備著手做web開發,給他選擇到底是進入奇特的global.asax和web.config還是簡單地查找幾行Javascript,Node會受到青睞並不奇怪。每一代人都會無意識地帶著自豪進行挖掘:“昨天我們用一個馬拉的犁和一個乾草叉,以及一些進取心,架設了HTTP服務,我們喜歡它!” 並且每一代人都有另外一代的年輕人緊隨其後,轉著它們的眼睛然後向前進,使用新的工具和技術。
額...但是再一次我們看到ASP.NET學到了一些新招數和進步。在近幾年,微軟致力於對.NET開發者社區定義的OWIN(.NET的開放Web介面)規範的限制。 它的基本思想是從伺服器端的基礎設施對.NET的web應用解耦,為.NET的web桟(主機,伺服器,中間件和應用)定義一個正式的抽象層,同時也定義了相鄰層之間交互的介面,這增進了多個web主機間的可移植性。它同時也作為Katana項目的基礎,Katana項目是微軟對OWIN的實現,旨在通過傳統的ASP.NET管道、HttpListener、IIS、主機以及其他更多東西來處理HTTP請求。
OWIN和Katana借鑒了Node和其他許多輕量級框架,所以編程模型簡單親切。記得在Node里"hello world"有多簡單嗎?看看Katana:
public class Startup { public void Configuration(IAppBuilder app) { app.Run(context => { context.Response.ContentType = "text/plain"; return context.Response.WriteAsync("Hello World!"); }); } }
很容易吧!為任何給定的終結點簡單定義行為(上面的代碼使用的簡單行為為這個應用的所有終結點所共用)。註意代碼預設是非同步的,就像Node一樣。誰說老狗不能學新招數!
明確地講,上面的代碼在任何Katana主機上都可以運行:IIS,OWIN.exe(一個微軟提供的控制台host,類似於node.exe),或是你自定義的host實現。可以在多台主機和伺服器端運行同樣的代碼是Katana非常有用的功能,而Node沒有提供。
等等,還有!Katana有一個在常用IIS+ASP.NET伺服器基礎設施中的弱點,它依賴於System.Web,它不僅包括ASP.NET管道中的類型(HttpApplication,HttpContext,IHttpModule,IHttpHandler等),它還包括一些Web Forms的編程模型(pages,contorls,view state等)。你真的想把這些都帶到你現代的輕量級Katana應用程式域中?是的,ASP.NET團隊不認為你會使用其中任何一個。
所以他們創建了Helios項目。Helios是一個雛形項目,旨在具體化使用IIS(利用IIS的安全性、緩存、進程生命周期管理等)host的OWIN應用不需要將System.Web拖入你的應用程式域。 Helios是一個pre-alpha版本,所以我們需要小心比較。然而,相比ASP.NET host的應用,Helios確實證明可以明顯減少請求錢的記憶體消耗,它預示了.NET的web桟在未來將更加簡潔,更具擴展性。這對於ASP.NET開發者而言是好事,你可以現在著手研究OWIN和Katana。最終,讓ASP.NET團隊知道你需要一個官方完整支持、類似於Helios的應用桟!
3. Node的"到處都是Javascript"很棒!除非它不是
Node的其中一個論據是在你整個應用桟中你可以只使用Javascript。這確實有很多優勢。
除非你所在的團隊里沒有專職的JavaScript程式員。除非你願意花錢去招聘一些目前市場上高需求的科技人才。或者除非你願意用JavaScript重寫你的整個應用程式(或者你正從零開始……在這種情況下,你興許沒有重寫的問題,但你肯定有“我到哪可以找到專職JavaScript開發者”這個問題)。
對一個從更傳統的面向對象語言如C#人來說,掌握(或精通)JavaScript是件難事。原型繼承非常強大,但也很容易繞住你。變數的範圍“規則”,嗯,也很有趣。強制類型轉換,有時需要,貌似很隨機。“typeof NaN === ‘number’”。相信我,還會遇到更多。沒有什麼是不可逾越的,JavaScript的生產力是不僅此而已。像TypeScript這類的東西很有幫助,尤其對從C#轉移到JS。只是不要指望一夜之間就能成功。
這是原因的一部分,對所有時興的技術,Node對企業級來說仍然是新的。當然,一些喜歡嘗鮮的人已使用多年(企業中也有人員在嘗試時髦的應用框架)。但在短期內,如果你在Bigcorp.com上尋找能引入Node的人才,你可能需要付出一些努力。這並不意味著你可以不在乎Node……但要把握實際期望值。
另外,註意到客戶端JavaScript開發與伺服器端開發相同卻又截然不同很重要。語言的差別並不那麼大(確實不大),但概念和模式只有少量的重疊。你需要的人不僅要瞭解JavaScript,也要明白網路延遲、非同步、伺服器的安全、規模縮放、雲測試和部署以及高效的數據訪問,等等。即使是一個優秀的客戶端js開發者也未必完全理解這些。反過來,如果你的公司剛開始做.NET開發,我敢打賭你團隊中也有幾個人知道這些東西。他們也許不瞭解Node (目前還不),但他們仍有你可以利用的知識。
到這裡,我們看到了一方面問題。不管JavaScript和Node目前多火。重要的是它們是否適用於你的具體情況。是的,想一些未來的技術趨勢和“人才市場”走向是很重要的……但你對你掌握的東西有多大的信心?我們可以接受一些培訓來猜測明天……但就是他們所做的,猜測而已。一個全新的區域……C#可能不“酷”了,但它仍不會很快消失。
更多的在下麵。所以,如果你喜歡Node那為它而興奮吧,瞭解下它看看是否適合你和你的團隊。但你要首先專註於今天的需求,適當少關心你將採取的下一個閃光的新東西。當你準備好時它就會出現閃光。
4.如何你的ASP.NET應用程式規模不大但反應緩慢,也許這個問題在於你的設計
這種差異將阻礙你使用Node重寫你的Web應用以修複你當前在ASP.NET的實現中所存在問題。用Node重寫不會有顯著幫助,很可能沒有任何幫助。
這就像在Ye Ole軟體協會中的穆哈咖啡加入印度茶必然會導致溢出一樣。因此轉向Node不會簡單的解決你的問題.....這些問題的根源在於你,而不是微軟。
詢問你自己(坦誠的)適合下列描述的那種:
1.你和你的團隊富有遠見,卓越的軟體工程師將利用時間儘可能識別出ASP.NET中最佳的軟體工程實踐並將他們應用於你的項目。你的設計是 SOLID。你可以對何模塊進行單元測試。你知道你的的代碼測試覆蓋標準,並且你知道他為什麼不是100%。你冷靜的分析 Entity Framework,、NHibernate,原生ADO.NET和百萬級 micro-ORMs 框架的優缺點並且基於你項目的限製做出合理的選擇。你經過長時間的艱難思考決定你如何聚集於你客戶端和伺服器端應用程式的業務邏輯,考慮接下來的技術、工具、生產力和性能等等如何影響你的決策。你與合作伙伴勤奮的模擬現實世界的用例和情節,甚至在項目早期設置性能層級和載入測試場景,所以你在那時就擁有關於性能和規模的圖表。你可以無數次自由的描繪你的應用和數據訪問許可權,你也可以修改他們或者去掉那些沒有任何差別的附加功能。管理人員理解這些事情需要時間,然而你需要交出高質量的代碼。
2. 你和你的團隊目光短淺,非理性的“開發者”乾著沒有能力勝任的工作並且不顧一切保住工作。 根據定義,你從來沒有讀過關於ASP.NET 最佳實踐的書或者文章(如果你正在讀這篇文章,你可以在Stack Overflow上通過鏈接找到最佳實踐的資料,搜索GDD或者Google-driven development)。 你的設計原則是,“solid”。 曾在你團隊里待過的開發者寫了一些單元測試用例,當一些代碼改變時,你把它們註釋了,這些用例永遠不會再編譯(儘管你的項目經理在給CTO的代碼質量周報中仍然引用這些用例)。 有一次,你從換過三次工作的同事那裡聽說實體框架“很爛”,NHibernate“太複雜”,因此你編寫自己的DAL拼裝記憶體中的SQL語句(通過請求...沒有緩存),並保證你的動態資料庫欄位(ExtraColumn1,ExtraColumn2,…ExtraColumn25)都讀取到適當位置,在一個記錄的基礎上(或者…你從另一個同事那裡聽說實體框架EF“很棒”,因此整個伺服器端架構你採用EF對象圖的操作深層次結構,EF對象圖映射由一個從未見過7張表關聯的“資料庫架構師”設計的相容第五範式的資料庫)。你不知道分析器是什麼東西。 “SQL執行計劃”對你而言,聽起來像是要進監獄的事情。你“測試”系統好幾個月了,卻從未嘗試併發用戶請求,你的可擴展性就是“買更多的硬體”。 等等。
3. 介於第 1 和第 2 種情況之間.
老實說, 我們當中很多團隊既不屬於第 1 種也不屬於第 2 種, 而是介於 1 和 2 之間. 這很正常,這世上沒有完美無缺的項目, 也沒有完美無缺的團隊. 所以, 如果你比較傾向於上面提到的第 1 中情況, 而且在提高 ASP.NET 的性能和擴展性方面, 你已經竭盡所能, 做了你能做的, 結果依然不盡如人意, 那麼, 也許是時候改用其他框架了.
只有無能的人才會把自己的過錯推給工具. 自己的程式有問題怨 ASP.NET 這就是現代版的在編譯器中找 bug. 拉不出屎, 別怪茅坑… 問題可能來至你的團隊. 當然, 我不是說 ASP.NET 很完美, 一點問題也沒有 (事實上, ASP.NET 本身也有很多不足). 它不過是個工具, 一個有用的工具, 一個成千上萬的網站每天都在正常使用的工具. 其中一些網站(甚至是大部分的網站), 其複雜程度,訪問量要比你的高的多. 像這樣一個身經百戰, 被使用多年的框架, 不能滿足你的項目開發需求的可能性, 坦白講, 不大.
基於對“更好”一詞不同的定義,以上幾點並不是說Node比ASP.NET 更好或更糟。 它是說如果你計劃由ASP.NET 轉向Node,這很好,但是不要批評ASP.NET 在性能和可擴展性方面的問題。在“自由地申請更多可擴展性”方面,Node並不是神奇的魔法粉。 Node 只是一個類似於ASP.NET 的工具…使用得當,它的效果很好。草率地使用並且沒有清楚地理解它的優缺點,就像花了大量的時間和金錢重寫Node,最終會回到原點,就是你今天所處的位置。
我的朋友,這就像一聲相當可悲的低音號。
5. 孩子,“微軟正在消亡!/破產!/邪惡!/無聊!/土裡土氣!/等等。”絕不可能孕育一個科技戰略
現在,正如你讀到的,由於一點不理性、毫無緣由的討厭微軟,一些在企業級軟體領域工作的人正在由 ASP.NET 轉向 Node。這些人在他們的組織內部是值得信賴的決策者。 他們對商業上能夠產生很大下游影響(正面或負面)的技術決策負有責任。當你跳過他們 PPT摘要頁、第一頁或者第二頁查看後面的內容時,站不住腳的理由消失了,他們的說法幾乎可以歸結為“我不喜歡微軟,我不喜歡ASP.NET,我想做年輕人正在做的比較酷的事情”。
截至2013年12月31日,《金融時報》(FT)全球500強指數中微軟市值排名第四。 在同一季度,微軟收入創紀錄地達到245億美元。 微軟還沒有破產,遠非如此。 微軟Azure充滿活力,它為企業持續遷移到雲提供良好的定位,在這一領域,它富有競爭力,而且這一領域我們目前僅僅發展到初期階段。相對於傳統的Office辦公軟體,Office 365已成為一個可行的基於雲計算的替代產品。和其它公司一樣,微軟持續地大量地投資於研發領域(這怎麼會“無聊”呢?)。 微軟“土裡土氣”嗎?如果你基於企業級IT解決方案的定義而問這個問題,你是不是發現自己已經錯了?
不要誤會 … 在一個大的全新的項目開始,有很多理由選擇使用其它產品而非微軟的產品。 前期的許可費用可能難以接受,尤其相比於各種開源的軟體(儘管有 方法減輕那些費用)。在這一點上,至少微軟一些 核心技術的長期生存能力是不穩定的。基於 Satya Nadella升職為 CEO及他所有快樂的公開演講,我們仍然不知道他是否有能力利用好微軟 已有的優勢,並解決微軟面臨的問題。
然而,如果你已經投入巨資並顯著地建立了一個技術優勢,拋棄它重新開始通常是一個錯誤。 100年前Joel這樣寫到。以我的經驗,這通常是一個主觀上的決定,而不是一個客觀上的決定…不管討論哪種技術。
如果你是一個 CTO,面對遺留的 ASP.NET 代碼這座大山,你認為用 Node重寫將會解決所有的問題,你這在給你的公司、你的董事會、你的客戶幫倒忙。他們給你工資不是為了讓你在流行技術中換來換去。他們給你工資也不是為了讓你表達對微軟肥皂劇故事“本周流行哪個技術?”的討厭。“他們給你工資是為了實現商業價值 … 可預見的經濟上的價值,並且產品要有高質量。當開始做的時候,研發技術的選擇比我們想要別人認可的選擇要少的多。 在一個軟體項目中,大部分可變性是以人為中心。但是當我們能夠批評自己的缺點時,事情卻變得容易得多,”每個人都知道 ASP.NET 不能形成規模“。
讓我們假設你是一個基於ASP的網路商店,但是你有點擔心過度依賴單一的供應商。你有什麼方案能超越“重寫所有Node”?首先,你可以標識部分你的架構,它可能得益於各種開源的替代方案,用它們變更盒子中的ASP.NET。對於.NET來說,過去幾年生機勃勃的開源生態系統使得其更加地優雅的重要原因(主要原因?)。整個框架是偉大的,但是你也可以考慮像Massive或者 Simple.Data那樣的替代方案?也許你的數據訪問策略需要一個改頭換面,使用NoSQL資料庫MongoDB 或者 RavenDB,您的應用程式會受益?也許,你建立了一個適當的服務層,可以利用新形式,像responsive UI那樣直接的方式。這些類型的架構更改絕非易事,他們不被認為是輕量級的,但是他們可能需要解決具體的問題,卻面臨著沒有更大的預算去拋掉風險,重新開始。
其次,考慮更多激進的想法,比如在Mono平臺上運行,選擇自己的主機操作系統。現在ASP.NET和MVC在Mono上運行得相當好。 如果你對雲技術感興趣,但是不想使用Azure,你有其它的平臺運行ASP.NET… AWS EC2(亞馬遜可擴展的雲托管)虛擬機,AWS Elastic Beanstalk (亞馬遜的PaaS服務),小型的雲計算空間供應商如AppHarbor,還有最近發佈的支持.NET的紅帽OpenShift(雲計算服務平臺)。
最後,在決定選擇哪個技術和平臺前,確定你已經找到選擇的理由。 除了前面提到的許可費用,還有很多需要考慮的東西。所有權的總花費並不只有前期費用,也有使用時的費用 … 其中一些費用不容易進行量化。 使用 Node進行重寫,你的團隊能夠按時完成的機會成本是多少?ASP.NET應用開發遇到問題時,你可以 24*7打電話尋求技術支持而不是隨便在一個 Node開發者論壇上尋找答案,這對你而言價值是多少?你所處的地理區域是前端人才如 Node更多,還是主流技術的專家如 .NET和 Java更多? 找到、吸引、留住優秀的 Node開發者難度是多大?你是否準備好應對他們可能被挖走的問題? 目前你的團隊很多開發者都掌握了有價值的領域專業知識 … 當你轉向 Node時,你是否計劃繼續保留這些開發者?如何將專業知識轉化為一門新的技術架構?如果你計劃不再雇佣他們,你確定你已經準備好看著他們帶著來之不易的領域知識走出公司大門?
這裡沒有黑白… 它不像招聘”過時的”開發者:精通過時的技術、對招聘人員也沒有吸引力,這是一個成功的招聘策略。說句公道話,永遠堅持使用ASP.NET(或其它任何一門技術)也不像是正確的選擇。在你做出由ASP.NET轉向Node重大的決定之前,必須客觀而不是主觀地考慮上面的所有因素。保持謹慎,保持客觀。
希望我在文章里已經給你了一些思考的東西。 我很喜歡Node,在接下來的幾年我計劃花費更多的時間學習它。 但是我也很喜歡ASP.NET... 鑒於它的功能和未來的規劃。 明智的做法是對兩者都保持關註,不要過早地下決定。
最後,對於不同的場景下選擇哪種語言並沒有一個唯一的答案,但是可以參考一些指導性的原則。 讓我們在下一篇文章中對這些原則進行討論。