IONA Orbix2000 Session Management

 

 By chengxuwen

mailto:xwcheng@proinfo.com.cn

http://www.proinfo.com.cn

 

 


目录                  

1.  前言:.. 3

2.  Orbix2k租借模式框架.. 3

2.1 概述.. 3

2.2 什么叫Session Managemant 3

2.3 Orbix2k中租借的设计原则.. 3

2.4 服务端动作.. 3

2.5 客户端动作.. 3

3.  例子:一个租借应用程序.. 5

3.1 IDL. 5

3.2 本例的意图.. 5

3.3客户端与服务端的交互.. 5

4.  客户端:使用租借插件.. 6

4.1 准备工作.. 6

4.2 如何使用插件.. 6

4.3 配置量.. 6

4.4 配置举例.. 6

5.  服务端: 使用租借插件.. 6

5.1 IT_Leasing模块.. 6

5.2实现LeaseCallback接口.. 7

5.2.1 对象的实例.. 7

5.2.2 代码实现(LeaseCallbackImpl) 8

5.3 IT_Leasing::Current 跟踪客户端Session. 10

5.3.1 概述.. 10

5.3.2 代码实现(PersonFactoryImpl类).. 10

5.4 发布租借.. 13

5.4.1 前期准备.. 13

5.4.2 在哪儿发布.. 13

5.4.3 代码实现(main()函数) 13

5.5 配置服务端插件.. 14

5.5.1 概述.. 14

5.5.2 配置变量.. 14

5.5.3 本例子的完全配置(itadmin show session_management) 14

6.  参考资料.. 15

 


1.    前言:

      分布式系统与其它软件系统一样,运行时难免会出现一些错误。但同时分布式环境下的引起的错误往往比独立系统复杂很多,因为在分布式系统中,任何一个组件的失败都会潜在的导致整个系统的瘫痪。而资源的服务者与使用者往往身处独立的进程甚至主机中,资源使用者在失败前往往来不及宣告放弃使用资源便告终终止,导致正确运行的服务器中还留有不能被释放的资源。在Orbix2k中有多种方式可以实现服务端资源的回收,主要通过Servant Manager(包括Servant Activator,Servant Locator)实现,另外一种更为方便的方式就是本文中将要讲到的租借模式(Leasing Model)。

2.    Orbix2k租借模式框架

2.1 概述

      Orbix2k中把租借做为一个插件,通过检查客户端状况来管理服务端被客户端使用的资源。Orbix2k把这整套体系叫租借框架。当客户端启动时,它获得一个租借,然后定时续借。当客户端终止时,它会自动释放该租借。如果客户端崩溃了,它已来不及释放这个租借,这时服务端会在稍后检查到租借已过期。这样,无论客户端是正常还是非正常关闭都会被检查到。

2.2 什么叫Session Managemant

      当客户端终止时,如何同时清除在服务端的客户端使用的资源,这是一个在CORBA应用中常常会遇到的问题。在服务端,基于Session的应用程序会分配给客户端请求的资源。为了避免服务端资源的过度膨胀,有必要建立一种检测客户端连接状况的机制。但是CORBA标准本身并没有提出统一的方法。

2.3 Orbix2k中租借的设计原则

Orbix2k中租借模式的设计满足如下特征:

l         客户端零影响

l         IDL接口零影响

l         容易实现

l         CORBA标准兼容

l         完全可配置

2.4 服务端动作

动作序列

描述

1

当服务端启动时,自动调用租借插件

2

初始化时,服务端公布租借,并将一个LeaseCallback对象绑定到名字服务中

3

服务端导出对象引用(IOR)时,租借插件自动把租借信息加到IOR中,当然这并不与CORBA标准冲突

2.5 客户端动作

动作序列

描述

1

同服务端一样,当客户端启动时,它自动加载租借插件

2

插件检查客户端将要使用的IOR,看是否有租借信息,如果有,则插件开始一个与公布这个IOR的服务端对应的session

3

插件自动更新租借信息(续借)

4

客户端关闭的两种情况:

正常关闭,插件自动释放租借信息

非正常关闭,服务端插件会检查到客户端没有再续借,于是,租借过期,服务端清除该客户端使用的资源

 

