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 固定的时间。