您的位置:首页 > 其它

NET插件系统之四——提升系统搜索插件和启动速度的思考

2012-06-05 11:14 483 查看
一. 面临的问题

  开发插件系统的主要优势是扩展性,我们不需要为系统模块的集成再多费脑筋,但这也带来了额外的问题。通常,系统需要在每次启动时搜索固定目录下的符合要求的插件。但是,当系统变得越来越庞大,所引用的dll文件越来越多时,就会出现很严重的问题:开启时间慢,性能差,用户体验降低,尤其是在调试程序时,会浪费大量宝贵的时间。

  我确确实实的面临了这样的问题,有兴趣的读者可以看看我的插件系列文章的前几篇,这两天痛定思痛,决心提升系统搜索插件的性能。

  我们先看一段普通的搜索插件的代码:

 

public void GetAllPluginInPath(string Path, string InterFaceName)
{
var DllFileName = from file in Directory.GetFileSystemEntries(Path)
where file.Contains(".dll")
select file;

//string[] DllFileName = Directory.GetFileSystemEntries(Path);
Type[] types;
foreach (string file in DllFileName)
{

if (System.IO.Path.GetExtension(file) == ".dll")
{
Assembly assembly;

try
{
assembly = Assembly.LoadFrom(file);
}
catch
{
continue;
}

try
{
types = assembly.GetTypes();
}
catch (Exception ex)
{
continue;
}

foreach (Type type in types)
{
if (type.GetInterface(InterFaceName) != null && !type.IsAbstract)
{
object thisObject = Activator.CreateInstance(type);

IXPlugin rc1 = thisObject as IXPlugin;

//如果要在启动时被加载
if (rc1 != null && rc1.isStartLoaded)
{
AddPlugin(rc1);
}
}
}
}
}
}


  造成启动慢的主要原因有:

  1. 目录下包含大量dll文件(这是因为项目引用了大量第三方库),它们并不包含我们开发的组件,却白白浪费大量搜索时间。有些dll文件不是托管dll,在获取程序集时还会抛出异常,直接捕获后,也会造成时间损失。

  2. 上述代码仅搜索了满足一种接口规范的插件, (见函数的参数InterFaceName)。如果不止一种插件类型,那么可能要做很多次这样的查找,对性能的影响更大。

3. 为了获取插件的一些信息(比如是否要在启动时加载),不得不实例化其对象获取字段,这种性能损失也是不能承受的。

二. 解决途径

找到问题,我们对症下药:

  1.成熟的软件系统采用了插件树的机制,将插件存储为树结构,包含父子关系,这样能尽可能的提升搜索和加载性能,同时方便管理,比如Ecilpse。 但是,这种复杂的插件管理机制可能不适用于我们开发的轻量级系统,因此我们仅仅考虑扁平化的插件结构。

  2. 虽然插件的数量是经常变化的,但通常加载的dll文件种类很少变化。我们可以考虑把实际包含所需插件的dll文件名列表存储起来,从而在搜索时仅搜索这些固定的dll文件,提升性能。

  3. 插件的种类可能多种多样,所以我们希望能一次性获得全部类型的插件。

  4. 采用.NET4.0的并行库机制实现插件的并行搜索。

三. 插件结构表述

  该部分已经在我的插件系列文章的.NET插件系统之二——不实例化获取插件信息和可视化方法 中进行了描述,主要是标记接口名称的InterfaceAttribute 和 标记实现接口的插件的XFrmWorkAttribute,你需要在插件接口和插件实现的类上添加这两类标识,此处不再赘述。

  我们定义两个数据结构存储插件名称和插件信息:

/// <summary>
/// 为了便于序列化而简化的插件接口数据类型,是简化的InterfaceAttribute
/// </summary>
[Serializable]

public  class PluginNameLite
{
public string myName { get; set; }
public SearchStrategy mySearchStrategy { get; set; }
public string detailInfo { get; set; }

public PluginNameLite()
{
}
public PluginNameLite(InterfaceAttribute attr)
{
myName = attr.myName;
mySearchStrategy = attr.mySearchStrategy;
detailInfo = attr.DetailInfo;
}

}

/// <summary>
/// 插件集合
/// </summary>
public class PluginCollection : ObservableCollection<XFrmWorkAttribute>
{
public PluginCollection()
: base()
{

}

/// <summary>
/// 可以被序列化的简化插件字典,仅包含插件接口名称和搜索策略
/// </summary>
static List<PluginNameLite> myPluginNameList = new List<PluginNameLite>();

/// <summary>
/// 插件字典
/// </summary>
static Dictionary<Type, PluginCollection> mPluginDictionary = new Dictionary<Type, PluginCollection>();


四. 插件搜索的方法

