技术开发 频道

国外开发者:为什么我不再用.NET框架

  狭隘的心理

  前些年有个运动叫做ALT.NET。该运动是全部是关于寻找我们自身之外的更广阔的开发社区以作为一个整体,并汇聚不同的部分。有趣的是,那是StructureMap、Autofac、NuGet、ASP.NET MVC和许多其它工具的灵感来源。在传统的.NET的圈子里,这个运动受到了很多的不屑和鄙视。我把这看作是,作为一个整体的社区普遍的狭隘心理和怠惰的一个极大的例证。(的确,它们中的一些可能会消失,进而以包括Redis,MongoBD还有其它的不同的技术而出现。)

  有这么多很棒的方案在那里。假定微软已注定是唯一正确之路的想法是荒谬的。如果是这样的话,我们就都还在使用Visual Studio的设计工具去拖放按钮和链接元素到一个WebForm的界面上,我们会设定了该按钮并且依赖ViewState以帮助我们与可怕的HTTP所带来的恐惧隔开。我从我的一个部署的代码库中最后一个WebForm中摆脱的那一天,是个光荣的值得庆贺的日子。

  谁又曾想过“网络控制”是个好主意?很显然我考虑过因为我喝了Kool-Aid(译者注:卡夫公司出品的饮料,这里意指明知是注定的或有危险的仍然去做,有负面涵义)并且完全接受它。它狠咬了我。见过2MB的ViewState吗?

  性能杀手

  C,Java和C#中典型的多线程范例都强烈推荐使用锁和互斥。对于锁来说有个隐藏的开销:它们慢得难以忍受。使用Disruptor(JVM中的无锁的环形缓存[译者注:实际上就是拥有一个序号指向下一个可用元素的数组]),你可以很容易得每秒处理20M以上的事件。在.NET中使用规定的“非常好的实践”等任何超过每秒十几次的传输,都被认为是体面又好的性能表现,在这一点上来说你仅仅需要更大/更好/更多的硬件设备。事实上,我见过第三方客户端库(Rabbit,Couch,Mongo等等)中锁语句遍布整个代码。即使在我的代码中没有任何的并发,默认的和首选的方法都用了锁。

  无锁的、事件驱动的方法允许你大幅降低硬件和资金支出。大部分应用程序可以轻易地运行在两台机器上,第二台机器仅仅在冗余和失效备援时是必须的,以防因为硬件相关的问题导致第一台机器不可用的时候起作用。

  这个问题的另一个方面是调用网络和磁盘子系统的传统方式:同步,阻塞代码。如果你需要多个并发的HTTP请求,你需要更多的线程。大多数人不知道的是,为维持线程多出的1-2MB和上下文切换线程的需求,使得CPU内核消耗所有的时间颠簸在上下文切换上而不是做真正的工作。所以现在我们得到了在一个应用程序中数百或数千的线程,占用了RAM,并造成CPU停滞不前。还有个更好的方式。

  Netty/NIO (JVM),Erlang,Node,Gevent (Python)和Go都支持使用事件驱动的子系统操作(选择/epoll[译者注:Linux内核中的一种可扩展IO事件处理机制]/kqueue[译者注:FreeBSD的可扩展的事件通知接口])。这就意味着当等待数据包被tx/rx跨网络的时候,CPU可以自由地去做其它,重要的工作。因为JVM的成熟,Netty可以认为是做这项工作最快的,但我喜欢Go用Goroutines操作这个的方式—它简单,优雅,很容易推理,没有像意大利面条一样的回调。

  SQL Server

  作为一名.NET开发者,当你开始一个新的工程时,有一些事是你通常会去做的:

  ˙创建一个新的solution;

  ˙将其部署到Team Foundation Server(译者注:Microsoft 应用程序生命周期管理 (ALM) 解决方案的核心协作平台);

  ˙IIS中建立相应的网站入口;

  ˙创建一个新的SQL Server数据库;

  ˙在solution中关联Entity Framework(通常是2010年之后创建的工程);

  ˙开始设计你的数据库和ActiveRecord实体。

  在大多数情况下这不是编写代码的正确方式。当然它可能在某些情况下有效,但是作为一个“默认的架构”它并不是你想要的。为什么在我们甚至还没理解问题领域之前已经做了任何技术上的选择?这简直是本末倒置了。

  微软的生态系统鼓励每个人使用SQL Server。在Visual Studio中和SQL Service进行交互或者使用SQL Management Studio(和它的前身,SQL查询分析器)是如此令人难以置信的容易。这种以数据库为中心的重点,是钦定的或唯一正确的方式的一部分。它使你更加迷恋微软。厂商锁定始终对厂商来说是好的。

  为什么我们要如此开发?为什么我们不更多地考虑应用程序的行为而不是它如何存储的?现在我所有的项目都使用基于JSON的键/值存储。有了这种功能,我可以选择任何我想要的存储引擎,包括SQL Server,Oracle,PostgreSQL,MySQL,Cassandra, CouchDB, CouchBase, Dynamo, SimpleDB, S3, Riak, BerkeleyDB, Firebird, Hypertable, RavenDB, Redis, Tokyo Cabinet/Tyrant, Azure Blobs,文件系统中的明文JSON文件等等等等。突然之间,我们能够开始根据其优点而不是仅仅对其熟悉来选择存储引擎了。

  题外话:在AWS RDS的云上运行过SQL Server吗?别这么做。当然它会工作,但是一些例如复制这样最简单的事是不存在的。文章充斥着对SQL Server不能在AWS RDS上工作的引用。

  结论

  也许我在软件开发中学到的两件最重要的教训是:

  ˙边界和封装的重要性(以多种形式)

  ˙付出代价以得到正确的模型和抽象

  许多年前我恨“模型”这个词。每个人都会把它到处扔,它是一个如此过载的术语,很难理解它的含义和它为什么这么重要。就这点来说,我仅仅会说模型是对你想要封装的现实的一个有限的表示。也许最简单的例子就是地球仪的墨卡托投影了。这很确切得说明了一件事:导航。如果你在其他的事情上使用它,它并不毫无价值。如果你不专注于付出代价去使模型正确,去封装商业现实,那么没有任何技术能够拯救你。

  我对.NET最大的抱怨是,“唯一正确的方式”引导你远离理想的模型并把你推向关注实现细节和技术缺陷的方向。这样的关注导致技术实施渗血并且感染模型,最终导致它腐烂变质,因为它不能适应不断变化的商业需求。当这发生的时候,开发者挣扎着并蹬踢着,如同吸毒者一样,他们从一个新技术转向另一个,以期望下一个强大的技术能够治愈他们的病痛。

  技术本身并不是灵丹妙药,相反地,它是关于取舍和选择。只有正确地理解了商业行为并把它们封装进结构良好的,易于理解的模型中,以帮助保持技术堆栈在属于它的地方—作为一个实现细节。

  And that’s why I left the .NET Framework because it kept reasserting itself and wanting to be more than it was: an implementation detail. 这就是我为什么不再用.NET 框架,因为它不断地重申自己(的主张),不断地想要比它的本身更多的:一个实现细节。

1
相关文章