net搭建热插拔式web框架(沙箱的构建)
net搭建热插拔式web框架(沙箱的构建)
上周五写了一个实现原理篇,在评论中看到有朋友也遇到了我的问题,真的是有种他乡遇知己的感觉,整个系列我一定会坚持写完,并在最后把代码开源到git中。上一篇文章很多人看了以后,都表示不解,觉得不知道我到底要干什么,可能就像隔行如隔山吧,就像做移动端开发的人很少去考虑分布式中的通信一样。大家都知道模块化,但模块化的思路有很多,我的只是其中一种,也许你看到最后会觉得这种思路在经过不断地演化后会成为一种很好的解决方案,当然这离不开以后大家对代码及思想的贡献。
好了不扯了,还是回到主题上来吧....
沙箱是什么?怎么用呢?
沙箱说白了就是插件、模块运行的环境,有点像docker又不像(哈哈)。沙箱主要由两部分组成:沙箱管道和沙箱启动器(为了显得高大上一些,就起了两个难以理解的名字),还有一个以后为功能做铺垫的实体类——controller/action 封包类
首先贴一下这个实体类的代码:

/// <summary>controller/action 封包类
/// </summary>
public class CAModel
{
public string ControllerName{get;set;}
public string ActionName{get;set;}
public string UrlPath { get; set; }
/// <summary>构造
/// </summary>
/// <param name="controllerName">controller 全名(带命名空间)</param>
/// <param name="actionName">action 全名(不带参数)</param>
public CAModel(string controllerName,string actionName)
{
ControllerName=controllerName;
ActionName=actionName;
UrlPath = controllerName.Replace(".Areas.", "/").Replace(".Controllers.", "/");//controller转Url
if (UrlPath.EndsWith("Controller"))
{
UrlPath = string.Format("/{0}/{1}", UrlPath.Substring(0, UrlPath.Length - 10),actionName);
}
} public CAModel()
{
}
}