下面如图表示整个过程:

 

图一 客户端得到租借

客户端初始始化时开始租借插件:

客户端租借插件从名字服务中获得IT_Leasing::LeaseCallback对象引用

调用LeaseCallback对象的acquire_lease( )客户端租借插件开始一个Session

 

图二 客户端续借

客户端定期续借,其中的时间间隔由配置量plugins:lease_ping_time决定。

 

图三 客户端关闭

客户端正常关闭,租借插件自动调用lease_release( )结束Session.

客户端非正常关闭,服务端的租借插件调用LeaseCallback对象的lease_expired( )以释放客户端使用的资源。这个调用的发生由配置量plugins:lease_reap_time决定。

 

3.    例子:一个租借应用程序

3.1 IDL

//IDL

module LeaseTest{

      exception PersonAlreadyExists { };

      interface Person {

           string name( );

      };

 

      interface PersonFactory {

           Person create_person(in string name) raises (PersonAlreadyExists);

      };

};

3.2 本例的意图

本例主要说明无论客户端创建多少个Person对象,无论以何种方式终止运行,服务端总会知道,并最终释放已无用的Person对象。这样,服务端保持内存的干净

3.3客户端与服务端的交互

序列

描述

1

客户端调用create_person( )创建Person对象

2

客户端终止时,先前在服务端被创建的对象就不再需要保留在内存中,然后被释放

4.    客户端:使用租借插件

4.1 准备工作

      客户端租借插件会不断的调用resolve( )从名字服务得到租借对象引用。因此,在客户端运行前,需要正确配置Locator,Activator和名字服务。

4.2 如何使用插件

      在客户端使用插件唯一需要做的就是正确的将插件的信息加到配置库中。

4.3 配置量

要使用客户端的插件,下面这些变量需要配置:

表一 客户端使用的配置量

配置量

作用

plugins:lease:shlib_name

插件对应的库名

Orb_plugins

客户端启动时需要加载的插件列表

Binding:client_binding_list

指定参与请求处理过程的插件

4.4 配置举例

Orbix 2000 Configuration File

plugins:lease:shlib_name=”it_lease”;

orb_plugins=[“local_log_stream”,”lease”,”iiop_profile”,”giop”,”iiop”];

binding:client_binding_list=[“POA_Coloc”,”LEASE+GIOP+IIOP”,”GIOP+IIOP”];

 

5.    服务端: 使用租借插件

5.1 IT_Leasing模块

//IDL

module IT_Leasing

{

interface LeaseCallback

{

   LeaseID acquire_lease( ) raise ( CouldNotAcquireLease);

   void lease_expired(in LeaseID lease_id);

   void lease_released(in LeaseID lease_id);

   void renew_lease(in LeaseID lease_id) raises(LeaseHashExpired);

};

local interface ServerLeaseAgent

{

void advertise_lease(in LeaseCallback lease_callback) raises(CouldNotAdvertiseLease);

   LeaseID manufacture_lease_id();

   void withdraw_lease();

   void lease_acquired(in LeaseID leaseid);

void lease_released(in LeaseID lease_id);

};

 

local interface Current : CORBA::Current

{

   exception NoContext{ };

   LeaseID get_lease_id( ) raise (NoContext);

};

};

LeaseCallback Interface : 服务端程序需实现IT_Leasing::LeaseCallback接口,用于处理租借插件发出的有关的事件。例如,当一个租借过期时,插件调用IT_Leasing::LeaseCallback::lease_expired()

Server Lease Agent Interface :ServerLeaseAgent接口的实现由租借插件提供。服务程序通过调用ServerLeaseAgent Interface定义的操作与租借插件通讯。比如,服务程序调用IT_Leasing::ServerLeaseAgent::advertise_lease()初始化租借插件。

Current Interface :服务端用于获取哪些资源正与租借相关联的信息。或者说,它是用于服务端保持创建的资源与正在使用这些资源的客户端的关联关系。

      具体过程是这样的:服务端要得到是哪个租借客户调用了某个操作,需要从IT_Leasing:Current对象中获取租借信息,而IT_Leasing.Current是从CORBA::Current中继承过来的,CORBA:Current的定义就是用于获取CORBA调用中的元信息。得到IT_Leasing:Current对象后,就可以调用get_lease_id()得到与调用相关的租借IDlease ID)。

 

