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/ 在原文基础上加上了自己在翻译过程中,查看的资料和自己的一些理解,同 ...
随机推荐
- hdu1081(最大子矩阵)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1081 分析:a[i][j]代表第i行,前j个数据的和:那么由a[i][j]可得sum[k][long] ...
- NTFS权限设置时卡死
客户是一家技术咨询和零部件制造的小公司,使用的文件服务器为R410上插4块1T硬盘做raid 5,服务器操作系统为windows server 2008R2,所有的设计资料的授权都是结合域账户和NTF ...
- HYSBZ 2243(树链剖分)
题目连接:http://acm.hust.edu.cn/vjudge/contest/view.action?cid=28982#problem/D 题意:给定一棵有n个节点的无根树及点权和m个操作, ...
- [置顶] ※数据结构※→☆线性表结构(list)☆============双向链表结构(list double)(三)
双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱.所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点. ~~~~~~~~~~~~ ...
- poj1094Sorting It All Out
主题链接: 啊哈哈,选我 题目: Sorting It All Out Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 268 ...
- mongoDB 查询附近的人的语句
mongoDB 自带LBS查询附近的人 {"location":{ $nearSphere: { $geometry: { type : "Point", co ...
- Rational Rose的四种视图介绍
Rose模型中有四种视图:Use Case View(用例视图),Logical View(逻辑视图),Component View(组建视图)和Deployment View(配置视图). 用例视图 ...
- 抢车位中的排名bug(比較使用了无符号数)
昨天把这个发在了qzone,想来还是怪怪的,还是转过来不吧,纯当发现了一个虫子,玩笑一下.只是csdn如今不能贴图,挺郁闷的,原文在http://user.qzone.qq.com/110907073 ...
- 【cocos2d-x制作别踩白块儿】第一期:游戏介绍
这一系类文章.我们将来分析时下最火的一款游戏 -- 别踩白块儿. 无图无真相,先上图 这就是我们终于要完毕项目的效果图. 游戏刚開始的最以下有一栏为黄色,紧接着上面每一行都是有一个黑色块,其余为白色块 ...
- Effective C++ Item 33 避免遮掩继承过来的名称
本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie ? 不懂 c++为什么derived classes 内的名称要遮掩 base classe ...