AppDomain and MarshalByRefObject life time : how to avoid RemotingException?

后端 未结 10 1990
时光取名叫无心
时光取名叫无心 2020-12-04 08:53

When a MarshalByRef object is passed from an AppDomain (1) to another (2), if you wait 6 mins before calling a method on it in the second AppDomain (2) you will get a Remoti

相关标签:
10条回答
  • 2020-12-04 09:11

    I recently ran into this exception also. Right now my solution is just unload AppDomain and then reload AppDomain after a long interval. Luckily this temporary solution work for my case. I wish there is a more elegant way to deal with this.

    0 讨论(0)
  • 2020-12-04 09:13

    Unfortunately this solution is wrong when AppDomains are used for plugin purposes (assembly of the plugin must not be loaded into your main appdomain).

    The GetRealObject() call in your constructor and destructor results in obtaining the real type of the remote object, which leads to trying to load the assembly of the remote object into the current AppDomain. This may cause either an exception (if the assembly cannot be loaded) or the unwanted effect that you have loaded a foreign assembly that you cannot unload later.

    A better solution can be if you register your remote objects in your main AppDomain with ClientSponsor.Register() method (not static so you must create a client sponsor instance). By default it will renew your remote proxies in every 2 minutes, which is enough if your objects has the default 5 minutes lifetime.

    0 讨论(0)
  • 2020-12-04 09:13

    If you would like to re-create the remote object after it has been garbage collected without having to create an ISponsor class nor giving it infinite lifetime, you could call a dummy function of the remote object while catching RemotingException.

    public static class MyClientClass
    {
        private static MarshalByRefObject remoteClass;
    
        static MyClientClass()
        {
            CreateRemoteInstance();
        }
    
        // ...
    
        public static void DoStuff()
        {
            // Before doing stuff, check if the remote object is still reachable
            try {
                remoteClass.GetLifetimeService();
            }
            catch(RemotingException) {
                CreateRemoteInstance(); // Re-create remote instance
            }
    
            // Now we are sure the remote class is reachable
            // Do actual stuff ...
        }
    
        private static void CreateRemoteInstance()
        {
            remoteClass = (MarshalByRefObject)AppDomain.CurrentAppDomain.CreateInstanceFromAndUnwrap(remoteClassPath, typeof(MarshalByRefObject).FullName);
        }
    }
    
    0 讨论(0)
  • 2020-12-04 09:15

    There are two possible solutions here.

    The Singleton approach: Override InitializeLifetimeService

    As Sacha Goldshtein points out in the blog post linked to by the original poster, if your Marshaled object has Singleton semantics you can override InitializeLifetimeService:

    class MyMarshaledObject : MarshalByRefObject
    {
        public bool DoSomethingRemote() 
        {
          // ... execute some code remotely ...
          return true; 
        }
    
        [SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.Infrastructure)]
        public override object InitializeLifetimeService()
        {
          return null;
        }
    }
    

    However, as user266748 points out in another answer

    that solution wouldn't work if such an object were created each time a client connects itself, because they would never be GCed and your memory consumption would go up and up until either you stop your server or it crashes because it has no more memory

    The Class-Based approach: Using ClientSponsor

    A more general solution is to use ClientSponsor to extend the life of a class-activated remote object. The linked MSDN article has a useful starting example you can follow:

    using System;
    using System.Runtime.Remoting;
    using System.Runtime.Remoting.Channels;
    using System.Runtime.Remoting.Channels.Tcp;
    using System.Runtime.Remoting.Lifetime;
    namespace RemotingSamples
    {
    
       class HelloClient
       {
           static void Main()
          {
             // Register a channel.
             TcpChannel myChannel = new TcpChannel ();
             ChannelServices.RegisterChannel(myChannel);
             RemotingConfiguration.RegisterActivatedClientType(
                                    typeof(HelloService),"tcp://localhost:8085/");
    
             // Get the remote object.
             HelloService myService = new HelloService();
    
             // Get a sponsor for renewal of time.
             ClientSponsor mySponsor = new ClientSponsor();
    
             // Register the service with sponsor.
             mySponsor.Register(myService);
    
             // Set renewaltime.
             mySponsor.RenewalTime = TimeSpan.FromMinutes(2);
    
             // Renew the lease.
             ILease myLease = (ILease)mySponsor.InitializeLifetimeService();
             TimeSpan myTime = mySponsor.Renewal(myLease);
             Console.WriteLine("Renewed time in minutes is " + myTime.Minutes.ToString());
    
             // Call the remote method.
             Console.WriteLine(myService.HelloMethod("World"));
    
             // Unregister the channel.
             mySponsor.Unregister(myService);
             mySponsor.Close();
          }
       }
    }
    

    It is worth nothing how lifetime management works in the Remoting API, which is described quite well here on MSDN. I've quoted the part I found most useful:

    The remoting lifetime service associates a lease with each service, and deletes a service when its lease time expires. The lifetime service can take on the function of a traditional distributed garbage collector, and it also adjusts well when the numbers of clients per server increases.

    Each application domain contains a lease manager that is responsible for controlling leases in its domain. All leases are examined periodically for expired lease times. If a lease has expired, one or more of the lease's sponsors are invoked and given the opportunity to renew the lease. If none of the sponsors decides to renew the lease, the lease manager removes the lease and the object can be collected by the garbage collector. The lease manager maintains a lease list with leases sorted by remaining lease time. The leases with the shortest remaining time are stored at the top of the list. The remoting lifetime service associates a lease with each service, and deletes a service when its lease time expires.

    0 讨论(0)
提交回复
热议问题