租借服务端实现的步骤:

步骤

动作

1

实现LeaseCallback接口

2

IT_Leasing::Current 跟踪客户端Session

3

发布租借

4

配置服务端的插件

 

5.2实现LeaseCallback接口

5.2.1 对象的实例

下面的两个对象实例会被LeaseCallbackImpl用到:

实例名

描述

LeaseObj

IT_Leasing::ServerLeaseAgent的对象引用。它用于与租借插件通讯

M_factory

PersonFactoryImpl对象指针。用于创建新的Person对象

 

5.2.2 代码实现(LeaseCallbackImpl)

 

extern IT_Leasing::ServerLeaseAgent_var leaseObj;//指向主程序(server.cxx/h)

 

// acquire_lease() -- Implements IDL operation

// "IT_Leasing::LeaseCallback::acquire_lease".

//

圆角矩形标注: Acquire_lease() 被客户端租借插件调用。这个例子只是简单的得到一个Lease Id后通过调用ServerLeaseAgent的lease_acquired()操作通知插件已收到租借请求。插件开始监测新的租借的生命周期和生存状态char*

LeaseCallbackImpl::acquire_lease()

  IT_THROW_DECL((

    CORBA::SystemException,

    IT_Leasing::CouldNotAcquireLease

))

{

    CORBA::String_var new_lease = leaseObj->manufacture_lease_id();

 

    // inform the plugin that it should monitor the lifecycle

    // and status of this new lease

    //

    leaseObj->lease_acquired(new_lease);

    cout << "Acquire lease was called! Returning: " << new_lease << endl;

    return new_lease._retn();

}

 

 

// lease_expired() -- Implements IDL operation

// "IT_Leasing::LeaseCallback::lease_expired".

//

// This method is called by the plugin when a particular

// lease has expired, giving the server a chance to evict

// the resources relevant to that lease. Here, we delegate

// that responsibility to the PersonFactory. Any servants

// relevant to that lease are subsequently evicted.

void

LeaseCallbackImpl::lease_expired(   

    const char* lease_id

圆角矩形标注: 在一个租借过期时,插件会调用lease_expired。
本例描述了当使用某个Person的客户端消失时,通过调用Person Factory的owner_has_gone_away()操作来清除属于该客户端的Person对象
) IT_THROW_DECL((

    CORBA::SystemException

))

{

 

 

    m_factory->owner_has_gone_away(lease_id);

}

 

 

// lease_released() -- Implements IDL operation

// "IT_Leasing::LeaseCallback::lease_released".

//

// This method is called by leasing clients when they shut

// down gracefully.

圆角矩形标注: 在客户端正正常关闭时,lease_released被调用,它实现的功能和lease_expired一样。但它们之间有最要的一点区别:当lease_released被调用时,它应该调用ServerLeaseAgent::lease_released(),以便能通知插件停止管理相应的租借。void

LeaseCallbackImpl::lease_released(   

    const char* lease_id

) IT_THROW_DECL((

    CORBA::SystemException

))

{

    cout << "The lease: " << lease_id;

    cout << " was explicitly released by its client." << endl;

 

    // Tell the plugin to stop monitoring the lifecycle

    // of this lease

    leaseObj->lease_released(lease_id);

    m_factory->owner_has_gone_away(lease_id);

}

 

 

// renew_lease() -- Implements IDL operation

// "IT_Leasing::LeaseCallback::renew_lease".

//

void

圆角矩形标注: Renew_lease被客户端租借插件定时调用以实现续借功能。你可以将本操件的功能置为空。但也以加入自己的代码以实现一些操作,比如日志登记功能。LeaseCallbackImpl::renew_lease(

    const char* lease_id

) IT_THROW_DECL((

    CORBA::SystemException,

    IT_Leasing::LeaseHasExpired

))

{

    cout << "renew_lease() has been called for lease: " << lease_id << endl;

     

    // Nothing to do, since the plugin has already intercepted

    // this request and knows that the lease has been renewed.

}

 

5.3 IT_Leasing::Current 跟踪客户端Session

