第二篇 基于.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(, UrlPath.Length - ),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框架(重造Controller)
第二篇 基于.net搭建热插拔式web框架(沙箱的构建)的更多相关文章
- 第三篇 基于.net搭建热插拔式web框架(重造Controller)
由于.net MVC 的controller 依赖于HttpContext,而我们在上一篇中的沙箱模式已经把一次http请求转换为反射调用,并且http上下文不支持跨域,所以我们要重造一个contro ...
- 第五篇 基于.net搭建热插拔式web框架(拦截器---请求管道)
好了,前边我们把核心内容介绍完了,接下来要做的就是拦截用户的请求,并把请求转向沙箱内. 这里我们准备通过实现一个HttpModule类来完成请求的拦截与转发.新建一个HuberHttpModule类, ...
- 第四篇 基于.net搭建热插拔式web框架(RazorEngine实现)
在开头也是先给大家道个歉,由于最近准备婚事导致这篇文章耽误了许久,同时也谢谢老婆大人对我的支持. 回顾上篇文章,我们重造了一个controller,这个controller中用到了视图引擎,我们的视图 ...
- 基于.net搭建热插拔式web框架(实现原理)
第一节:我们为什么需要一个热插拔式的web框架? 模块之间独立开发 假设我们要做一个后台管理系统,其中包括“用户活跃度”.“产品管理”."账单管理"等模块.每个模块中有自己的业务特 ...
- net搭建热插拔式web框架
net搭建热插拔式web框架(重造Controller) 由于.net MVC 的controller 依赖于HttpContext,而我们在上一篇中的沙箱模式已经把一次http请求转换为反射调用,并 ...
- net搭建热插拔式web框架(沙箱的构建)
net搭建热插拔式web框架(沙箱的构建) 上周五写了一个实现原理篇,在评论中看到有朋友也遇到了我的问题,真的是有种他乡遇知己的感觉,整个系列我一定会坚持写完,并在最后把代码开源到git中.上一篇文章 ...
- 带你手写基于 Spring 的可插拔式 RPC 框架(一)介绍
概述 首先这篇文章是要带大家来实现一个框架,听到框架大家可能会觉得非常高大上,其实这和我们平时写业务员代码没什么区别,但是框架是要给别人使用的,所以我们要换位思考,怎么才能让别人用着舒服,怎么样才能让 ...
- 转-基于NodeJS的14款Web框架
基于NodeJS的14款Web框架 2014-10-16 23:28 作者: NodeJSNet 来源: 本站 浏览: 1,399 次阅读 我要评论暂无评论 字号: 大 中 小 摘要: 在几年的时间里 ...
- 两个基于C++/Qt的开源WEB框架
1.tufao 项目地址: https://github.com/vinipsmaker/tufao 主页: http://vinipsmaker.github.io/tufao/ 介绍: Tufão ...
随机推荐
- DeepMind背后的人工智能:深度学习原理初探
去年11月,一篇名为<Playing Atari with Deep Reinforcement Learning>的文章被初创人工智能公司DeepMind的员工上传到了arXiv网站.两 ...
- php 设计模式--准备篇
要了解设计模式 首先我们要先了解 php的命名空间和类的自动载入的功能 下面我们来说一下 命名空间 概念缘由:比如一个a.php的文章 但是我们需要两个 此时同一个目录下不可能存在两个a.php 那么 ...
- VLC 资料整理
libvlc_media_t的创建 创建libvlc_media_t有两种方法:libvlc_media_new_path()和libvlc_media_new_location().简单描述一下这两 ...
- 【应用】_有道词典客户端一个后缀名为sql的数据库。
[缘起] 在清理电脑磁盘的时候,看一看各安装文件夹有占用了多大容量,发现有道词典居然达140MB了,于是进去看看. 发现个有趣的文件:XXX.sql. 首先我们看一看它的安装文件夹的结构: Dict ...
- Bzoj1426 收集邮票
Time Limit: 1 Sec Memory Limit: 162 MBSubmit: 292 Solved: 232 Description 有n种不同的邮票,皮皮想收集所有种类的邮票.唯一 ...
- DUT Star Weekly Contest #3 Problem F Solution
题目链接 问题转化 \[a_i+a_j+(i-j)^2=a_i+i^2+a_j+j^2-2ij\] 令 \(b_i=a_i+i^2\) , 问题化为: 求 \[\max \{b_i+b_j-2ij\} ...
- [C#] 日志类
在程序发布到服务器上的时候,不能在像本地执行一样可以调试,在发生错误时候,往往不能很方便的查找错误.将错误信息写入文件是一种比较常用的处理方法.以下是一个日志类,实现以下功能: 1)按日期每天生产不同 ...
- Bitmap转换成BitmapImage
public BitmapImage BitmapToBitmapImage(System.Drawing.Bitmap bitmap) { MemoryStream ms = new MemoryS ...
- HD3033I love sneakers!(分组背包+不懂)
I love sneakers! Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) ...
- 页面测试点testpoint
页面测试点整理(非逻辑测试点) 由于自己一年来一直在做页面测试,也看了很多测试理论的书和方法,但是方法并非也无法照搬,此处总结自己工作以来通过各种坑摸出来的一些方法点,希望一边靠上经典测试理论,一边形 ...