ASP.NET 开发人员不必担心 Node 的五大理由
我把我上一篇博文献给了讨论为什么 ASP.NET 开发者需要了解 Node.js 。就像高中辩论赛那样,因为没有任何技术抉择(或者提议)可以凭空存在,我想试试看翻盘,于是我决定从对立面重新想几个 ASP.NET 开发者应该离 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... 鉴于它的功能和未来的规划。 明智的做法是对两者都保持关注,不要过早地下决定。
最后,对于不同的场景下选择哪种语言并没有一个唯一的答案,但是可以参考一些指导性的原则。 让我们在下一篇文章中对这些原则进行讨论。