5.3.1 概述

      服务端通过IT_Leasing::Current接口跟踪每个客户端和它关联的资源。在本例中,资源就是Person对象(它是由LeaseTest::PersonFacrory接口创建)。每一个新创建的Person对象总是的当前客户端关联。

      本例中,当前客户端session由当前的租借ID来标识。

5.3.2 代码实现(PersonFactoryImpl类)

// create_person() -- Implements IDL operation

//"LeaseTest::PersonFactory::create_person".

LeaseTest::Person_ptr

PersonFactoryImpl::create_person(

    const char* name

) IT_THROW_DECL((

    CORBA::SystemException,

    LeaseTest::PersonAlreadyExists

))

{   

    LeaseTest::Person_var result = LeaseTest::Person::_nil();

    try

    {

        cout << "LeaseTest::create_person(\"" << name << "\");" << endl;

 

        /////////////////////////////////////////////////////////////////////////

        // Now we attempt to determine if this method was called by

        // a leasing client. If so, the appropriate LeaseID for this

        // invocation will be available in the LeaseCurrent object.

        //

        CORBA::String_var owner = CORBA::string_dup("<unknown>");  

圆角矩形标注: 给出缺省的Lease ID <unknown>,适用于非租借客户端 


        try

        {

            CORBA::Object_var objref =

                global_orb->resolve_initial_references("LeaseCurrent");

            if (!CORBA::is_nil(objref))

圆角矩形标注: 得到LeaseCurrent的初始化引用            {

                IT_Leasing::Current_var current =

                    IT_Leasing::Current::_narrow(objref);

                if (!CORBA::is_nil(current))

                {                                    

                    owner = current->get_lease_id();

圆角矩形标注: 得到LeaseCurrent的引用后,调用它的get_lease_id操作得到lease ID返回值,String类型                }

            }

        }

        catch (...)

        {

            cerr << "An unknown exception occurred while getting ServiceContext data." << endl;

        }

 

        /////////////////////////////////////////////////////////////////////////   

        // Create a new Person servant and activate it

        //

        PersonImpl*                   newPersonServant;

        PortableServer::ObjectId_var  oid;

        CORBA::Object_var           tmp_ref;

 

        {

            IT_Locker <IT_Mutex> lock(m_mutex);

 

            // check for Person existence within this process

            if (person_is_alive(name))

            {

                cout << "Person already exists!" << endl;       

                throw LeaseTest::PersonAlreadyExists();

            }

            else

            {   

                // Person does not exist, so it is created and

                // stored with the others, indexed by its name

                //

                newPersonServant = new PersonImpl(

圆角矩形标注: 新建一个Person对象给特定的客户端请求                    name, owner

                );

 

                oid = m_poa->activate_object(newPersonServant);

                tmp_ref = m_poa->id_to_reference(oid);

                result = LeaseTest::Person::_narrow(tmp_ref);

圆角矩形标注: 激活新建的Person对象                assert(!CORBA::is_nil(result));           

 

                // store the new servant with the others

                //

                IT_String temp_string(name);

                m_People[temp_string] = newPersonServant;

圆角矩形标注: Person Factory将新建的Person对象存放在它内部的Person对象表中,对象表为IONA私有IT_StringMap类型            }   

        }

 

        dump_people_to_screen();

    }

    catch (...)

    {

        cerr << "Unknown exception within create_person()" << endl;

    }

圆角矩形标注: 返回Person对象引用 


    return result._retn();

}

 

圆角矩形标注: Owner_has_gone_away()被 LeaseCallback::lease_expired 或 LeaseCallback::lease_released调用,用于清除与客户端关联的资源(Person对象)。客户端会话由唯一owner标识。Owner即是客户端租借ID 

 


void

PersonFactoryImpl::owner_has_gone_away(

    const char* owner

)

