IIS 远程异常
IIS Remote Exception
我有一个 IIS 应用程序,它引用了一个插件管理器,它从一个文件夹中读取所有可用的插件,当它在大约 5-10 分钟后托管时,我开始收到以下异常
[RemotingException: Object '/1608465e_9d80_4b40_be20_4c96904643e0/wizi+0g5od5gwmunm_indiws_253.rem' has been disconnected or does not exist at the server.]
System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg) +14416170
System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type) +388
PluginManager.Core.Interfaces.IPluggin.get_Id() +0
PluginManager.PluginDetails.get_Id() +27
我做了一些研究并遇到了 ILease 和 ISponsor,但我不知道如何实施它或它是如何工作的。我目前的代码是这样的,为了清楚起见,我只是删除了方法主体的一部分
[编辑:添加了方法主体]
public class AssemblyReflectionProxy : MarshalByRefObject
{
private string _assemblyPath;
public AssemblyReflectionProxy()
{
Id = "";
}
public void LoadAssembly(String assemblyPath)
{
try
{
_assemblyPath = assemblyPath;
Assembly.ReflectionOnlyLoadFrom(assemblyPath);
}
catch (FileNotFoundException)
{
}
}
public TResult Reflect<TResult>(Func<Assembly, TResult> func)
{
var directory = new FileInfo(_assemblyPath).Directory;
ResolveEventHandler resolveEventHandler = (s, e) => OnReflectionOnlyResolve(e, directory);
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += resolveEventHandler;
var assembly = AppDomain.CurrentDomain.ReflectionOnlyGetAssemblies().FirstOrDefault(a => String.Compare(a.Location, _assemblyPath, StringComparison.Ordinal) == 0);
var result = func(assembly);
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve -= resolveEventHandler;
return result;
}
public T GetEntryType<T>()
{
var directory = new FileInfo(_assemblyPath).Directory;
ResolveEventHandler resolveEventHandler = (s, e) => OnReflectionOnlyResolve(e, directory);
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += resolveEventHandler;
var assembly = AppDomain.CurrentDomain.ReflectionOnlyGetAssemblies().FirstOrDefault(a => string.Compare(a.Location, _assemblyPath, StringComparison.Ordinal) == 0);
if (assembly != null)
{
var result = assembly.GetTypes();
var type = result.FirstOrDefault(x => x.GetInterface(typeof(T).Name) != null);
if (type != null)
{
var remoteObject = AppDomain.CurrentDomain.CreateInstanceFrom(type.Assembly.Location, type.FullName);
var obj = remoteObject.Unwrap();
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve -= resolveEventHandler;
return (T)obj;
}
}
return default(T);
}
private Assembly OnReflectionOnlyResolve(ResolveEventArgs args, DirectoryInfo directory)
{
var loadedAssembly = AppDomain.CurrentDomain.ReflectionOnlyGetAssemblies().FirstOrDefault(asm => string.Equals(asm.FullName, args.Name, StringComparison.OrdinalIgnoreCase));
if (loadedAssembly != null)
{
return loadedAssembly;
}
var assemblyName = new AssemblyName(args.Name);
var dependentAssemblyFilename = Path.Combine(directory.FullName, assemblyName.Name + ".dll");
if (File.Exists(dependentAssemblyFilename))
{
return Assembly.ReflectionOnlyLoadFrom(dependentAssemblyFilename);
}
return Assembly.ReflectionOnlyLoad(args.Name);
}
private string Id { get; set; }
internal string GetId()
{
if (String.IsNullOrEmpty(Id))
{
var fileBytes = File.ReadAllBytes(_assemblyPath);
var hash = Convert.ToBase64String(fileBytes).GetHashCode();
var bytes = BitConverter.GetBytes(hash);
StringBuilder sb = new StringBuilder();
foreach (byte b in bytes)
sb.Append(b.ToString("X2"));
Id = sb.ToString();
}
return Id;
}
}
public sealed class AssemblyManager : MarshalByRefObject, IDisposable
{
private readonly Dictionary<string, AppDomain> _assemblyDomains = new Dictionary<string, AppDomain>();
readonly Dictionary<string, AssemblyReflectionProxy> _proxies = new Dictionary<string, AssemblyReflectionProxy>();
public AssemblyManager()
{
}
public string LoadAssembly(string assemblyPath)
{
var fileInfo = new FileInfo(assemblyPath);
var name = fileInfo.Name.Replace(".dll", "");
if (fileInfo.Exists)
{
if (!_assemblyDomains.ContainsKey(name))
{
var appDomain = CreateChildDomain(AppDomain.CurrentDomain, fileInfo.Name);
_assemblyDomains[name] = appDomain;
try
{
Type proxyType = typeof(AssemblyReflectionProxy);
{
var proxy = (AssemblyReflectionProxy)appDomain.CreateInstanceFrom(proxyType.Assembly.Location, proxyType.FullName).Unwrap();
proxy.LoadAssembly(assemblyPath);
_proxies[name] = proxy;
return name;
}
}
catch
{ }
}
else
{
return name;
}
}
return "";
}
public void Unload()
{
}
private AppDomain CreateChildDomain(AppDomain parentDomain, string domainName)
{
var evidence = new Evidence(parentDomain.Evidence);
var setup = parentDomain.SetupInformation;
return AppDomain.CreateDomain(domainName, evidence, setup);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~AssemblyManager()
{
Dispose(false);
}
public IPluggin GetEntryPluggin(string name)
{
IPluggin plugin = default(IPluggin);
if (_proxies.ContainsKey(name))
{
plugin = _proxies[name].GetEntryType<IPluggin>();
}
return plugin;
}
private void Dispose(bool disposing)
{
if (disposing)
{
foreach (var appDomain in _assemblyDomains.Values)
AppDomain.Unload(appDomain);
_assemblyDomains.Clear();
}
}
internal string GetEntryPlugginID(string name)
{
string Id = "";
if (_proxies.ContainsKey(name))
{
Id = _proxies[name].GetId();
}
return Id;
}
}
我的界面是
public interface IPluggin
{
string Name { get; }
string Version { get; }
string Id { get; }
void Initialize();
string[] GetElements();
void SaveSettings(string settings);
void SetBasePath(string path);
}
.NET 远程处理有点过时了,但我不明白为什么应用程序域之间的通信会出现问题,所以...
当你创建一个远程对象的新实例时,你实际上做的是请求远程端为你创建一个,而你只维护一个代理。远程端将每个此类对象与 lease 相关联 - 描述对象生命周期如何处理的内容。这意味着即使客户端仍然对远程对象有强引用,远程对象也可以在其租约到期时被垃圾回收。
检查是否发生这种情况的最简单方法是使用 RemotingServices.GetLifetimeService
方法。只需在代理对象上使用它,你就会得到你需要的信息——例如,CurrentState
会告诉你远程是否还活着。如果是,您还可以使用 Renew
.
延长租约
因此,处理远程对象生命周期的通常方法是——检查它是否还活着;如果是,请延长租约并为所欲为。确保你也检查了 CurrentLeaseTime
- 最好保持它一些合理的值,而不是总是 Renew
固定的时间。
我有一个 IIS 应用程序,它引用了一个插件管理器,它从一个文件夹中读取所有可用的插件,当它在大约 5-10 分钟后托管时,我开始收到以下异常
[RemotingException: Object '/1608465e_9d80_4b40_be20_4c96904643e0/wizi+0g5od5gwmunm_indiws_253.rem' has been disconnected or does not exist at the server.]
System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg) +14416170
System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type) +388
PluginManager.Core.Interfaces.IPluggin.get_Id() +0
PluginManager.PluginDetails.get_Id() +27
我做了一些研究并遇到了 ILease 和 ISponsor,但我不知道如何实施它或它是如何工作的。我目前的代码是这样的,为了清楚起见,我只是删除了方法主体的一部分 [编辑:添加了方法主体]
public class AssemblyReflectionProxy : MarshalByRefObject
{
private string _assemblyPath;
public AssemblyReflectionProxy()
{
Id = "";
}
public void LoadAssembly(String assemblyPath)
{
try
{
_assemblyPath = assemblyPath;
Assembly.ReflectionOnlyLoadFrom(assemblyPath);
}
catch (FileNotFoundException)
{
}
}
public TResult Reflect<TResult>(Func<Assembly, TResult> func)
{
var directory = new FileInfo(_assemblyPath).Directory;
ResolveEventHandler resolveEventHandler = (s, e) => OnReflectionOnlyResolve(e, directory);
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += resolveEventHandler;
var assembly = AppDomain.CurrentDomain.ReflectionOnlyGetAssemblies().FirstOrDefault(a => String.Compare(a.Location, _assemblyPath, StringComparison.Ordinal) == 0);
var result = func(assembly);
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve -= resolveEventHandler;
return result;
}
public T GetEntryType<T>()
{
var directory = new FileInfo(_assemblyPath).Directory;
ResolveEventHandler resolveEventHandler = (s, e) => OnReflectionOnlyResolve(e, directory);
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += resolveEventHandler;
var assembly = AppDomain.CurrentDomain.ReflectionOnlyGetAssemblies().FirstOrDefault(a => string.Compare(a.Location, _assemblyPath, StringComparison.Ordinal) == 0);
if (assembly != null)
{
var result = assembly.GetTypes();
var type = result.FirstOrDefault(x => x.GetInterface(typeof(T).Name) != null);
if (type != null)
{
var remoteObject = AppDomain.CurrentDomain.CreateInstanceFrom(type.Assembly.Location, type.FullName);
var obj = remoteObject.Unwrap();
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve -= resolveEventHandler;
return (T)obj;
}
}
return default(T);
}
private Assembly OnReflectionOnlyResolve(ResolveEventArgs args, DirectoryInfo directory)
{
var loadedAssembly = AppDomain.CurrentDomain.ReflectionOnlyGetAssemblies().FirstOrDefault(asm => string.Equals(asm.FullName, args.Name, StringComparison.OrdinalIgnoreCase));
if (loadedAssembly != null)
{
return loadedAssembly;
}
var assemblyName = new AssemblyName(args.Name);
var dependentAssemblyFilename = Path.Combine(directory.FullName, assemblyName.Name + ".dll");
if (File.Exists(dependentAssemblyFilename))
{
return Assembly.ReflectionOnlyLoadFrom(dependentAssemblyFilename);
}
return Assembly.ReflectionOnlyLoad(args.Name);
}
private string Id { get; set; }
internal string GetId()
{
if (String.IsNullOrEmpty(Id))
{
var fileBytes = File.ReadAllBytes(_assemblyPath);
var hash = Convert.ToBase64String(fileBytes).GetHashCode();
var bytes = BitConverter.GetBytes(hash);
StringBuilder sb = new StringBuilder();
foreach (byte b in bytes)
sb.Append(b.ToString("X2"));
Id = sb.ToString();
}
return Id;
}
}
public sealed class AssemblyManager : MarshalByRefObject, IDisposable
{
private readonly Dictionary<string, AppDomain> _assemblyDomains = new Dictionary<string, AppDomain>();
readonly Dictionary<string, AssemblyReflectionProxy> _proxies = new Dictionary<string, AssemblyReflectionProxy>();
public AssemblyManager()
{
}
public string LoadAssembly(string assemblyPath)
{
var fileInfo = new FileInfo(assemblyPath);
var name = fileInfo.Name.Replace(".dll", "");
if (fileInfo.Exists)
{
if (!_assemblyDomains.ContainsKey(name))
{
var appDomain = CreateChildDomain(AppDomain.CurrentDomain, fileInfo.Name);
_assemblyDomains[name] = appDomain;
try
{
Type proxyType = typeof(AssemblyReflectionProxy);
{
var proxy = (AssemblyReflectionProxy)appDomain.CreateInstanceFrom(proxyType.Assembly.Location, proxyType.FullName).Unwrap();
proxy.LoadAssembly(assemblyPath);
_proxies[name] = proxy;
return name;
}
}
catch
{ }
}
else
{
return name;
}
}
return "";
}
public void Unload()
{
}
private AppDomain CreateChildDomain(AppDomain parentDomain, string domainName)
{
var evidence = new Evidence(parentDomain.Evidence);
var setup = parentDomain.SetupInformation;
return AppDomain.CreateDomain(domainName, evidence, setup);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~AssemblyManager()
{
Dispose(false);
}
public IPluggin GetEntryPluggin(string name)
{
IPluggin plugin = default(IPluggin);
if (_proxies.ContainsKey(name))
{
plugin = _proxies[name].GetEntryType<IPluggin>();
}
return plugin;
}
private void Dispose(bool disposing)
{
if (disposing)
{
foreach (var appDomain in _assemblyDomains.Values)
AppDomain.Unload(appDomain);
_assemblyDomains.Clear();
}
}
internal string GetEntryPlugginID(string name)
{
string Id = "";
if (_proxies.ContainsKey(name))
{
Id = _proxies[name].GetId();
}
return Id;
}
}
我的界面是
public interface IPluggin
{
string Name { get; }
string Version { get; }
string Id { get; }
void Initialize();
string[] GetElements();
void SaveSettings(string settings);
void SetBasePath(string path);
}
.NET 远程处理有点过时了,但我不明白为什么应用程序域之间的通信会出现问题,所以...
当你创建一个远程对象的新实例时,你实际上做的是请求远程端为你创建一个,而你只维护一个代理。远程端将每个此类对象与 lease 相关联 - 描述对象生命周期如何处理的内容。这意味着即使客户端仍然对远程对象有强引用,远程对象也可以在其租约到期时被垃圾回收。
检查是否发生这种情况的最简单方法是使用 RemotingServices.GetLifetimeService
方法。只需在代理对象上使用它,你就会得到你需要的信息——例如,CurrentState
会告诉你远程是否还活着。如果是,您还可以使用 Renew
.
因此,处理远程对象生命周期的通常方法是——检查它是否还活着;如果是,请延长租约并为所欲为。确保你也检查了 CurrentLeaseTime
- 最好保持它一些合理的值,而不是总是 Renew
固定的时间。