原文链接:Why I Left the .NET Framework
这篇文章从列举了.NET框架好的方面,然后列举不好的方面,最后给出结论为什么不再使用.NET框架(作者说明了只是不使用.NET框架,而非整个.NET生态系统,它包含了各种工具,项目,平台,组织,开发团队等等)。
C#是一门非常棒的语言,有很多大比例的程序员都在使用它。在Windows操作系统上,它是非常值得学习的一门语言。
ReSharper是JetBrains公司出品的VS的一个插件,能够极大地提高开发效率。
Machine.Specification是一个BDD风格的框架。作者高度赞扬了它,有了它,测试代码就从一段糟走上正轨了。
作者认为多语言公共运行环境这个概念(CLR)让JVM世界开始思考起来。在CRL之前,作者从未知道任何一个非Java的JVM语言,但是“公共语言运行环境”的出现,让在JVM上工作的人们最终创造了伟大的基于JVM的开发语言,比如Scala,Clojure。
Windows的包管理,特别是Windows开发涉及到的包管理,是一件可怕的事情。NuGet通过大量借鉴Python和Ruby的做法,它做了很多正确的事情,并解决了很多的问题。
对于开发Mono的那些人,我再过高地评价他们都不为过。他们创造了令人惊叹作品。在没有官方支持,且有潜在法律风险的情况下,他们实现了一个跨平台的.Net Framework的微软官方的替代品。
.NET是CQRS及其相关技术事件源的发源地。
在前一篇文章中,作者指出在基于网络的服务器软件中,Windows不是一个好的竞争者。另外一个问题是传统Windows开发者只能在Window上进行开发,一旦离开了Windows这个舒适区,他们就无所适从了。而Linux开发者就没有这个问题。
另一个作者经常抱怨的问题是文件系统。NTFS不是唯一的文件系统,但是对于任何给定的任务,它都不是最好的选择。ZFS,BTRFS,ReiserFS,ext* 等等,他们都有一些很酷的特性。作者特别喜欢用BASH来创建回路设备,或者为各种高速磁盘操作创建内存驱动器。如果没有第三方软件的支持,这些在Windows下是不可能做到的。
在AWS云上,启动一个Windows虚拟机需要10分钟以上的时间,但是启动一个简单的Linux虚拟器却只需要15-20秒!当系统需要在云端进行扩容的时候,10-15分钟的时间太长了。
作者的另一个痛是Visual Studio。作者曾经的台式机电脑的硬件配置不可谓不高, i7 3770K 3.5Ghz,16G内存, Vertex 4 512G SSD硬盘。Windows体验指数分值达到了最大。但是在Windows上跑VS仍然很慢。现在作者使用MacBook Pro,尽管CPU功率比台式机的小,但是能明显地感觉速度更快,且到用户体验更好。
VS还有一个不好的地方是它的调试器,既不方便看,又不方便用。在Watch窗口经常会显示错误的变量值,这会导致作者额外多花几个小时来调试。
VS还会创建令人不爽的“csproj”和“sln”文件。C#需要知道何时编译说明文件,这个没问题。在Go语言中,代码的引用关系通过"import"语句来说明。如果.NET里没有这些项目文件,那就可以使用文本编辑器来编写代码,这会让编码更加流畅。
更不要说回车换行问题了。如果VS文件使用了Linux的行结束符号,那么通过双击解决方案文件没法加载这个项目,因为VS的解决方案文件解析器根本就没法读取文件内容并解析它们。
作者很庆幸他很早就离开了微软的源码控制阵营(Visual Source Safe)。早在2000,在VSS不知多少次地弄丢了作者的提交信息后,他转向使用了Subversion。后来Git出来了,作者很喜欢,但是没有Windows版本。有一次,作者在一个小项目上使用了TFS,结果发现TFS是一个灾难。它感染了项目文件,并且污染了源码目录。作者感叹到哪怕给他一个Git的命令行工具也行啊
这里有一次提到了Mono。上面是讲它好的一面,这里是讲它不好的一面。尽管Mono是一个了不起的产品,但是在.NET世界里,它还是二等公民。不管何时,当作者想要在Mono中运行一些重要的程序时,总会发现一些实现上的bug。幸好,作者不反感下载源码,通过排查问题,找到原因,发送pull request,并在Linux上重新编译,最终解决问题。作者已经不记得做过类似的事情多少次了。虽然问题能够解决,但是查找问题让代码顺利运行花费了太多的时间。
另外Mono不好的一面是太慢。也许不是每一方面都慢,但是对作者来说,网络服务器是极其重要的一方面,而这个方面却是非常慢,即使是一些很普通的操作也很慢。
IIS的问题也是慢。很多基于JVM的实现,比如Netty,能够支持650K+/秒的请求,而IIS即使运行一个简单的返回“Hell World”的应用,只能支持50K/秒的请求。(有趣的是,有人直接使用C#的TCP Socket来实现一个Web服务器,却能达到120K/秒的请求。)
几年前曾有一个叫做ALT.NET的运动,主要是在更广泛的开发社区中寻找不同的技术点,而不在局限于自己的圈子。那次运动成了StructureMap,Autofac,NuGet,ASP.NET MVC以及其他许多工具的灵感来源。但是传统.net圈子对此却不屑一顾。作者认为整个社区充满了思维惯性和封闭的思想。
在C,Java和C#中,典型的多线程编程范式鼓励使用锁和互斥量。但是使用锁有个隐藏代价:它们会非常慢。使用Disruptor(JVM上一种无锁环缓冲区),可以每秒轻松处理20M+/秒的事件。而使用.NET的“最佳实践”,每秒只能处理10+多个事务。到了这个时候,就需要通过增加更大/更好/更多硬件设备来提升性能。即使代码中没有任何并发语句,.NET编译后默认的,首选的方法还是会使用锁。
不用锁,事件驱动的方式很大程度上可以减少硬件和资金。绝大多数应用可以使用2台计算机就可以轻松应付,其中一台作为热备。
另一个性能问题是使用传统方法调用网络或磁盘子系统:同步,阻塞代码。如果要同时进行多个HTTP请求,只能使用线程。但是绝大多数开发者不知道的是维护一个线程需要额外的1-2M内存,并且线程切换会导致CPU内核花费很多时间进行线程上下文的切换。如果应用程序有成百上千个线程的话,光线程间的切换就会让CPU忙个不停。
其实有个更好的方法。Netty/NIO(JVM),Erlang,Node,Gevent(Python),和Go都鼓励使用基于事件的子系统操作(select/epoll/kqueue)。这意味着CPU可以做真正的工作而不是等着接受或发送网络上的包。
微软的生态系统鼓励每个人都利用SQL Server。在Visual Studio跟SQL Server交互或SQL Management Studio使用SQL Server都是极其简单的。但是这会让你更依赖于微软。
为什么我们不能考虑应用程序的行为本身而不是怎么保存数据?作者所有的项目都使用基于JSON的键值对来存储数据。有了这个能力,作者就能任意选择存储引擎,包括SQL Server,Oracle,PostgreSQL,MySQL,Cassandra,CouchDB,CouchBase,Dynamo,SimpleDB,S3,Riask,BerkeleyDB, Firebird, Hypertable, RavenDB, Redis, Tokyo Cabinet/Tyrant, Azure Blobs, 文件系统中的JSON文本文件,等等等等。
另外:想要在AWS RDS的云端运行SQL Server?不行。文档上明确指出SQL Server不能运行在ASW RDS里。
作者提到在软件开发中他学到的2件重要的事情是:
- 边界和封装的重要性
- 花点代价把模型和抽象做正确
作者认为模型是所有封装的现实世界的狭窄表示。最简单的例子是地球的墨卡托投影。在一件事情上它做得非常好:导航。如果你不花点代价关注在做正确的模型上以此来封装现实业务,那么没有任何技术可以拯救你。
使用.NET最大的问题在于它会把你带离理想的模型,并让你关注在技术实现细节上。这个结果是技术实现影响抽象模型,最终导致模型衰退而不能适应不断变化的业务需求。
技术本身不是万能药,相反,它需要权衡并做出选择。只有正确的理解业务行为并把它们封装到形式良好的,易于理解的模型中,才能帮助技术成为实现细节。
这就是为什么我离开.NET框架的原因,因为它一直在声称自己是一种实现的细节,而说它是实现细节其实还是夸大了它。
================================================== 这篇文章写于2014年1月30日,距离现在已经有5年多了。最几年微软在做了很多改变,特别是开源方面做了很多工作。所以文章中提到的不好的方面,大多数都有了很大的改进。比如
- Windows 10性能提升了很多,口碑不错
- Visual Studio 2019已经发布,效率很高,而且开发效率也有很大提升,即使不用ReSharper问题也不大。
- 源码控制方面,VS2017可以无缝继承Git
- 技术封闭圈方面,近年来微软大力拥抱并积极参与开源项目,开源了 .NET Core
- 性能方面,.NET Framework / .NET Core效率有很大提升,提供了不少并发机制
- 至于作者说的SQL Server的问题,我觉得完全不是问题,使用Entity Framework或者其他ORM框架,数据存储引擎可以随便替换
作者最后说的明确边界,做正确的业务模型并抽象出来,这两点我非常赞同。具体的技术实现是细节问题,真正重要的是对现实世界中的业务进行抽象。模型做好了,在不同场景下可以使用不同的技术来实现功能。
Java中进行Socket的开发,可以使用Netty。最近在C#中也要做Socket开发,就想是否在C#也有类似Netty的开源项目。
近年来,.Net在开源方面做得还不错,也借鉴了很多Java的优秀项目,把它们移植到.Net平台或者在.Net平台也开发了一套,比如日志工具,Java有Log4j,.Net中有Log4net;测试工具,Java有JUnit,.Net有NUnit。
于是上网搜索了下,结果真有.Net版本的Netty:DotNetty。
原文链接:从一个真实的分布式ID案例看如何做架构
本文从一个实际的案例,说明了如何落地一个架构设计。
首先作者做了背景交代:商场在节假日需要对某些商品进行打折促销。然后从业务架构,技术架构,编码实现,代码审查等几个方面一一做了介绍。最后通过这个案例,分享了做架构的心得。摘录如下:
一个看似简单的方案,其实涉及的细节考量非常多,本以为架构设计、流程处理已足够详细,开发人员也已明确理解方案,实现起来应该问题不大,然而落实到代码时,还是会发现诸多严重bug,这就说明我们在工作过程中,一定要要重视以下几个问题:
- 做架构不能与开发脱节,架构设计不是空中楼阁,一定要能指导开发,能落地,接地气
- 一定要对代码实现进行复查及走读,及时发现实现与设计有出入的地方,并及时修正
- 需要对分布式大数据高并发场景下的一致性及性能问题重点考虑
- 异常场景不容忽视,需要做好容错及必要的降级处理,保证高可用