开发 ASP.NET vNext 续篇:云优化的概念、Entity Framework 7.0、简单吞吐量压力测试
继续上一篇《开发 ASP.NET vNext 初步总结(使用Visual Studio 2014 CTP1)》之后,
关于云优化和版本控制:
我本想做一下MAC和LINUX的self-host测试,但是官方说运行环境的MONO版本至少需要3.4.1,我去年买了个表,至本文发布为止,你让我下地狱去找3.4.1吗,硬着头皮用3.4.0搞了一晚上,MAC一直停留在 httpapi.dll出错,Ubuntu Server 12.0.4 是不认其中的几个DLL包,具体哪几个也忘了,过段时间有了稳定版本再弄吧。
但是,我因此却大概了解了它所谓的“云优化“是什么概念,先来看看如何在非windows环境运行一个项目的完整步骤:
- 把生产环境代码部署到某个全新的平台上(*nix与MAC)之后,进入当前代码目录下
- 运行sudo kpm restore 命令
- k运行时环境会根据project.json中记录的各个命名空间,自动联网恢复当前整个项目所需的包
- 包保存到本地
- 运行 k web ,启动self-host模式,或者 k run ,启动nginx等第三方宿主模式。"web"和"run"参数对应project.json中的相应节点配置。
在步骤3,不同的系统会得到不同的包,这就是所谓的云优化。
如果以后需要更新包的版本,使用 kpm upgrade 来更新,依旧是手动,除了”.net 4.5 core“这个最核心的命名空间是从系统加载之外,其它的整个运行环境所需的全部包,都保存在你自己当前的目录下,从你自己的目录里面加载运行,相当于”绿色版“ASP.NET,举例来说,从前总有些功能是即使你把dll放到bin也没用的,必须是通过系统安装才可以使用,而现在,它可以100%的保证你所需的全部功能都可以通过把dll部署到bin目录来解决,这样做的实际意义是:aws和azure的linux主机不允许我们运行root用户,总有些东西是我们哪怕sudo也没法安装的,这只是其中一例,至此明白我的意思就好,进而的,我们可以把同一个应用程序的不同版本部署到不同的目录,绑定不同的域名,完全隔离它们的运行期上下文,这就是所谓的每个应用程序可以运行于不同的版本。
关于从nuget加载包的担心:
初次启动它不会自动去nuget加载,下次再怎么重启也更不会去nuget自动加载,一切涉及到nuget的行为都需要你自己手动控制,restore时只有100%成功加载后,你的应用程序才能够运行,否则回滚,upgrade也一样必须是100%成功,否则回滚,你把它想象成一个“事务”来理解就可以了,不会在“加载”这件事上对你的应用程序造成影响。
此nuget也非彼nuget,默认情况下它会到nuget官方服务器加载包,但是kpm restore 和 upgrade 可以指定不同的源地址,如果你愿意,你可以指定到127.0.0.1。所以到此为止,不需要在部署上有什么担心了。
关于VS2014:
根据微软向来的潜规则,某个组件还在测试版本阶段的时候,它的命名空间是:Microsoft.XXX.XXX,待到正式版本发布后,会变成System.XXX.XXX。目前很多包都是Microsoft名下。
我目前的VS2014 CTP1是在卸载掉本机的2013、2012、2010之后才顺利安装上的,但是也不知道是我卸载过程中卸错了东西还是VS2014 CTP1本身作怪,我系统上的SQLServer光荣牺牲掉了,重新安装一遍SQLServer就好(修复安装),虽然它说了不能与旧版本的VS共存于同一台机器,但是我后来再次安装上了VS2013.2,一切工作正常。(有风险,请勿仿效,本人不负责一切后果)
有时候你编辑着一个项目,一切风平浪静,但是当你保存,关闭vs2014,再次打开vs2014开启项目后,项目中的“引用列表”会一直停留在loading状态,重启几次后才好,鬼才知道是为什么。
凡事讲究门当户对,既然时代已经进入vNext,不用EF 7.0怎么对得起D和人民:
Entity Framework 7.0 :更加简洁,可恶的Migration终于离去
(Entity Framework 7.0可以独立于vNext,运行于MVC1~5.x)
创建好一个示范用的数据库之后,看看:
在过去,使用Migration的时候遇到的最大问题,不是Migration步骤本身,而是时不时出现:“已经存在相同的表或对象”,各种解法稀奇古怪,甚至需要去修改Migration自动生成的文件,自己玩玩的项目就算了,真正大规模实时生产环境里面你敢这么做?
在过去,Migration表中记录了每个数据表的结构Hash值,如果你手动修改了表结构,哪怕是实际已经保持了结构的一致性,但是Entity Framework依然会报错,因为Hash值不对,它只相信它自己的Migration,虽然可以通过应用程序初始化期间关闭数据结构检查来越过Migration步骤,但是依旧心慌慌的,出门和人吹牛也怕被因此抓到把柄。现在,Migration终于消失了,意味着你可以自己去手动修改表结构与Code First结构同步,它再也不会自作聪明来为难你了。
由此带来的另一个好处:省掉该功能后,同时也意味着省去很多开发与测试步骤,EF团队能够更专注于EF本身的核心功能,更快速更新/修复版本,第三方数据库开发商可以更快速地发行/更新与自己的数据库对应的Entity Framework框架。
你依然可以使用Migration,系统把这个功能分离出来放置在Microsoft.Data.Entity.Migration空间下,至于怎么去使用,我就没去研究了。
过去,在运行时,Entity Framework 会自动检查数据库是否存在,如果不存在,就自动创建一个数据库,并且创建对应的表等所有对应结构。现在这个功能需要我们手动去开启,并且“创建数据库”与“创建表”还是两码事,就像这样:
/// <summary> /// 数据库操作类 /// </summary> public class DBOperator : DbContext { public DBOperator() { if (!Database.Exists()) { Database.Create(); //创建数据库 Database.CreateTables(); //创建表 } } public DbSet<Customers> Customers { get; set; } protected override void OnConfiguring(DbContextOptions builder) { builder.UseSqlServer(@"Server=HP\SQLSERVER;Database=vNextTest;Trusted_Connection=True;MultipleActiveResultSets=true"); } }
在1~6.x版本中,你可以把构造函数去掉,没问题,系统会自动创建数据库和表,但是现在不行了,如果你删掉上面代码例子中的构造函数,数据库不会在一开始得到创建,由此引发了这么一种思考: 在实际运行过程中,99.9999999999%的情况里是不需要去if (!Database.Exists())的,我们就可以通过构建一个多态结构来这么做,确保性能的最大化:
public abstract class DBO : DbContext { public DbSet<Customers> Customers { get; set; } protected override void OnConfiguring(DbContextOptions builder) { builder.UseSqlServer(@"Server=HP\SQLSERVER;Database=vNextTest;Trusted_Connection=True;MultipleActiveResultSets=true"); } } public sealed class DBOperator : DBO { //实际运行时使用这个类 //省去不需要的判断 } public sealed class FirstTime : DBO { public FirstTime() { //系统第一次运行时使用这个类,把它放在Started.cs中执行一次就好了 if (!Database.Exists()) { Database.Create(); //创建数据库 Database.CreateTables(); //创建表 } } }
设置project.json,启用Entity:
{ "dependencies": { "Helios" : "0.1-alpha-build-0585", "Microsoft.AspNet.StaticFiles": "0.1-alpha-build-0443", "Microsoft.AspNet.Mvc": "0.1-alpha-build-1268", "Microsoft.AspNet.Server.WebListener": "0.1-alpha-build-0520", "Microsoft.Data.Entity": "0.1-alpha-build-*", //启用Entity "Microsoft.Data.Entity.SqlServer": "0.1-alpha-build-0863", //启用SQL Server "Microsoft.DataAnnotations": "0.1-alpha-build-0100", //启用DataAnnotations "Microsoft.Framework.ConfigurationModel": "0.1-alpha-build-0233" //启用配置文件操作 }, "configurations" : { "net45" : { }, "k10" : { } } }
其它的SaveChange、Add等操作和从前一样,就不说了。
同道中人们,从现在开始,请扔掉Migration。
vNext运行于self-host模式和IIS模式,对比传统MVC 5.x
下面是使用AB对vNext做一个简单压力测试,主要是对吞吐量有个心理概念,controller中进行斐波那契序列30次迭代运算,View是空的,只输出简单HTML,客户端模拟1000个用户总共进行10000次访问。 每次测试前,都额外通过IE访问一次(消除初次启动)。
public IActionResult Index() { var x = new int[30]; var length = x.Length; for (int n = 0; n < 30; n++) { switch (n) { case 0: x[n] = 1; break; case 1: x[n] = 1; break; default: x[n] = x[n - 1] + x[n - 2]; break; } this.Context.Response.WriteAsync((x[n]).ToString() + "<br />"); } return View(); }
该说的都在图中说了,但愿正式版出来后情况会好一些。
目前尚未解决的问题
*nix环境下实在配置不来,如果有哪位师兄愿意折腾的话,请看他们的官方说明:https://github.com/aspnet/home