这个封装类主要是为了以后系统核心功能:权限管理,方便获取模块内所有action对应的Url路径,现在就不过多投入精力了。
下边我们说一下沙箱管道(SandBoxChannel ):沙箱管道服务于沙箱启动器,这个类需要继承MarshalByRefObject,也就是必须要支持跨域访问。这个类中最关键的就是_assembly和InvokeMothod。
/// <summary>沙箱管道
/// </summary> public class SandBoxChannel : MarshalByRefObject { /// <summary>沙箱内加载的程序集 /// </summary> private Assembly _assembly; /// <summary>加载程序集 /// </summary> /// <param name="assemblyFile">程序集文件路径(主程序类库路径,依赖类库自动加载)</param> public void LoadAssembly(string assemblyFile) { try { _assembly = Assembly.LoadFrom(assemblyFile); } catch (Exception ex) { throw ex; } } /// <summary>沙箱内方法调用 /// </summary> /// <param name="typeName">类名称(全名称)</param> /// <param name="methodName">方法名称</param> /// <param name="args">参数</param> /// <returns></returns> public object InvokeMothod(string typeName, string methodName, params object[] args) { var _assembly_temp = _assembly; if (_assembly_temp == null) return null; if (typeName == "Huber.Kernel.Entity.CurVariable" && methodName == "SetCurWebDir") {//设置当前沙箱内的系统变量,非外部方法 foreach (var a in AppDomain.CurrentDomain.GetAssemblies()) { if (a.GetName().Name == "Huber.Kernel") { _assembly_temp = a; break; } } } Type tp = _assembly_temp.GetType(typeName, true, false); if (tp == null) return null; MethodInfo method = tp.GetMethod(methodName); if (method == null) return null; Object obj = Activator.CreateInstance(tp); //ContentResult //FileContentResult //FileStreamResult //FilePathResult //HttpNotFoundResult //JavaScriptResult //JsonResult //PartialViewResult //RedirectToRouteResult //RedirectResult //ViewResult object result = method.Invoke(obj, args); return result; } /// <summary>获取所有的action /// </summary> /// <returns></returns> public Dictionary<string, CAModel> GetAllAction() { Dictionary<string, CAModel> result = null; Type[] types = _assembly.GetTypes(); MethodInfo[] meths = null; string controller = string.Empty; if (types != null) { result = new Dictionary<string, CAModel>(); string url = string.Empty; CAModel temp; foreach (var t in types) { if (t.BaseType != null && t.BaseType.ToString() == "Huber.Kernel.MVC.HuberController") { meths = t.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly); foreach (var m in meths) { temp = new CAModel(t.ToString(), m.Name); result.Add(temp.UrlPath.ToLower(), temp); } } } } return result; } public override object InitializeLifetimeService() { //Remoting对象 无限生存期 return null; }再看一下沙箱启动器(SandBoxDynamicLoader):所谓沙箱启动器就是创建一个启动器对象,把模块的类库、配置文件等加载进去,当然这个启动内部是一个沙箱(即appdomain)。在SandBoxDynamicLoader的构造方法中创建的了一个appdomain和一个SandBoxChannel对象,支持模块的加载与卸载
public class SandBoxDynamicLoader
{ /// <summary>沙箱对应的应用程序域 /// </summary> private AppDomain appDomain; /// <summary>沙箱管道 /// </summary> private SandBoxChannel channelChannel; /// <summary>程序域的ID /// </summary> public int AppDomainID { get; set; } /// <summary>插件名称 /// </summary> public string PluginName { get; set; } /// <summary>构造 /// </summary> /// <param name="ApplicationBase">插件的所在的目录(bin目录)</param> /// <param name="_PluginName">插件名称</param> /// <param name="configPath">config文件位置</param> /// <param name="_AppDomainID">域标识(唯一)</param> public SandBoxDynamicLoader(string ApplicationBase, string _PluginName, string configPath, int _AppDomainID) { PluginName = _PluginName; AppDomainID = _AppDomainID; AppDomainSetup setup = new AppDomainSetup(); setup.ApplicationBase = ApplicationBase; DirectoryInfo di=new DirectoryInfo(ApplicationBase); if (configPath != string.Empty) { setup.ConfigurationFile = configPath; } setup.PrivateBinPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "private"); setup.CachePath = setup.ApplicationBase; setup.ShadowCopyFiles = "true"; setup.ShadowCopyDirectories = setup.ApplicationBase+"\\SandBoxRunShadow"; AppDomain.CurrentDomain.SetShadowCopyFiles(); this.appDomain = AppDomain.CreateDomain(PluginName, null, setup); this.appDomain.SetData("APP_CONFIG_FILE", configPath); String name = Assembly.GetExecutingAssembly().GetName().FullName; try { this.channelChannel = (SandBoxChannel)this.appDomain.CreateInstanceAndUnwrap(name, typeof(SandBoxChannel).FullName); } catch (Exception ex) { } } /// <summary>加载程序集 /// </summary> /// <param name="assemblyFile"></param> public void LoadAssembly(string assemblyFile) { channelChannel.LoadAssembly(assemblyFile); } /// <summary>获取当前模块内所有action /// </summary> /// <returns></returns> public Dictionary<string, CAModel> GetAllAction() { if (channelChannel == null) return null; return channelChannel.GetAllAction(); } /// <summary>方法调用 /// </summary> /// <param name="typeName">类名称(全名称)</param> /// <param name="methodName">方法名称</param> /// <param name="args">参数</param> /// <returns></returns> public object InvokeMothod(string typeName, string methodName, params object[] args) { return channelChannel.InvokeMothod(typeName, methodName, args); } /// <summary>卸载 /// </summary> public void Unload() { try { if (appDomain == null) return; AppDomain.Unload(this.appDomain); this.appDomain = null; } catch (CannotUnloadAppDomainException ex) { throw ex; } }到此沙箱模型就完了,其实整个过程可以归纳为:创建一个appdomain,利用反射调用方法处理请求。这个模型不仅在web平台上可以使用,其实他早就在系统服务型框架、窗体框架中大范围使用了。
转载请注明出处:http://www.cnblogs.com/eric-z/p/5028243.html
net搭建热插拔式web框架(沙箱的构建)的更多相关文章
- 第二篇 基于.net搭建热插拔式web框架(沙箱的构建)
上周五写了一个实现原理篇,在评论中看到有朋友也遇到了我的问题,真的是有种他乡遇知己的感觉,整个系列我一定会坚持写完,并在最后把代码开源到git中.上一篇文章很多人看了以后,都表示不解,觉得不知道我到底 ...
- 第三篇 基于.net搭建热插拔式web框架(重造Controller)
由于.net MVC 的controller 依赖于HttpContext,而我们在上一篇中的沙箱模式已经把一次http请求转换为反射调用,并且http上下文不支持跨域,所以我们要重造一个contro ...
- 基于.net搭建热插拔式web框架(实现原理)
第一节:我们为什么需要一个热插拔式的web框架? 模块之间独立开发 假设我们要做一个后台管理系统,其中包括“用户活跃度”.“产品管理”."账单管理"等模块.每个模块中有自己的业务特 ...
- net搭建热插拔式web框架
net搭建热插拔式web框架(重造Controller) 由于.net MVC 的controller 依赖于HttpContext,而我们在上一篇中的沙箱模式已经把一次http请求转换为反射调用,并 ...
- 第五篇 基于.net搭建热插拔式web框架(拦截器---请求管道)
好了,前边我们把核心内容介绍完了,接下来要做的就是拦截用户的请求,并把请求转向沙箱内. 这里我们准备通过实现一个HttpModule类来完成请求的拦截与转发.新建一个HuberHttpModule类, ...
- 第四篇 基于.net搭建热插拔式web框架(RazorEngine实现)
在开头也是先给大家道个歉,由于最近准备婚事导致这篇文章耽误了许久,同时也谢谢老婆大人对我的支持. 回顾上篇文章,我们重造了一个controller,这个controller中用到了视图引擎,我们的视图 ...
- 架构探险——第三章(搭建轻量级Java Web框架)
解决的问题 servlet的数量会随业务功能的扩展而不断增加,我们有必要减少servlet的数量,交给controller处理,它负责调用service的相关方法,并将返回值放入request或res ...
- 读《架构探险——从零开始写Java Web框架》
内容提要 <架构探险--从零开始写Java Web框架>首先从一个简单的 Web 应用开始,让读者学会如何使用 IDEA.Maven.Git 等开发工具搭建 Java Web 应用:接着通 ...
- 什么是web框架?
英文原文:http://jeffknupp.com/blog/2014/03/03/what-is-a-web-framework/ 在原文基础上加上了自己在翻译过程中,查看的资料和自己的一些理解,同 ...
随机推荐
- GOJ1150(矩阵快速幂)
sum Time Limit: 1000ms Problem Description: 给定a和n,计算a+aa+aaa+aaaa+...+a...a(n个a) 的和. Input: 测试数据有多组, ...
- Coreseek:indexer crashed不解之谜
前两天浩哥让我再把Coreseek的索引再做一次,由于需求那边有点变化,要把索引的公司名字显示出来,就在配置文件中面加入了sql_field_string:字符串字段.. 这个属性特别好用,由于它不仅 ...
- C语言中结构体參数变量的传递
[文章摘要] 在C语言中,结构体參数变量常常作为函数的參数来进行传递.但假设參数设置不当.会出现内存问题. 本文以实际的程序代码为例.具体地介绍怎样正确地使用结构体參数变量.为相关的开发工作提供了參考 ...
- poj Budget
Budget 建图好题.不知道为什么提交一直TLE. 然后.该了几次,看了别人的普通网络流都过了. 我觉得可能是卡DINIC的某些部分吧.这题就是一道普通的上下界最小流. 建图麻烦,所以说一下建图吧. ...
- 在web浏览器中判断app是否安装并直接打开
最近公司App产品在运营推广上有一个需求,就是要求可以让用户在访问我们的推广网页时,就可以判断出这个用户手机上是否安装了我们的App,如果安装了则可以直接在网页上打开,否则就引导用户前往下载.从而形成 ...
- POJ 2299 Ultra-QuickSort (求序列的逆序对数)
题意:废话了一大堆就是要你去求一个序列冒泡排序所需的交换的次数. 思路:实际上是要你去求一个序列的逆序队数 看案例: 9 1 0 5 4 9后面比它小的的数有4个 1后面有1个 0后面没有 5后面1个 ...
- 【LaTeX排版】LaTeX论文排版<三>
A picture is worth a thousand words(一图胜千言).图在论文中的重要性不言而喻,本文主要解说图的制作与插入. 1.图像的插入 图像能够分为两大类:位图和向量图 ...
- NET Core控制反转(IoC)
ASP.NET Core中的依赖注入(1):控制反转(IoC) ASP.NET Core在启动以及后续针对每个请求的处理过程中的各个环节都需要相应的组件提供相应的服务,为了方便对这些组件进行定制, ...
- Operand should contain 1 column(s)
今天sql当测试发现错误:Operand should contain 1 column(s). 因为in背后有多种条件字段,in只有有背后场.
- hbase列表排序
hbase都是依照字典序进行排序的,也就是降序,在页面的表现就是最早的数据(rowkey最小的)排在前面. 眼下的解决方式是:给主键添加一个外键关联表.外键的生成规则是 400000000000-主键 ...