我有一个用C#(.NET 3.5)编写的Windows服务,这个服务的作用是接收带有特定接口的DLL并执行它们的方法。
有些客户抱怨有时服务无法启动,在我查看我们的日志后,我注意到在尝试动态加载程序集时的部分需要花费大量时间(约〜 30-40秒),因此服务在60秒后尝试启动时会超时。
这里的主要问题是这个问题并不总是发生。我们很少能在QA环境中重现这种缓慢,所以当我试图重构动态加载的代码时,我真的不知道我是否正在解决这个问题。
目前代码正在加载2个DLL,并且对于每个程序集,我们获得所有引用的程序集,以便以递归方式动态加载程序集树。
我还可以看到,当我尝试加载.NET System.Data
,System.Core
之类的.NET程序集时,我会在递归时加载,我忘记了因为它们不是在我的目录中。这似乎是一个主要的耗时。还有更好的建议吗?
/// <summary>
/// Loads all the assemblies and classes needed to run methods dinamically.
/// </summary>
public class BLService
{
public static Dictionary<string, Assembly> AssemblyHistory { get; private set; }
static BLService()
{
// assembly
Assembly assemblyToLoad = LoadAssembly(System.Configuration.ConfigurationManager.AppSettings[settingKey]);
// load assembly dependencies
foreach (var dependencyAssemblyName in assemblyToLoad.GetReferencedAssemblies())
DynamicallyLoadAssembly(dependencyAssemblyName);
}
private static Assembly DynamicallyLoadAssembly(AssemblyName dependencyAssemblyName)
{
LCTracer.Instance.WriteToLog(string.Format("Loading assembly | Trying to dynamically load assembly {0}", dependencyAssemblyName.Name), LCTracer.ProfileAction.Start);
Assembly currentAssembly = LoadAssembly(dependencyAssemblyName.Name);
if (currentAssembly != null)
{
LCTracer.Instance.WriteToLog(string.Format("Loading assembly | Iterating on Assembly {0} references", dependencyAssemblyName.Name));
foreach (var assembly in currentAssembly.GetReferencedAssemblies())
{
DynamicallyLoadAssembly(assembly);
}
LCTracer.Instance.WriteToLog(string.Format("Loading assembly | Finished iterating on Assembly {0} references", dependencyAssemblyName.Name));
}
LCTracer.Instance.WriteToLog(string.Format("Loading assembly"), LCTracer.ProfileAction.End);
return currentAssembly;
}
/// <summary>
/// Loads an assembly
/// </summary>
/// <param name="assemblyName"></param>
/// <returns></returns>
private static Assembly LoadAssembly(string assemblyName)
{
string assembliesDir = System.Configuration.ConfigurationManager.AppSettings["AssemblyPath"];
string assemblyPath = Path.Combine(assembliesDir, assemblyName + ".dll");
// We only load files from inside the designated directory.
if (!File.Exists(assemblyPath))
{
LCTracer.Instance.WriteToLog("Loading assembly | " + assemblyName + " does not exist in path");
return null;
}
byte[] assemblyByteArray = File.ReadAllBytes(assemblyPath);
Assembly asm = null;
try
{
asm = Assembly.Load(assemblyByteArray);
// Load only if already loaded.
if (!AssemblyHistory.ContainsKey(asm.GetName().Name))
{
AssemblyHistory.Add(asm.GetName().Name, asm);
LCTracer.Instance.WriteToLog("Loading assembly | Success: Adding Assembly " + assemblyName + " to history.");
}
}
catch (Exception ex)
{
LCTracer.Instance.WriteToLog("Loading assembly | Error: Adding Assembly " + assemblyName + " failed: message - " + ex.Message + " ,stack trace - " + ex.StackTrace + " ,inner exception - " + ex.InnerException, null, LCTracer.LogDebugLevel.Low);
}
return (asm != null) ? AssemblyHistory[asm.GetName().Name] : AssemblyHistory[assemblyName];
}
}
编辑:为上下文添加了一些代码。您可以将此服务视为插件运行器,它获取插件并执行它。 任何帮助将不胜感激。
答案 0 :(得分:0)
尝试在抱怨客户端的服务器上安装Fusion日志查看器(随Windows SDK一起提供),并将其设置为记录所有加载尝试(也是成功的)。 然后你可以看到它在哪里寻找组件 - 你可能会发现那里有一些缓慢的网络路径或其他问题。
答案 1 :(得分:0)
我找到了解决方案!
我尝试加载的程序集之一是NHibernate.XmlSerializers.dll,它是在运行时创建的,如果没有找到,在慢速机器上这个创建需要花费很多时间。
Here是一篇关于如何预生成此dll以提高性能的文章。
主要命令是:
sgen.exe NHibernate.dll /type:NHibernate.Cfg.MappingSchema.HbmMapping /compiler:/keyfile:NHibernate.snk