clients and can be used for load distribution and similar services. KEY WORDS Dispatcher, Load Distribution, CORBA. 1 Introduction. Currently, several basic ...
TECHNIQUES FOR THE IMPLEMENTATION OF A GENERIC REQUEST DISPATCHER FOR CORBA-BASED APPLICATIONS Markus Aleksy, Axel Korthaus Department of Information Systems University of Mannheim Schloss (L5,6) D-68131 Mannheim, Germany {aleksy|korthaus}@wifo3.uni-mannheim.de
ABSTRACT In distributed software application systems, dispatcher components which act as an intermediate layer between clients and servers are often used to solve several problems such as location transparency of servers, scalability, and load-balancing. In this paper we analyze different approaches to the design and implementation of dispatcher components in a CORBA-based environment. Since our design is focused on providing a generic and portable solution, we restrict ourselves exclusively to design and implementation elements provided by the CORBA standard. We point out which alternative solutions can be derived from this set of elements and what their respective advantages and disadvantages are. As a result, we show how fully generic and portable CORBA-based dispatcher components can be developed which are completely transparent to clients and can be used for load distribution and similar services.
KEY WORDS Dispatcher, Load Distribution, CORBA 1
Introduction
Currently, several basic technologies are available for the development of distributed applications. Among these is the Common Object Request Broker Architecture (CORBA) standard [1] which has become very popular and widespread in the area of object-oriented and distributed systems. Not only does it provide independence from the computer architectures, the operating systems and the programming languages being used, but it also guarantees the developer freedom of choice between different vendors of Object Request Broker (ORB) pro-ducts. The last benefit was achieved in CORBA version 2.0 by introducing unique object references, the so-called Interoperable Object References (IORs), and a standardized transmission protocol for the communication between ORBs, the so-called Internet Inter-ORB Protocol (IIOP). Because of its flexibility, the CORBA standard does not restrict programmers to implement simple client-server applications, but allows modern n-tier architectures as well. In that context, special aspects
of distributed n-tier systems, such as load distribution, scalability, location transparency etc. can be solved by introducing dedicated dispatcher components according to the Client-Dispatcher-Server design pattern [2] (figure 1). The application of this pattern leads to a decoupling of client-server applications by introducing an intermediate layer which can serve to accomplish a number of different tasks. In the case of load distribution/balancing, the Dispatcher component is responsible for distribution and assignment of client requests to different business logic servers. In this paper, we will discuss some techniques for the implementation of a rather generic and transparent variant of the dispatcher component described in the pattern mentioned above. Because of its popularity, we chose the CORBA middleware as our implementation environment and we point out those parts of the CORBA standard that are especially important for the implementation of a generic dispatcher component.
2
Design Considerations
For the examination of different approaches to the implementation of the dispatcher we defined the following design goals: •
Generic behavior The provided solution should be applicable to a multitude of applications. Prerequisite to this is that it can abstract from specific kinds of clients and servers to be fully generic.
•
Portability The solution must not use or require product- or platform-specific features.
•
Access transparency to client applications It should be possible to leave preexisting client applications unchanged for use with the dispatcher. The dispatcher has to be transparent to the client.
Client
requests service
Server
doTask() sendRequest()
requests connection
returns results
acceptConnection() runService() receiveRequest() registers accepts link
Dispatcher locationMap registerService() unregisterServer() establish locateServer() connection erstablishChannel() getChannel()
Figure 1: Client-Dispatcher-Server design pattern [2]
3
Solution Alternatives
The easiest way to realize a dispatcher component for a CORBA-based application is to define a dedicated IDL interface for this component. If this approach is chosen, the Client-Dispatcher-Server design pattern can be implemented straightforwardly. The dispatcher’s interface has to provide suitable operations and should be declared abstract, so that application specific dispatcher components can have their own interfaces defined by extending this superinterface and supplementing additional, application-specific elements. However, this approach has considerable disadvantages. Especially our goal of providing a generic solution cannot be achieved with this IDL-based solution, and the definition of an IDL interface for the new dispatcher component in the system requires client applications to be adapted, so that the goal of access transparency to the client would also be missed. If the developer does not want to put up with these drawbacks, different solutions have to be found to provide a generic and dynamic dispatcher variant. This kind of solution alternatives could be based on using the functionality of the Portable Object Adapter (POA) or the Dynamic Skeleton Interface (DSI), respectively, to distribute client requests to different servants. Before we will elaborate on the details of our POA- and DSI-based approaches, we will briefly outline the basic functionality of the POA in the following section to provide some background information. 3.1 The Portable Object Adapter With the adoption of the POA specification, the Object Management Group (OMG) has for the first time clarified some confusions about basic CORBA concepts. It was defined that a “CORBA object” is a virtual entity providing an interface and its own identity. A “servant”, on the other hand, is a programming language entity that exists in the
context of a server and implements a CORBA object. The association of a CORBA object with an active servant is called “incarnation”. Putting a CORBA object in a state in which it can handle requests is called “activation”. Thus, a CORBA object can of course only be activated if it has an incarnation. The activation of an object can be achieved in three different ways: •
explicitly by calling operation activate_ object(),
•
implicitly after specifying the IMPLICIT_ ACTIVATION policy for the POA, or
•
by a special Servant Manager (POA policy USE_SERVANT_MANAGER).
The last alternative is particularly interesting for distributing client requests which is the main task of the dispatcher we are going to develop. It gives an application the opportunity to register a Servant Manager callback operation with the POA which will always be invoked if a request directed to an object that is not activated arrives. Two different types of servant managers have to be distinguished: Servant Activator and Servant Locator. While a Servant Activator is used to permanently activate an object, a Servant Locator activates an object for the handling of one single request only. Therefore, the Servant Locator will be involved in each single operation invocation, which is very useful for our purposes here, as we will explain shortly. The interface of a Servant Locator declares two operations called preinvoke and postinvoke. Operation preinvoke is called each time the POA receives a request directed to an object that is not activated. A typical management use of a Servant Locator is to determine the current system time in the body of the preinvoke and postinvoke methods and to calculate the difference in order to find
out how long it took the servant to process the request. In that way, the application layer components do not have to be changed and do not notice that they are monitored. Figure 2 shows a simplified overview of the POA architecture. 3.2
Implementation of a “Per-Session-Dispatcher”
If server components are to be used which manage a state that persists between different requests issued by a specific client, we will have to implement a dispatcher component that reroutes the first client request to a specific servant in a way that all subsequent client requests will be directly routed to that servant without the involvement of the dispatcher. If such a kind of “Per-Session-Dispatcher” is to be implemented, the best choice will be to utilize the POA functionality of the dispatcher component. With respect to the client, there is no need to introduce any new IDL interfaces for the dispatcher, so that from the client’s point of view the system remains unchanged. Instead, the built-in lowlevel POA functionality of the dispatcher component can be exploited. The basic idea of this approach is to create a POA with certain specific policies, namely the NON_RETAIN and the USE_SERVANT_MANAGER policy, for the dispatcher. This kind of POA does not manage an Active Object Map, but instead calls its associated Servant Manager (i.e., Servant Locator, to be more precise) each time a request is received from a client. Therefore, operation preinvoke of its Servant Locator will be invoked. The trick is now that it is sufficient to throw a ForwardRequest exception and provide it with the IOR of an appropriate target servant as an argument. Thus, the client will be informed of the actual servant’s IOR by
means of standard exception processing. This rerouting of the request in the form of a ForwardRequest exception has a permanent effect, i.e., all subsequent requests from this client in the current session will be forwarded to the new IOR stored in the exception argument without contacting the dispatcher component again. The handling of this exception on part of the client is automatically done on the ORB level. This means that it is absolutely transparent to the client programmer, and, on the application level, the client will get no hint that its request was not processed immediately but was forwarded to another servant. Therefore, this approach requires no changes in the client implementation like, for example, specific exception handling code. Since we make maximum use of built-in CORBA mechanisms, the solution is completely transparent to the client. Although operation postinvoke must of course exist in the implementation of the Service Locator, its body does not have to carry any code, because the operation will never be invoked in this approach. The reason is that after throwing the ForwardRequest exception in operation preinvoke, the dispatcher’s work is done and no more processing will take place on its part. The following Java code snippet shows parts of the implementation of a “Per-Session-Dispatcher”: public class Dispatcher extends PortableServer.ServantLocatorPOA { private List objlist = new ArrayList(); private int pos = 0;
POA A
Root POA
Default servant
Object Id
Active Object Map Object Id Object Id ...
User-supplied servant
POA Manager
User-supplied servant User-supplied servant User-supplied servant
User-supplied Servant Manager POA B Servant Manager Object Id Object Id ...
Object reference Servant pointer
User-supplied servant
User-supplied servant
POA C Object Id
Figure 2: POA Architecture
User-supplied servant
public POA create_dispatcher_poa( POA parentPOA) { POA result = null; try { Policy[] policies = { POA.create_servant_retention_ policy( ServantRetentionPolicyValue. NON_RETAIN), parentPOA.create_request_ processing_policy( RequestProcessingPolicyValue. USE_SERVANT_MANAGER) }; result = parentPOA.create_POA( "DispatcherPOA", parentPOA.the_POAManager(), policies); } catch(PortableServer.POAPackage. InvalidPolicy ex) { } catch(PortableServer.POAPackage. AdapterAlreadyExists ex) { }
again be found within the POA. With the help of operation create_reference (in CORBA::RepositoryId intf) raises (WrongPolicy); it is possible to create references of any type required and to pass them to any client. Like this, a generic dispatcher can create several different IORs with different type information and serve different kinds of clients at the same time. In order to receive information about the available business logic servants and their type information, the dispatcher must provide appropriate functionality for the registration and unregistration of such servants. The easiest way to accomplish this is to define an ordinary IDL interface for the dispatcher which has to be used by the business logic servants (see example below), or to implement this functionality using the DSI. interface Dispatcher { void register( in Object ior, in string type); void unregister( in Object ior, in string type); };
return result; } public synchronized Servant preinvoke(byte[] oid, POA adapter, String operation, CookieHolder the_cookie) throws ForwardRequest { org.omg.CORBA.Object obj = (org.omg.CORBA.Object) objlist.get(pos); if(pos==objlist.size()-1) pos=0; else pos++; throw new ForwardRequest(obj); } public void postinvoke(byte[] oid, POA adapter, String operation, Object the_cookie, Servant the_servant) { } // further methods }
Since it was our goal to design a generic solution, we had to find a way to avoid an approach that, from the client’s point of view, depends on creating concrete CORBA objects. The question was now, how a client application can be deluded by a dispatcher component that doesn’t instantiate CORBA objects at all. The client usually looks up the IOR of a servant of its desired server object type using the Naming Service [3], Trading Service [4], file system etc. In order to intercept the client’s requests, the generic dispatcher has to pretend to be the right kind of object and has to make sure that the client is provided with its own IOR instead of an actual servant’s IOR. The solution to these problems can
The use of supertype Object as parameter type for the registration of server components enables the registration of any type of server with the dispatcher component. Parameter type of type string serves the dispatcher as an aid for the creation of respective “proxy” references that are used to delude the client and to pretend that the dispatcher is an incarnation of the required type of business logic server. Because of the different views on the dispatcher – the chameleon-like appearance to clients and the “ordinary” appearance to servers – it is necessary to create two different POAs for the dispatcher. As already mentioned, the part of the dispatcher which is relevant to clients has to be realized by a POA with the policies NON_RETAIN and USE_SERVANT_ MANAGER. The part of the dispatcher relevant to servers for registration and unregistration, on the other hand, has to be supported by a normal POA which actually incarnates and activates the Dispatcher servant implementing the IDL interface shown above. This task can be performed by the “root POA”, for example, which is commonly used in most server-side CORBA applications. 3.3
Implementation of a “Per-RequestDispatcher” Should a “Per-Session-Dispatcher” be not adequate for the problem to be solved, the already discussed features of the CORBA standard can also be used to implement a generic and portable “Per-Request-Dispatcher” which forwards each single client requests to a possibly new server. This solution excludes the management of a session state on the server’s part, because it is not guar-
anteed that subsequent calls from a client will arrive at the same servant. For the implementation of a “Per-Request-Dispatcher” the POA functionality can again be the best starting point to look at. However, a main difference with respect to the prior approach is that the Servant Locator’s operation preinvoke must not throw a ForwardRequest exception but, instead, has to forward the client request to an actual servant by calling the respective operation of the servant. As opposed to the first approach, we now have to implement operation postinvoke, because the results of the operation invocation at the servant have to be collected and passed to the client.
In [5], an approach to load balancing is described which is very interesting from the client’s point of view, because the Naming Service’s [3] functionality is extended with transparent load balancing features. When the client calls operation resolve at the extended Naming Service, the IOR of the least loaded servant registered with the required name will be returned to the client. This premises, of course, that different servants are allowed to register at the Naming Service using the same name, which, however, is not provided by the Naming Service specification and would lead to an AlreadyBound exception. Thus, this solution is not fully standardcompliant, which is a considerable disadvantage.
As we already mentioned briefly, the use of the Dynamic Skeleton Interface (DSI) can be an alternative to an implementation based on the POA functionality only. The DSI was supplemented in CORBA version 2.0. It provides a runtime mechanism for plugging in new components that do not have IDL-based compiled skeletons. The DSI provides an interface which can receive a specific category of requests. Incoming messages are analyzed with respect to their target object and the requested operation. The interface contains an invoke operation which has to be connected with the actual method call by the programmer. The DSI can handle both static and dynamic requests. From the client’s perspective, nothing has to be changed. The following Java code fragment roughly sketches a possible implementation of method invoke:
The Load Balancing Service developed in the TAO ORB project ([6], [7]) is another way of distributing operation calls to different servers in a CORBA environment. The concepts suggested by the authors were also the foundation for the first reply [8] to the OMG’s load balancing Request-for-Propsals (RFP) [9]. The load balancing service of the TAO ORB uses GIOP LOCATION_FORWARD messages for delegating requests on heavily loaded components to less claimed components.
public class Dispatcher extends PortableServer.DynamicImplementation { // ... public void invoke( ServerRequest request) { String op = request.operation(); if(op.equals("operation1")) // call operation1 else if(op.equals("operation2")) // call operation2 // ... } // further methods }
In order to implement a generic dispatcher based on DSI functionality, some special POA policies have to be used. In this context, the USE_DEFAULT_ SERVANT policy is of particular importance. Based on this policy, each incoming remote request is forwarded to the default servant registered with the POA. The default servant then decides to which business logic servant the request has to be forwarded on its part.
4
Related Work
One of the central fields of application for the approaches presented in this paper is the problem of load balancing/distribution in CORBA.
The Load Balancing Service developed at the University of Mannheim ([10], [11]) supports a multitude of static and dynamic load balancing algorithms. Analogies to the Trading Service [4] enable programmers who are already experienced in using the Trading Service to shorten their technological adjustment period. As a basic mechanism, the time needs of servants are determined by calculating the difference between the points of time measured in the preinvoke and the postinvoke method, respectively. For dynamic load balancing algorithms, this information can be used to make decisions about the actual target servant for a given request.
5
Conclusions
In this paper we have discussed different ways how to implement a reusable dispatcher component in a CORBA-based environment. While an IDL-based approach is quite easy to be realized, it has the lowest flexibility and does not provide client transparency or generic behavior. The functionality provided by the POA or the DSI, on the other hand, facilitates the development of generic components. Using the built-in features, it is relatively simple to implement flexible “PerSession-” and “Per-Request-Dispatchers”. Dispatcher components designed like that are not only generic but they are also portable and can be used together with any standard-compliant ORB product. Furthermore, access to this kind of generic dispatchers is transparent to client applications, thus avoiding any changes to existing clients, so that they can be easily reused in more sophisticated systems mediated by dispatcher components.
REFERENCES [1] OMG, The Common Object Request Broker: Architecture and Specification Revision 2.6, OMG Technical Document Number 00-12-01, 2001, ftp://ftp.omg.org/pub/
[7] O. Othman, O, C. O’Ryan, & D.C. Schmidt, The Design of an Adaptive CORBA Load Balancing Service, IEEE Distributed Systems Online, 4/01, 2001
docs/formal/00-12-01.pdf
[2] F. Buschmann, R. Meunier, H. Rohnert, P. Sommerlad, & M. Stal, A System of Patterns – Pattern-Oriented Software Architecture, John Wiley & Sons, Chichester, 1996 [3] OMG, Naming Service Specification, OMG Technical Document Number 00-06-19, 2000, ftp://ftp.omg.org/pub/ docs/formal/00-06-19.pdf
[4] OMG, Trading Object Service Specification, OMG Technical Document Number 00-06-27, 2000, ftp://ftp.omg. org/pub/docs/formal/00-06-27.pdf
[5] T. Barth, G. Flender, B. Freisleben, & F. Thilo, Load Distribution in a CORBA Environment, Proc. of the International Symposium on Distributed Objects and Applications (DOA ’99), Edinburgh, Scotland, 1999, 158-166 [6] O. Othman, C. O’Ryan, & D.C. Schmidt, An Efficient Adaptive Load Balancing Service for CORBA, IEEE Distributed Systems Online, 3/01, 2001
[8] OMG, Load Balancing and Monitoring, OMG Technical Document Number mars/2002-04-05, 2002, http://cgi.omg.org/cgi-bin/doc?mars/02-04-05
[9] OMG, Load Balancing and Monitoring for CORBAbased Applications – Request for Proposal, OMG Technical Document Number orbos/01-04-27, 2001, http://cgi.omg.org/cgi-bin/doc?orbos/01-04-27
[10] M. Hoffmann, Entwurf und Implementierung eines CORBA-basierten Load-Balancing Service, Master Thesis, Department of Information Systems III, University of Mannheim, 2000 [11] M. Aleksy, A. Korthaus, & M. Schader, Design and Implementation of a Flexible Load Balancing Service for CORBA-based Applications, Proc. of the International Conference on Parallel and Distributed Processing Techniques and Applications (PDPTA 2001), Las Vegas, Nevada, USA, CSREA, 2001, 2140-2144