{

 

    // Iterate through the people map and evict any people

    // who were created by 'owner'.

    //

    IT_Locker <IT_Mutex> lock(m_mutex);

    IT_String current_name;

圆角矩形标注: 在PersonFactory的内部Person对象表(m_People)中挨个寻找current_name和owner相等的元素    People::iterator theIter = m_People.begin();

    while (theIter != m_People.end())

    {

        current_name = (*theIter).second->owner();

        if (current_name == owner)

        {

圆角矩形标注: 找到后,调用POA方法deactivate_object()将对应的Person伺服对象置于非激活状态            // deactivate the servant before deleting it

            //

            PortableServer::ObjectId_var oid =

                m_poa->servant_to_id((*theIter).second);

 

            // deactivate the servant with the corresponding id on the POA

            m_poa->deactivate_object(oid);

 

            cout << "Deleting: " << (*theIter).first << endl;

            delete (*theIter).second;

            m_People.erase(theIter);

            theIter = m_People.begin(); // iterator is invalidated

            continue;

        }              

        theIter++;

    }

    dump_people_to_screen();

}

 

 

5.4 发布租借

5.4.1 前期准备

      由于LeaseCallback对象引用需要发布到Naming Service中。因此,在运行前需要正确配置Orbix2kLocator,Activator Daemon以及Naming Serices并运行。

5.4.2 在哪儿发布

      租借的发布做为一个初始功能,它应该放在main()中。并且在ORB::run()ORB::perform_work()前执行完成。

5.4.3 代码实现(main()函数)

//C++

int

main(int argc,char **argc)

{

PersonFactoryImpl* the_PersonFactoryServant;

LeaseCallbackImpl* the_LeaseCallbackServant;

 

the_LeaseCallbackServant = new LeaseCallbackImpl(the_PersonFactoryServant);

 

    oid = my_poa->activate_object(the_LeaseCallbackServant);

    tmp_ref = my_poa->id_to_reference(oid);

 

IT_Leasing::LeaseCallback_var the_LeaseCallbackObject =  IT_Leasing::LeaseCallback::_narrow(tmp_ref);

圆角矩形标注: 得到IT_ServerLeaseAgent
的初始化引用
 


    // Contact the lease plugin and advertise a lease

    try

    {

   tmp_ref = global_orb->resolve_initial_references("IT_ServerLeaseAgent");

        leaseObj = IT_Leasing::ServerLeaseAgent::_narrow(tmp_ref);

 

        leaseObj->advertise_lease(

圆角矩形标注: 发布LeaseCallbackObject对象            the_LeaseCallbackObject

        );

 

    }        

    catch (CORBA::Exception &e)

    {

        cerr << "Error advertising lease: " << e << endl;              

        cerr << "Continuing without leasing." << endl;

}

}

 

5.5 配置服务端插件

5.5.1 概述

      服务端配置变量用于初始化服务端插件并定制其行为。一部分变量被加进对象引用(IOR)中用于与客户端通讯。

5.5.2 配置变量

配置变量

功能

binding:server_binding_list

用于指定将LEASE 拦截器加载到服务端

plugins:lease:lease_name_to_advertise

绑定到Naming Service上的LeaseCallback对象名。该名字在每个服务程序中必须唯一

plugins:lease:lease_ping_time

指定客户端续借的时间间隔

plugins:lease:lease_reap_time

如果客户端在lease_reap_time时间间隔内没有续借,服务程序将会释放与对应客户端关联的资源

5.5.3 本例子的完全配置(itadmin show session_management)

session_management

{

binding:client_binding_list = POA_Coloc, LEASE+GIOP+IIOP, GIOP+IIOP

binding:server_binding_list = LEASE,

plugins:lease:shlib_name = "it_lease"

plugins:lease:allow_advertisement_overwrites = true

event_log:filters = IT_LEASE=*

orb_plugins = local_log_stream, lease, iiop_profile, giop, iiop

 

server1

{

plugins:lease:lease_ping_time = 10000

plugins:lease:lease_reap_time = 20000

plugins:lease:lease_name_to_advertise = "PersonFactorySrv1"

}

 

server2

{

plugins:lease:lease_ping_time = 20000

plugins:lease:lease_reap_time = 40000

plugins:lease:lease_name_to_advertise = "PersonFactorySrv2"

}

}

 

6.    参考资料

1. IONA Technologies corporation, IONA Orbix 2000 Session Management Guide, C++ Edition

2. 王维汉,邸瑞华著,通用CORBA服务器资源回收框架研究

3. Prashant Jain, Evictor