ASP.NET Linux部署(2) - MS Owin + WebApi + Mono + Jexus
ASP.NET Linux部署(2) - MS Owin + WebApi + Mono + Jexus
本文承接我的上一篇博文: ASP.NET 5 Linux部署,那篇文章主要是针对最新的ASP.NET 5的,但在随后的研究中,我对这种娱乐型的部署依然不是非常满意,当然其主要原因是因为ASP.NET 5 依然处于RC版本,并不十分成熟. 但可以预见到的是,就算本月ASP.NET 5 RTM版本如期推出,其在Linux上面的开发和部署前景依然不是非常明朗: 特别令人困惑的是,MS在Linux上至今仅仅推出了几个以开发为目的的简单服务器实现,难以在其计划中寻觅到类似IIS的完整部署环境,那么所谓的ASP.NET 5的跨平台开发是否只能停留在实验室水平? 目前乃至今后很长一段时间内(直到ASP.NET 5完全在Linux上站稳脚跟),我们有没有更好的选择?下面我将给出我自己的想法.
这里首先声明一点,ASP.NET Linux部署系列仅针对Linux部署环境,不涉及Windows部署环境.下面还是先给出一些概念以便于大家更好的理解后续的内容.
ASP.NET |
ASP.NET是.NET Framework的一部分,是一项微软公司的技术,是一种使嵌入网页中的脚本可由因特网服务器执行的服务器端脚本技术, 本月即将发布的最新版本是版本5,又成为vNext. |
Linux |
Linux是一套免费使用和自由传播的类Unix操作系统,是一个基于POSIX和UNIX的多用户、多任务、支持多线程和多CPU的操作系统. 本文中的Linux主要以Ubuntu作为样例. |
Mono |
mono是指由Novell公司(由Xamarin发起,并由Miguel de lcaza领导的,一个致力于开创.NET在Linux上使用的开源工程. 就目前而言,在Linux上的.NET应用还必须基于Mono来运行. |
Jexus |
Jexus 即 Jexus Web Server,简称JWS,是Linux平台上的一款ASP.NET WEB服务器,是目前唯一能够支持企业级ASP.NET Linux部署的一种方案(其他的服务器方案无类似定位). |
OWIN |
OWIN在.NET Web Servers与Web Application之间定义了一套标准接口,OWIN的目标是用于解耦Web Server和Web Application。基于此标准,鼓励开发者开发简单、灵活的模块,从而推进.NET Web Development开源生态系统的发展。 |
MS Owin |
微软开发的基于OWIN规范的底层实现,最新版本是3.0.1,其主项目名称为Kanata |
ASP.NET WebApi |
ASP.NET MVC 4 包含了 ASP.NET Web API, 这是一个创建可以连接包括浏览器、移动设备等多种客户端的 Http 服务的新框架, ASP.NET Web API 也是构建 RESTful 服务的理想平台 |
RESTful |
一种软件架构风格,设计风格而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。 |
NancyFx |
Nancy 是一个基于 .NET 和 Mono 平台用于构建轻量级基于 HTTP 的 Web 服务。基于 .NET 和 Mono 平台,框架的目标是保持尽可能多的方式,并提供一个super-duper-happy-path所有交互。官方网站 http://nancyfx.org/ |
三种选择
就.NET路线的Web开发来看,不管何种方式,未来必然是基于OWIN开发的事实已经不可动摇了; 在这个基础上, 我认为目前在Linux上开发并部署.NET Web应用程序有3个路线可以选择:
- 底层Owin路线: 选择MS的底层OWIN实现,配合其他基于OWIN的独立组件,形成以底层OWIN为核心的自行搭配的轻型构架,这个方案目前已经可以完美部署到Jexus.
- 三方构架路线: 选择同样基于OWIN标准的,非MS的三方构架实现, 目前最有潜力,名气最大的是NancyFx, Nancy框架目前也同样能较好的部署到Jexus上去.
- 正统vNext路线: 选择MS正统的下一代ASP.NET 5 (vNext),该版本的底层基于OWIN实现(注意任何老的ASP.NET版本都不是基于OWIN的), 可谓是亲儿子; 但目前还没有发布正式版本,其未来不可预期, 最关键的一点是,在Linux上,包括Jexus在内,目前依然没有完美的基于商业环境的部署服务器环境支持.
就这3个方案而言,我觉得各有利弊: 底层方案需要更多的自行选择和组装,但是与任何基于Owin的组件搭配自如; 三方方案面临生态环境的问题,由于大部分高端的组件都来自MS,能否真正无缝连接需要考验,自身的生存能力也是问题; 正统方案内容完整,支持强大, 与MS各项技术融合度高,但却面临成熟周期问题(时不我待),另外我最不爽的一点是,vNext又一次搞成了铁索连环船, 连WebApi都和MVC融合了,又给人一种整套推销的感觉, 有违当初Owin体系的初衷;而最根本的问题是,目前还没有任何方案给vNext提供一个Linux上的IIS级服务器,没有好的载体,仅仅是把vNext的Linux部署定义为娱乐这个显然看不出太大的诚意.
综上,我目前还是倾向于使用底层Owin方案,目前商业化开发路线是: 基于MS Owin实现,根据需要加入各种MS稳定组件,比如Web API 2.2 OWIN 5.2.3, Identity Owin 2.2.1, SignalR OWIN 1.2.2, OAuth 3.0.1,和其他所有的通用型组件,如EF, Logging, IoC等等; 最终通过Mono和 Jexus架设到Linux环境.
下面我建一步演示如何组装MS Owin和Web API 2.2, 并把它们部署到Jexus上去.
开发环境VS 2013, Window 7或 8; 部署环境Ubuntu 15.
第一步: 建立项目
首先,在VS 2103中建立一个Class Library项目,注意只要Library项目,这里可以选择Framework 4.5.2或者4.5.1. 这个项目假设命名为OwinExample.
然后,我们加入这个项目必须的组件,根据上面的描述,我们需要2个组件: MS Owin的核心实现Microsoft Owin和ASP.NET WebApi 2.2 Owin
我们先加入Microsoft Owin
然后加入ASP.NET WebApi 2.2 Owin
第二步: 建立Owin入口代码
首先,Owin的传统入口类登场: Startup.cs
using Owin; using System.Web.Http; public class Startup { public void Configuration(IAppBuilder app) { #region WebApi var httpConfig = new HttpConfiguration(); httpConfig.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{action}/{id}", defaults: new { id = RouteParameter.Optional } ); //强制设定目前的WebApi返回格式为json httpConfig.Formatters.Remove(httpConfig.Formatters.XmlFormatter); //加载WebApi中间件 app.UseWebApi(httpConfig); #endregion } }
几个要点:
l Startup中的Configuration写成类成员方式,而不是静态方式,是为了和Jexus适配器配合,其实差异不大.
l WebApi的配置写法和MVC基本类似.
l Startup和Configuration的命名并不是固定的,只是预定俗成而已.
第三步: 建立WebApi代码
建立DefaultController.cs 为一个默认的WebApi,里面包含一个最简单的Hello函数.
using System.Web.Http; [AllowAnonymous] public class DefaultController : ApiController { [HttpGet] public string Hello() { return "Hello Owin!"; } }
自此,简单的MS Owin + WebApi程序架设完毕. 在Owin体系下,我们发现一切都变得非常简单和清晰.
第四步: 建立Jexus适配器代码
为了把项目部署到Jexus上去,我们还需要一个非常简单的适配器类,在项目中加入这个类以后,就能无缝部署到Jexus服务器上去了, 我们把这个代码命名为Adapter.cs:
/************************************************************************************** * 加载Microsoft.Owin.dll 进行owin编译的适配器(插件)示例 * ================================================================================== * 目的: * 演示如何将自己的处理方法(中间件)加入到 Microsoft.Owin.dll的处理环节中 * * 使用方法: * 将编译得到的dll连同Owin.dll、Microsoft.Owin.dll等文件一并放置到网站的bin文件夹中 *************************************************************************************/#region <USINGs>
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Owin.Builder;#endregion
namespace OwinExample
{
public class Adapter
{
static Func<IDictionary<string, object>, Task> _owinApp;</span><span style="color: #808080;">///</span> <span style="color: #808080;"><summary></span> <span style="color: #808080;">///</span><span style="color: #008000;"> 默认构造函数 </span><span style="color: #808080;">///</span> <span style="color: #808080;"></summary></span> <span style="color: #0000ff;">public</span><span style="color: #000000;"> Adapter() { </span><span style="color: #008000;">//</span><span style="color: #008000;">创建默认的AppBuilder</span> <span style="color: #0000ff;">var</span> builder = <span style="color: #0000ff;">new</span><span style="color: #000000;"> AppBuilder(); </span><span style="color: #008000;">//</span><span style="color: #008000;">创建用户定义的 Startup类 </span><span style="color: #008000;">//</span><span style="color: #008000;">这个类中必须有“Configuration”方法</span> <span style="color: #0000ff;">var</span> startup = <span style="color: #0000ff;">new</span><span style="color: #000000;"> Startup(); </span><span style="color: #008000;">//</span><span style="color: #008000;">调用Configuration方法,把自己的处理函数注册到处理流程中</span>
startup.Configuration(builder);
</span><span style="color: #008000;">//</span><span style="color: #008000;">生成OWIN“入口”函数</span> _owinApp =<span style="color: #000000;"> builder.Build(); } </span><span style="color: #808080;">///</span> <span style="color: #808080;"><summary></span> <span style="color: #808080;">///</span><span style="color: #008000;"> *** JWS所需要的关键函数 *** </span><span style="color: #808080;">///</span> <span style="color: #808080;"><para></span><span style="color: #008000;">每个请求到来,JWS都把请求打包成字典,通过这个函数交给使用者</span><span style="color: #808080;"></para></span> <span style="color: #808080;">///</span> <span style="color: #808080;"></summary></span> <span style="color: #808080;">///</span> <span style="color: #808080;"><param name="env"></span><span style="color: #008000;">新请求的环境字典,具体内容参见OWIN标准</span><span style="color: #808080;"></param></span> <span style="color: #808080;">///</span> <span style="color: #808080;"><returns></span><span style="color: #008000;">返回一个正在运行或已经完成的任务</span><span style="color: #808080;"></returns></span> <span style="color: #0000ff;">public</span> Task OwinMain(IDictionary<<span style="color: #0000ff;">string</span>, <span style="color: #0000ff;">object</span>><span style="color: #000000;"> env) { </span><span style="color: #0000ff;">if</span> (_owinApp == <span style="color: #0000ff;">null</span>) <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">null</span><span style="color: #000000;">; </span><span style="color: #008000;">//</span><span style="color: #008000;"> 将请求交给Microsoft.Owin对这个请求进行处理 </span><span style="color: #008000;">//</span><span style="color: #008000;">(你的处理方法已经在本类的构造函数中加入到它的处理序列中了)</span> <span style="color: #0000ff;">return</span><span style="color: #000000;"> _owinApp(env); } }
}
这里再次感谢Jexus作者宇内流云提供的代码, 出于对原作者的敬意这个代码除了命名空间以外我一个字母也没有改,其实也不需要改. 其实大家可以看的出来,这么变态的注释应该不是我故意去写的.
自此我们的基于MS Owin和WebApi的迷你版应用开发完成,改为Release模式编译,我们可以得到如下图所示的一系列DLL:
就这些DLL就能形成一个WebApi应用吗?事实就是如此,而且这个应用能很好的部署到Linux环境上去.
第五步: 安装Jexus环境
这里先声明下,基于个人的能力所限,只能先给出Ubuntu最新版本的一个部署方案,使用其他版本Linux的兄弟只能麻烦你们自寻门路了.
首先,我们再Ubuntu上面安装Mono最新版本. 可以参考下面超链文章的指引:
http://www.linuxdot.net/bbsfile-3090
然后我们安装Jexus最新版本. (同样请参考下面的超链)
http://www.linuxdot.net/bbsfile-3500
第六步: 部署到Jexus
部署Jexus网站的常规指导信息,大家可以移步这里:
http://www.linuxdot.net/bbsfile-3084
下面说下我们的特殊部署步骤 (具体Linux命令我就不列举了):
- 建立网站目录 /var/www/owinexample, 然后在这个目录下再建立一个bin目录.
- 通过各种方式把上面自己开发产生的dll拷贝网站目录的bin目录下.
- 建立Jexus网站的配置文件,假设我们命名为 owinexample
其重要内容应该包括以下设置:
# For owinexample
port=88
root=/ /var/www/owinexample
hosts=* # or your.com,*.your.com
OwinMain=OwinExample.dll,OwinExample.Adapter
特别强调的是OwinMain这个必需配置,并且需要对应正确的DLL文件名和Apdater类. 根据前面的描述,我们可以知道我们应用的配置应该是OwinExample.dll, OwinExample.Adapter.
另外, Web.Config文件和其他任何文件在这种构架里面不是必须的.
- 重启 Jexus 服务
- 打开你的浏览器,输入 http://linuxserverip:88/api/default/hello 就可以看到结果.
注意linuxserverip为部署服务器的IP, 88为我们再Jexus配置中设置的端口, api/default/hello对应我们WebApi的路径映射,Controller类名和方法名.
结束语
最后还是说下我们这种模式的优势,劣势和意义:
优势: 基于Owin底层,简单明了稳定,可以融合任何基于Owin的相关技术,扩展性强,可以和Mono, Jexus完美结合,性能最高.
劣势: 相当于自行组建构架,搭建工作量大, 由于目前没有独立的MVC组件,在MVC开发方面缺乏支持(Nancy的一部分MVC构架比如Razor引擎可以独立移入,但这个方案有待验证).
这个方案的最终意义在于,结合目前.NET和Linux方向上最具备稳定性和代表性的MONO, MS Owin和 Jexus, 在ASP.NET vNext最终能完美部署到Linux之前,这是最接近于商业生产环境的方案之一.
最后拖一句,这个方案的开发环境可以考虑用TinyFox或者MS Owin Self Host来做宿主. 都可以无缝连接,代码不需要修改.