Конференция ".Net" » Спонсоры для контролирования времени жизни удаленных объектов
 
  • Джо © (16.01.06 05:15) [0]
    На сервере создается и маршализируется некий объект:

     /// Тут создается и регестрируется канал и.т.п
     ...
     ///
     RemoteObject ro =
       new RemoteObject (SomeParam);
     RemotingServices.SetObjectUriForMarshal(ro,"bla-bla");
     RemotingServices.Marshal(ro);


    Понятное дело, что объект по истечении определенного времени (5 мин) убивается сборщиком мусора.
    Чтобы не допустить этого, сделал следующее.

     /// Простой класс реализующий ISponsor
           class LifeTimeSponsor: ISponsor
           {
               public TimeSpan Renewal (ILease lease)
               {
                   TimeSpan ts = new TimeSpan(10000); // даем пожить еще
                   return ts;
               }

           }




    Ну, и после маршализации удаленного объекта сделал следующее:

     ISponsor sponsor = new LifeTimeSponsor();

     ILease lifetime = (ILease)si.GetLifetimeService();
     lifetime.Register(sponsor);



    Это, кажется, работает, т.е, объект на сервере не убивается сборщиком до завершения приложения.
    Но, в связи с тем, что приведенное решение является плодом собственных изысканий в новой для меня среде, есть вопросы такого плана к коллегам, имеющим опыт работы с .Net:
    1. Стандартен ли такой подход?
    2. Нет ли каких подводных камней, которые я упустил?
    3. Есть ли альтернативные (более простые) подходы.
    4. Ну, и не накосячил ли я где? ;)

    Спасибо.
  • Джо © (16.01.06 05:22) [1]
    Сорри, вот это
    ILease lifetime = (ILease)si.GetLifetimeService();


    читать как
    ILease lifetime = (ILease)ro.GetLifetimeService();



    Просто опечатка.
  • ИА (16.01.06 07:13) [2]
    А GC.KeepAlive не спасет отца русской демократии?
  • Lamer@fools.ua © (16.01.06 09:59) [3]
    >>ИА   (16.01.06 07:13) [2]

    Не спасёт.

    >>Джо ©   (16.01.06 05:15)

    См. в MSDN:
    Activation of Remote Objects (в индексе "client-activated objects" либо "server-activated objects").
    В 2005-м ссылка такая:
    ms-help://MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualStudio.v80.en/dv_fxremoting/html/e90d0c64-00da-4080-ae78-a6c558825c25.h tm
  • Курдль © (16.01.06 10:39) [4]

    > 1. Стандартен ли такой подход?


    Не стандартен. Рекомендую почитать книгу http://www.rsdn.ru/res/book/net/dotnet_remoting.xml - там все очень подробно написано.
  • Джо © (16.01.06 14:01) [5]

    > [4] Курдль ©   (16.01.06 10:39)

    Спасибо, поищу. Кажется, была где-то в эл. виде, буду потихоньку читать.
    ----
    > [3] Lamer@fools.ua ©   (16.01.06 09:59)

    Спасибо. Попробовал воспользоватьсе этим способом из указанного топика:
    You can create the former type of Singleton object by overriding the InitializeLifetimeService method of MarshalByRefObject to return a null reference (Nothing in Visual Basic). This effectively keeps the object in memory as long as the host application domain is running.

    Переопределил у remote class указанный метод, чтобы он возвращал null, однако, не смотря на это, объект убивается на сервере спустя некоторое время, как и прежде. Или я что-то упустил?

    Да, я не привел код клиента, ссылку на object proxy получаю так:

                           RemoteObject ro =
                               (RemoteObject)Activator.GetObject(
                                 typeof(RemoteObject),
                                 connection_string
                               );

  • Джо © (16.01.06 14:22) [6]
    И еще одно.
    Я не использую
    RemotingConfiguration.RegisterWellKnownServiceType

    так в моем случае не хотелось бы использовать конструктор по умолчанию, вызываемый в данном случае.
  • Джо © (16.01.06 16:07) [7]
    В общем, пока нарисовался такой вариант (несколько более громоздкий сравнительно с первоначальным).


           /// Реализация спонсора.
           class LifeTimeSponsor: ISponsor
           {
               public TimeSpan Renewal (ILease lease)
               {
                   TimeSpan ts = TimeSpan.FromMinutes(5);
                   return ts;
               }

           }

           /// Перекрываем InitializeLifetimeService в remote class
           public class RemoteObject: MarshalByRefObject
           {
               public override Object InitializeLifetimeService ()
               {
                    return null;
                }


               ///
               /// skipped
               ///
            }



    Marshalling и активация на клиенте остались прежними.

    Регистрацию  спонсора переделал так:

                           ISponsor sponsor = new LifeTimeSponsor();
                           ILease lifetime = (ILease) RemotingServices.GetLifetimeService(si); // вот так
                           lifetime.Register(sponsor);



    Вроде бы все работает как нужно, однако не уверен, потому, что мой первоначальный вариант без перекрытия InitializeLifetimeService тоже, кажись, работал.

    Коллеги, покритикуйте, плиз.
  • Джо © (16.01.06 21:09) [8]
    В общем, как я убедился, без перекрытия InitializeLifetimeService все тоже прекрасно работает :)
  • Курдль © (17.01.06 10:42) [9]
    Читайте книжки - пользы больше.

    Но на всяк случай приведу свою тестовую прогу:


    using System;
    using System.Drawing;
    using System.Collections;
    using System.ComponentModel;
    using System.Windows.Forms;
    using System.Data;
    using System.Runtime.Remoting;
    using System.Runtime.Remoting.Lifetime;
    using System.Runtime.Remoting.Channels;
    using System.Runtime.Remoting.Channels.Tcp;

    namespace CommonNameSpace
    {

    /// <summary>
    /// Главная форма приложения-клиента. Она может быть назначена спонсором аренды
    /// Тогда её тип должен быть производым от интерфейса ISponsor,
    /// а для этого необходимо реализовать метод Renewal (см. ниже)
    /// </summary>
    public class frmClientMain : System.Windows.Forms.Form, ISponsor
    {
     /// <summary>
     /// Required designer variable.
     /// </summary>
     private System.ComponentModel.Container components = null;
     private System.Windows.Forms.Button button1;
     private System.Windows.Forms.Button button2;
     private System.Windows.Forms.Panel panel1;
     private System.Windows.Forms.Panel panel2;
     private System.Windows.Forms.RichTextBox richTextBox1;

     /// <summary>
     /// Реализация метода Renewal для спонсора аренды
     /// </summary>
     /// <param name="lease"></param>
     /// <returns></returns>
     public TimeSpan Renewal(ILease lease)
     {
      richTextBox1.Text += String.Format("\r\n{0}
    \tСпонсор аренды продлил аренду.", DateTime.Now);
      //Возвращаемое значение - срок продления аренды
      return TimeSpan.FromSeconds(15);
     }

     //Глобальная переменная удаленного объекта
     RemoteObject FRemoteObject;

     public frmClientMain()
     {
      //
      // Required for Windows Form Designer support
      //
      InitializeComponent();

      //
      // TODO: Add any constructor code after InitializeComponent call
      //

      richTextBox1.Text += String.Format("\r\n{0}
    \tЗапущено клиентское приложение.", DateTime.Now);

      try
      {
       //Форматировщик создается для возможности задания доп. параметров канала
       //Это необходимо для механизма спонсорской аренды КАО
       BinaryServerFormatterSinkProvider bsfsp = new BinaryServerFormatterSinkProvider();
       bsfsp.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;
       richTextBox1.Text += String.Format("\r\n{0}
    \tСоздан двоичный форматировщик приемника", DateTime.Now);

       //Канал создается для общания между менеджером аренды сервера и спонсором аренды клиента
       IDictionary props = new Hashtable();
       props["name"] = "MyTcpChannel";
       props["port"] = "0"; //Слушать по всем портам

       TcpChannel channel = new TcpChannel(props, null, bsfsp);
       ChannelServices.RegisterChannel(channel);
       richTextBox1.Text += String.Format("\r\n{0}\tСоздан и зарегистрирован канал", DateTime.Now);

       //Регистрируется клиентски-активируемый объект (КАО)
       RemotingConfiguration.RegisterActivatedClientType(typeof(RemoteObject), "tcp://LocalHost:8000");

       //Создадим экземпляр удаленного объекта и передедим ему для проверки GUID
       RemoteObject _ro = new RemoteObject(System.Guid.NewGuid().ToString());
       if (_ro != null)
       {
        FRemoteObject = _ro;
        richTextBox1.Text += String.Format("\r\n{0}
    \tСоздан удаленный объект.", DateTime.Now);
        /*
        //Альтернативный способ задания спонсора аренды
        //(если никаких действий, кроме продления аренды от него не требуется)
        ClientSponsor _cs = new ClientSponsor(TimeSpan.FromSeconds(10));
        _cs.Register(_ro);
        */
        //Создается интерфейс аренды для управления временем жизни КАО
        ILease lease = (ILease)RemotingServices.GetLifetimeService(_ro);
        if (lease != null)
        {
         lease.Register((ISponsor)this);
         richTextBox1.Text += String.Format("\r\n{0}
    \tЗарегистрирован спонсор аренды.", DateTime.Now);
        }
        else
         MessageBox.Show("Сервис времени жизни не получен", "Ошибка");
       }
       else
        MessageBox.Show("Удаленный объект не получен", "Ошибка");
      }
      catch(Exception er)
      {
       MessageBox.Show("При инициализации объекта произошла ошибка:\n" + er.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
      }

     }

     public void Unregister(MarshalByRefObject obj)
     {
      try
      {
       ILease lease = (ILease)RemotingServices.GetLifetimeService(obj);
       if (lease != null)
        lease.Unregister(this);
      }

      catch
      {
       //Если сервер подох еще раньше, чем закрывается эта форма
      }

     }

     /// <summary>
     /// Clean up any resources being used.
     /// </summary>
     protected override void Dispose( bool disposing )
     {
      if( disposing )
      {
       if (components != null)
       {
        components.Dispose();
       }

      }
      base.Dispose( disposing );
     }

     #region Windows Form Designer generated code
     #endregion

     /// <summary>
     /// The main entry point for the application.
     /// </summary>
     [STAThread]
     static void Main()
     {
      Application.Run(new frmClientMain());
     }


     private void button1_Click(object sender, System.EventArgs e)
     {
      try
      {
       FRemoteObject.IndicateClientWork();
       richTextBox1.Text += String.Format("\r\n{0}
    \tOK!", DateTime.Now);
      }
      catch(Exception er)
      {
       MessageBox.Show("При обращении к объекту произошла ошибка:\n" + er.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
      }

     }

     private void button2_Click(object sender, System.EventArgs e)
     {
      richTextBox1.Text += String.Format("\r\n{0}
    \tПолучено время от сервера: {1}", DateTime.Now, FRemoteObject.GetCurrentDateTime().ToString());
     }

     private void frmClientMain_Closed(object sender, System.EventArgs e)
     {
      Unregister(FRemoteObject);
     }

    }
    }


  • Джо © (17.01.06 15:52) [10]
    > [9] Курдль ©   (17.01.06 10:42)


    Спасибо, но, как я понял, в общем, это тоже самое, что у меня. Отличие только в том, что спонсор у вас создается на клиенте, а у меня на сервере. Или еще есть какие-то принципиальные отличия?
  • Курдль © (17.01.06 16:13) [11]

    > спонсор у вас создается на клиенте, а у меня на сервере


    А кому нужен спонсор на сервере???  8-()

    В том-то и прелесть КАО, что они порождаются на сервере для каждого клиента индивидуально. Их методы могут принимать параметры от "своего" клиента, хранить индивидуальные данные "своего" клиента и т.п. Они умирают, как только перестают быть нужными "своему" клиенту.

    Кому нужны неубиваемые КАО? Тогда проще работать с Wellknown objects.
  • Джо © (17.01.06 16:44) [12]
    > [11] Курдль ©   (17.01.06 16:13)
    > А кому нужен спонсор на сервере???  8-()
    > В том-то и прелесть КАО,

    В том-то и дело, что мне НЕ нужен CAO. У меня SAO. :^)
  • Курдль © (17.01.06 17:05) [13]

    > В том-то и дело, что мне НЕ нужен CAO. У меня SAO. :^)


    А тогда зачем ему спонсор аренды??!! :-)
    Он активируется в памяти всякий раз, как кто-либо обращается к его члену.
    Точнее, вызывает его метод. Не понимаю, какой смысл продлевать ему жизнь? Ведь он не способен нести никакой полезной информации и не имеет корректного   способа "личной идентификации". Т.е. после того, как отработал вызванный метод, SAO превращается в натуральный мусор только и ожидающий уборки.
    Или я не понял истинных целей? :-)
  • Курдль © (17.01.06 17:18) [14]
    Кстати, Ваш способ инициализации SAO не попадает под его классическое определение, которое предполагает лишь регистрацию, как указано в [6].
    Скорее этот способ можно назвать "мануальной маршаллизацией", в которой я не силен :)
    Но повторюсь, была бы мне известна конечная цель, возможно и мои знания бы пригодились.
  • Джо © (17.01.06 17:26) [15]
    > [13] Курдль ©   (17.01.06 17:05)
    > А тогда зачем ему спонсор аренды??!! :-)
    > Он активируется в памяти всякий раз, как кто-либо обращается
    > к его члену.

    Ну да, именно так все и происходит. Объект этот создается на сервере один раз, все клиенты, подключающиеся к нему, получают ссылку на один и тот же экземпляр. Фактическая активация происходит при вызове одного из методов, точнее, метода Login серверного  объекта.

    Только дело том, что если к нему в течение некоторого времени не обращаются, сборщик его убивает. А он должен жить на сервере всегда, ожидая запросов со стороны клиентов. Затем и спонсор аренды, чтобы продлевал ему жизнь неограниченно. Добился этого простой регистрацией спонсора аренды на сервере, который в методе Renewal продлевает жизнь объекта еще на какое-то время.

    Может, у меня какая-то путаница?

    Если это важно, то суть моего тестового сервера такова.
    Он реализует работу файлового сервера. Клиенты, подключаясь к экземпляру на сервере, вызывают его методы для листинга директорий и загрузки файлов. В зависимости, он прав подключающегося клиента (они хранятся на сервере), сервер разрешает или нет вызовы определенных методов. Вот и все. В этой схеме меня все устраивает, вопрос был только в методах продления жизни.
  • Джо © (17.01.06 17:27) [16]
    > В зависимости, он прав

    В зависимости от прав.
    Сорри.
  • Курдль © (18.01.06 09:47) [17]
    Hello alexmoon,


    > Может, у меня какая-то путаница?


    Вроде все логично.

    Подозрительным кажется только "Фактическая активация происходит при вызове одного из методов, точнее, метода
    Login серверного  объекта.


    Я думаю, что фактическая активация происходит после вызова конструктора на
    RemoteObject ro = new RemoteObject
    (SomeParam);



    Еще я опасаюсь, что при определенном количестве методов, объект станет слишком громоздким для постоянно активного
    состояния на сервере.

    В моих вариантах используется множество классических SAO (они для удобства проектируются по сущностям, например для
    работы с субъектами - один, для работы с документами - другой, и т.д.).
    Все "индивидуальные" данные о клиенте ходят между клиентом и SAO посредством контекста вызова (Call Context).
     

    --
    Best regards,
    Logic                          mailto:Alexmoon@logexp.ru
  • Джо © (18.01.06 15:03) [18]
    > [17] Курдль ©   (18.01.06 09:47)

    Спасибо за ответ!
    Вроде со своим примером разобрался. Буду дальше читать литературу.
 
Конференция ".Net" » Спонсоры для контролирования времени жизни удаленных объектов
Есть новые Нет новых   [134430   +2][b:0][p:0.006]