  我们将插件搜索的步骤分为两步:

  1. 搜索所有接口契约(即搜索所有的接口)

  

/// <summary>
/// 获取所有的插件接口契约名称
/// </summary>
/// <param name="Path"></param>
/// <param name="InterFaceName"></param>
public static void GetAllPluginName(string folderLocation, bool isRecursiveDirectory)
{

List<PluginNameLite> mPluginNameList = new List<PluginNameLite>();  //缓存所有插件名称

if (!isRecursiveDirectory)
{
try  //如果不执行递归搜索,则查看在目录下是否有保存了插件名称的文件,若有,直接反序列化之,不执行插件名称搜索
{
mPluginNameList = CustomSerializer.Deserialize<List<PluginNameLite>>(folderLocation + "\\PluginLog.xml");
myPluginNameList.AddRange(mPluginNameList);
return;
}
catch (Exception ex)
{

}
}
var DllFile = from file in Directory.GetFileSystemEntries(folderLocation)  //若无缓存文件,获取目录下全部的dll文件执行搜索
where file.Contains(".dll")
select file;

Parallel.ForEach(DllFile,   //并行化处理
file =>
{
Type[] types;
Assembly assembly;
try
{
assembly = Assembly.LoadFrom(file);

}
catch
{
return;
}

try
{
types = assembly.GetTypes();
}
catch
{
return;
}
foreach (Type type in types)
{

if (type.IsInterface == false)
continue;
// Iterate through all the Attributes for each method.
foreach (Attribute attr in
type.GetCustomAttributes(typeof(InterfaceAttribute), false))
{
mPluginNameList.Add(new PluginNameLite(attr as InterfaceAttribute));
}
}
});
if (isRecursiveDirectory)      ////执行递归搜索
{
foreach (var dir in Directory.GetDirectories(folderLocation))
{
GetAllPluginName(dir, isRecursiveDirectory);
}
}
else   //保存当前目录下的插件名称
{

CustomSerializer.Serialize(mPluginNameList, folderLocation + "\\PluginLog.xml");
myPluginNameList.AddRange(mPluginNameList);
}

}


流程图如下:

完整的插件搜索器代码

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace XFrmWork.Data
{

/// <summary>
/// 插件集合
/// </summary>
public class PluginCollection : ObservableCollection<XFrmWorkAttribute>
{
public PluginCollection()
: base()
{

}

}

/// <summary>
/// 执行搜索策略
/// </summary>
public enum SearchStrategy
{

/// <summary>
/// 目录内搜索
/// </summary>
FolderSearch,
/// <summary>
/// 目录内递归搜索
/// </summary>
RecursiveFolderSearch,
}
/// <summary>
/// 该类定义了插件系统的接口契约记录
/// </summary>

public class InterfaceAttribute : Attribute
{
/// <summary>
/// 该插件接口的名称
/// </summary>
public string myName { get; set; }

/// <summary>
/// 搜索策略
/// </summary>
public SearchStrategy mySearchStrategy { get; set; }

/// <summary>
/// 相关信息
/// </summary>
public string DetailInfo { get; set; }

public InterfaceAttribute(string thisName, string thisDetailInfo, SearchStrategy thisSearchStrategy)
// 定位参数
{

this.myName = thisName;
this.DetailInfo = thisDetailInfo;
this.mySearchStrategy = thisSearchStrategy;

}
}

/// <summary>
/// 为了便于序列化而简化的插件接口数据类型,是简化的InterfaceAttribute
/// </summary>
[Serializable]

public class PluginNameLite
{
public string myName { get; set; }
public SearchStrategy mySearchStrategy { get; set; }
public string detailInfo { get; set; }

public PluginNameLite()
{
}
public PluginNameLite(InterfaceAttribute attr)
{
myName = attr.myName;
mySearchStrategy = attr.mySearchStrategy;
detailInfo = attr.DetailInfo;
}

}
/// <summary>
/// 单例模式提供的插件搜索器
/// </summary>
public class PluginProvider
{

PluginProvider()
{

}
/// <summary>
/// 可以被序列化的简化插件字典,仅包含插件接口名称和搜索策略
/// </summary>
static List<PluginNameLite> myPluginNameList = new List<PluginNameLite>();

/// <summary>
/// 插件字典
/// </summary>
static Dictionary<Type, PluginCollection> mPluginDictionary = new Dictionary<Type, PluginCollection>();

/// <summary>
/// 获取某插件在插件目录中的索引号
/// </summary>
/// <param name="interfaceName">接口名称</param>
/// <param name="className">类名</param>
/// <returns></returns>
public static int GetObjectIndex(Type interfaceName, Type className)
{
foreach (var rc in GetPluginCollection(interfaceName))
{
if (rc.myType == className)
return GetPluginCollection(interfaceName).IndexOf(rc);
}
return 100;
}

/// <summary>
/// 获取所有的插件接口契约名称
/// </summary>
/// <param name="Path"></param>
/// <param name="InterFaceName"></param>
public static void GetAllPluginName(string folderLocation, bool isRecursiveDirectory)
{

List<PluginNameLite> mPluginNameList = new List<PluginNameLite>();  //缓存所有插件名称

if (!isRecursiveDirectory)
{
try  //如果不执行递归搜索,则查看在目录下是否有保存了插件名称的文件,若有,直接反序列化之,不执行插件名称搜索
{
mPluginNameList = CustomSerializer.Deserialize<List<PluginNameLite>>(folderLocation + "\\PluginLog.xml");
myPluginNameList.AddRange(mPluginNameList);
return;
}
catch (Exception ex)
{

}
}
var DllFile = from file in Directory.GetFileSystemEntries(folderLocation)  //若无缓存文件,获取目录下全部的dll文件执行搜索
where file.Contains(".dll")
select file;

Parallel.ForEach(DllFile,   //并行化处理
file =>
{
Type[] types;
Assembly assembly;
try
{
assembly = Assembly.LoadFrom(file);

}
catch
{
return;
}

try
{
types = assembly.GetTypes();
}
catch
{
return;
}
foreach (Type type in types)
{

if (type.IsInterface == false)
continue;
// Iterate through all the Attributes for each method.
foreach (Attribute attr in
type.GetCustomAttributes(typeof(InterfaceAttribute), false))
{
mPluginNameList.Add(new PluginNameLite(attr as InterfaceAttribute));
}
}
});
if (isRecursiveDirectory)      ////执行递归搜索
{
foreach (var dir in Directory.GetDirectories(folderLocation))
{
GetAllPluginName(dir, isRecursiveDirectory);
}
}
else   //保存当前目录下的插件名称
{

CustomSerializer.Serialize(mPluginNameList, folderLocation + "\\PluginLog.xml");
myPluginNameList.AddRange(mPluginNameList);
}

}

/// <summary>
/// 获取所有插件
/// </summary>
/// <param name="folderLocation"></param>
/// <param name="isRecursiveDirectory">是否进行目录递归搜索</param>
public static void GetAllPlugins(string folderLocation, bool isRecursiveDirectory)
{
bool isSaved = false;  //是否已经保存了包含插件的dll文件列表
List<string> mPluginFileList = new List<string>();  //包含插件的dll文件列表
List<string> allDllFileList = new List<string>();    //所有dll文件列表
if (!isRecursiveDirectory)
{
try
{
mPluginFileList = CustomSerializer.Deserialize<List<string>>(folderLocation + "\\PluginFileLog.xml");
isSaved = true;

}
catch (Exception ex)
{
allDllFileList = (from file in Directory.GetFileSystemEntries(folderLocation)
where file.Contains(".dll")
select file).ToList();
}
}
else
{
allDllFileList = (from file in Directory.GetFileSystemEntries(folderLocation)
where file.Contains(".dll")
select file).ToList(); ;
}

Type[] types;
IEnumerable<string> dllPluginFils;  //最终要进行处理的的dll文件
if (mPluginFileList.Count == 0)       //如果不存在插件文件目录,则获取该目录下所有dll文件
dllPluginFils = allDllFileList;
else
dllPluginFils = from file in mPluginFileList
select folderLocation + file;   //否则将插件文件名称拼接为完整的文件路径

Parallel.ForEach(dllPluginFils,
file =>
{

Assembly assembly;
try
{
assembly = Assembly.LoadFrom(file);
}
catch
{
return;
}

try
{
types = assembly.GetTypes();
}
catch
{
return;
}
foreach (Type type in types)
{
if (type.IsInterface == true)
continue;
Type interfaceType = null;
string interfaceName = null;
foreach (var interfacename in myPluginNameList)   //对该Type,依次查看是否实现了插件名称列表中的接口
{
interfaceType = type.GetInterface(interfacename.myName);
if (interfaceType != null)
{
interfaceName = interfacename.myName;
// Iterate through all the Attributes for each method.
foreach (Attribute attr in
type.GetCustomAttributes(typeof(XFrmWorkAttribute), false))  //获取该插件的XFrmWorkAttribute标识
{
XFrmWorkAttribute attr2 = attr as XFrmWorkAttribute;

attr2.myType = type;  //将其类型赋值给XFrmWorkAttribute

if (attr2.MainKind != interfaceName)
{
continue;
}

PluginCollection pluginInfo = null;   //保存到插件字典当中
if (mPluginDictionary.TryGetValue(interfaceType, out pluginInfo))
{
pluginInfo.Add(attr2);       //若插件字典中已包含了该interfaceType的键,则直接添加

}
else
{
var collection = new PluginCollection();
collection.Add(attr2);
mPluginDictionary.Add(interfaceType, collection);    //否则新建一项并添加之

}
file = file.Replace(folderLocation, "");  //获取文件在该文件夹下的真实名称
if (!mPluginFileList.Contains(file))    //若插件文件列表中不包含此文件则添加到文件目录中
mPluginFileList.Add(file);
goto FINISH;

}

}

}
FINISH:
;

}
});

if (isRecursiveDirectory)  //执行递归搜索
{
foreach (var dir in Directory.GetDirectories(folderLocation))
{

GetAllPlugins(dir, isRecursiveDirectory);
}
}
else
{
if (!isSaved)   //若没有保存插件文件目录,则反序列化保存之。
CustomSerializer.Serialize(mPluginFileList, folderLocation + "\\PluginFileLog.xml");

}

}
/// <summary>
/// 获取接口中固定索引类型的实例
/// </summary>
/// <param name="interfaceName">接口名称</param>
/// <param name="index">索引号</param>
/// <returns>实例化的引用</returns>
public static Object GetObjectInstance(Type interfaceName, int index)
{
return Activator.CreateInstance(GetPluginCollection(interfaceName)[index].myType);
}
/// <summary>
/// 获取某程序集的接口列表
/// </summary>
/// <param name="interfaceName"></param>
/// <param name="myAssembly"></param>
/// <returns></returns>
public static ObservableCollection<XFrmWorkAttribute> GetPluginCollection(Type interfaceName, Assembly myAssembly)
{
return GetPluginCollection(interfaceName, myAssembly, false);
}

public static ObservableCollection<XFrmWorkAttribute> GetPluginCollection(Type interfaceName, Assembly myAssembly, bool isAbstractRequired)
{
if (mPluginDictionary.ContainsKey(interfaceName))
return mPluginDictionary[interfaceName];
else  //若发现插件不存在,则再执行搜索(这种可能性很低)
{

PluginCollection tc = new PluginCollection();

Type[] types = myAssembly.GetTypes();
foreach (Type type in types)
{
if (type.GetInterface(interfaceName.ToString()) != null)
{
if (!isAbstractRequired && type.IsAbstract == true)
{
continue;
}

// Iterate through all the Attributes for each method.
foreach (Attribute attr in
type.GetCustomAttributes(typeof(XFrmWorkAttribute), false))
{
XFrmWorkAttribute attr2 = attr as XFrmWorkAttribute;
tc.Add(attr2);
}
}
}
mPluginDictionary.Add(interfaceName, tc);
return tc;
}
}

public static ObservableCollection<XFrmWorkAttribute> GetPluginCollection(Type interfaceName)
{
if (mPluginDictionary.ContainsKey(interfaceName))
return mPluginDictionary[interfaceName];
else
{
PluginCollection tc = new PluginCollection();

Assembly assembly = Assembly.GetAssembly(interfaceName);
Type[] types = assembly.GetTypes();
foreach (Type type in types)
{
if (type.GetInterface(interfaceName.ToString()) != null && !type.IsAbstract)
{

// Iterate through all the Attributes for each method.
foreach (Attribute attr in
type.GetCustomAttributes(typeof(XFrmWorkAttribute), false))
{
XFrmWorkAttribute attr2 = attr as XFrmWorkAttribute;
tc.Add(attr2);
}
}
}
mPluginDictionary.Add(interfaceName, tc);
return tc;
}
}
}

}


  

  

  
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: