【转载】企业级服务器设计与实现经验之插件系统基础篇
最初之所以要采用插件的形式进行开发,主要是为了解决功能服务的“热插拔”问题,在决定采用“框架+插件”的方式进行设计后,我们就更进一步,打算将一个个可以分割开来的拥有完整功能的组件都做成插件的形式,并且使同类型的插件的接口兼容,这样在以后需要改变时就可以灵活的进行替换。比如,将通信部分做成通信插件、日志记录部分做成日志插件等等。
首先,我们要弄清楚,什么是插件?我给出了一个定义,可能有失偏颇。
插件又称为扩展,是一种特殊的组件,用于增强和扩展基本框架的行为能力。插件和框架的通信协议是一组接口,插件的各种特性都可以通过该接口进行访问。插件主要有如下特点:
(1)一个插件是一个独立的物理单元。它可以独立的提供一项完整的服务(功能),而不需要依赖于其它插件。
(2)插件能自我描述 ―― 插件的所有对外的发布信息都由插件自己内部提供,而不依赖于外部文件或注册表。
(3)插件能自我管理 ―― 插件如果需要配置信息,则插件自己能读取和修改配置信息,而不是框架来完成这些事情。
(4)插件自我独立 ―― 一个插件不得引用其它的插件。如果一个插件与另一个插件关系紧密,那么应该将这两个插件合成一个插件,或者重新分解为两个独立的插件.
在DataServer系统中,所有的插件从IAddin继承,IAddin接口定义如下:
public interface IAddin
{
void Start() ; //不同类型的插件对开始和停止的解释不一样,如果一个插件没有此意义,可实现为空
void Stop() ;
int ServiceKey {get ;}
string ServiceName {get ;}
string Description {get ;} //插件说明信息
AddinAppendixInfo AddinAppendixInfo {get ;}
string AddinType {get ;}
}
接口中的所有方法属性的意义一目了然,其中,ServiceKey是插件关键字,用于区分不同的插件。我们可以把同一类型的插件的关键字限定在某个范围之内,比如,我将通信插件的关键字范围限定在2000-2999之间。AddinAppendixInfo用于描述插件的附加信息,如作者、创建日期、版本号等。
public class AddinAppendixInfo
{
public string Author ;
public string TimeCreated ;
public string TimeLastRevised ;
public string AddinVersion ;
}
AddinType是插件类型,在DataServer中,有如下几种插件类型:
public class AddinTypes
{
public const string Net = "通信插件" ;
public const string Function = "功能插件" ;
public const string Reporter = "报告者插件" ;
public const string BasicReqDealer = "基本请求处理者插件" ;
public const string RespondInterceptor = "回复截获者插件" ;
}
每种类型的插件的功用将在后面说明。
大部分插件都需要进行配置,我将配置部分抽象为一个公共的接口,IAddinConfig定义如下:
public interface IAddinConfig
{
//以下方法返回的控件或窗体必须实现EnterpriseServerBase.Configure.IConfigControl接口
IConfigControl GetAddinConfigForm() ;
IConfigControl GetAddinConfigControl() ;
}
这样,一个插件如果需要配置信息,则实现该接口即可。前面已经提到“插件如果需要配置信息,则插件自己能读取和修改配置信息,而不是框架来完成这些事情”,插件就是通过IAddinConfig接口来操作配置信息的。GetAddinConfigForm用于获取一个独立的配置窗体,通过此窗体可以完成对插件配置,而GetAddinConfigControl获取的是一个配置控件,一般其结构与功能和配置窗体一样,只不过它可以嵌入到其它任意的窗体中,比如到时,我们可以在主框架中设置一个专门用于配置的TabControl,而其中的每一页就用于加载一个GetAddinConfigControl返回的配置控件,从而实现对配置的统一管理。
IConfigControl究竟是如何操作配置信息的?其定义如下:
public interface IConfigControl
{
bool Initialize(string configPath ,string objName) ; //objName 是配置文件中的对象名
bool SaveConfig() ;
bool ReDisplayConfigInfo() ; //显示更新后的配置信息
event EventHandler ConfigChanged ;
}
一般,配置信息保存于配置文件中,所以初始化时要给出配置路径,而objName参数的作用,可以参见前面的《如何使用XCodeFactory自动生成XML配置文件和对应的解析类?》这篇文章,我的所有于配置文件相关的解析类和配置窗体都是通过XCodeFactory自动生成的。XCodeFactory接口的其它几个方法的意思很明显,就不用解释了。
接下来,我们看看如何将插件加载到系统中。
我们都知道,一个插件是一个独立的物理单元,我们如何获得该插件的类型了?有很多办法可以解决,比如,我们访问插件目录下的每个插件,通过Assembly.LoadFrom加载一个插件,然后Type. IsSubclassOf查看其是否实现了某种插件类型的接口,从而判断其类型。我采用了更直观的方式,用插件的名字来标志其类型,比如所有的通信插件都以NetAddin.dll结尾。
可以用一个特定的模块来加载插件,我称之为“插件加载器”,插件加载器用于加载某目录下的所有特定类型的插件,"特定"表现在两个方面:(1)插件的名字以[addinSign].dll结尾;(2)插件中的主类是从[baseAddinType]继承。插件加载器的接口定义如下:
public interface IAddinLoader
{
ArrayList LoadAddins(string addinFolderPath ,string addinSign ,Type baseAddinType) ;
}
很简单,只有一个方法,该方法加载某目录下的所有某特定类型的插件,而返回的列表中的每个元素就引用着一个插件。
插件加载器仅仅负责将某种类型的插件加载进来,但是如何实现将所有的插件加载进来、并动态的加载/移除插件了?我们需要一个管理器组件来完成这件事情,它就是IAddinManagement,其接口定义如下:
public interface IAddinManagement
{
void LoadAllAddins(string addin_FolderPath ) ;
bool ReLoadAddins(out int increment) ;
void UnloadAllAddins() ;
bool DynRemoveAddin(int serviceKey) ;
bool DynRemoveAddin(string serviceName) ;
void StartAllAddin() ;
void StopAllAddin() ;
ArrayList AddinBoxList {get ;}
Hashtable GetAllServiceList() ; //key - name
event CallBackSimple AddinsChanged ;
}
可以看到,IAddinManagement实现了对所有插件的全部管理,其还发布了一个事件AddinsChanged,就是当插件列表发生变化的时候用于通知外部。
关于插件的基础部分,就先介绍这么多,在后面的章节中,我们将深入到各种类型的插件的内部。
转载于:https://www.cnblogs.com/fx2008/archive/2011/11/24/2261184.html
【转载】企业级服务器设计与实现经验之插件系统基础篇相关推荐
- 其实一切与游戏无关--yy笔录+转载网络游戏服务器设计
严格的说,我从来没有玩过网络游戏,对于网游的理解仅限于大学时其他室友之间关于魔兽世界的经验交流.曾经yy过网游的后台实现方法,但现在回想起来那时的想法确实幼稚的很.两个月打酱油般的工作当中给我最深的体 ...
- 转载来自朱小厮的博客的NIO相关基础篇
用户空间以及内核空间概念 我们知道现在操作系统都是采用虚拟存储器,那么对32位操作系统而言,它的寻址空间(虚拟存储空间)为4G(2的32次方).操心系统的核心是内核,独立于普通的应用程序,可以访问受保 ...
- Python Qt GUI设计:QPrinter打印图片类(基础篇—21)
打印图像是图像处理软件中的一个常用功能,打印图像实际上是在QPaintDevice中画图,与平常在QWidget.QPixmap和Qlmage中画图一样,都是创建一个QPainter对象进行画图的,只 ...
- Python Qt GUI设计:QClipboard剪贴数据类(基础篇—19)
QClipboard类提供了对系统剪贴板的访问,可以在应用程序之间复制和粘贴数据,它的操作类似于QDrag类,使用类似的数据类型. QApplication类有一个静态方法clipboard(),它返 ...
- Python Qt GUI设计:QSlider滑动条类(基础篇—16)
QSlider控件提供了一个垂直或水平的滑动条,滑动条是一个用于控制有界值的典型控件,它允许用户沿水平或垂直方向在某一范围内移动滑块,并将滑块所在的位置转换成一个合法范围内的整数值. 有时候这种方式比 ...
- 胡侃EXCEL服务器设计需要的因素
开篇之前,我首先要感谢那些看了我前面几篇文章并与我进行探讨的网友,更要感谢那些委托我代为进行软件设计的网友,谢谢你们的信任! 你需要什么样的软件? 几年下来,与形形色色使用EXCEL服务器的朋友交流甚 ...
- 如何设计一个高可用的运营系统
转载自 如何设计一个高可用的运营系统 这是一篇来自粉丝的投稿,作者[林湾村龙猫]近一年在做关于运营活动方面的设计.本文是他的关于运营活动的总结,Hollis做了一点点修改. 概述 一个产品业务的发 ...
- Python Qt GUI设计:窗口布局管理方法【强化】(基础篇—6)
目录 1. 水平布局类(QHBoxLayout) 2.垂直布局类(QVBoxLayout) 3.网格布局类(QGridLayout) 3.1.单一的网络布局 3.2.跨越行.列的网络布局 4.表单布局 ...
- 【转载】专访阿里陶辉:大规模分布式系统、高性能服务器设计经验分享
关注陶辉很长时间,初次对陶辉的了解还是在我们CSDN的博客上,从2007年开始写博客,一直到现在,如果不是对技术的追求和热爱,以及热爱分享的精神,我想不是很多人能坚持下来,拥有多年大型互联网公司的从业 ...
最新文章
- Spring整合Quartz定时发送邮件
- 爬取股票信息(股票代码+价格)
- 【五校联考6day2】er
- matlab逆变换法产生随机数_matlab中产生随机数的程序
- SQL Server 2014如何提升非在线的在线操作
- niagara mysql_Niagara AX连接MySQL数据库
- 使用arguments对象模拟函数重载
- (转)UIButton用法详解一
- Spring Boot 搭建 Eureka Servrer 单机模式、高可用模式
- Python Class System
- Java jar 如何防止被反编译
- win10固态硬盘分区 整数_固态硬盘怎么重装win10系统?Win10固态硬盘重装系统教程...
- 软件工程导论知识点总结
- iOS框架引见--媒体层
- 多媒体计算机软件按功能分为,多媒体软件可分为
- c语言求5个整数最小公倍数,C语言求两个正整数的最小公倍数和最大公约数
- flutter ListView.separated 带分割线
- 饥荒服务器物品指令,饥荒控制台指令大全物品大全服务器管理命令大全.docx
- 最新中文行业垂直搜索引擎大全
- 怎样选择ADC芯片?
热门文章
- LA3602DNA序列
- hdu2196 树形DP
- 【Linux 内核】Linux 内核源码几个重要的入口源文件及函数介绍 ( 系统初始化 | 内存管理 | 虚拟文件系统 | 网络管理 )
- 【Flutter】Flutter 混合开发 ( Flutter 与 Native 通信 | 在 Flutter 端实现 BasicMessageChannel 通信 )
- 【Android 插件化】Hook 插件化框架 ( Hook 实现思路 | Hook 按钮点击事件 )
- 【MATLAB】基本绘图 ( text 函数 | annotation 函数 | 绘制图像示例 )
- 【数据挖掘】基于密度的聚类方法 - DBSCAN 方法 ( K-Means 方法缺陷 | 基于密度聚类原理及概念 | ε-邻域 | 核心对象 | 直接密度可达 | 密度可达 | 密度连接 )
- 【IntelliJ IDEA】中文乱码问题 ( 代码乱码 | 编译乱码 | 控制台乱码 )
- php中static和self的区别
- websocket趣说_转