Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Advanced CORBA Programming wit C++ - M. Henning, S. Vinoski.pdf
Скачиваний:
65
Добавлен:
24.05.2014
Размер:
5 Mб
Скачать

IT-SC book: Advanced CORBA® Programming with C++

Chapter 20. The OMG Event Service

20.1 Chapter Overview

This chapter describes the OMG Event Service, which allows applications to use a decoupled communications model rather than strict client-to-server synchronous request invocations. After the introduction, we explain in Section 20.3 why using the Event Service can be beneficial to applications by discussing the pros and cons of distributed callbacks. Section 20.4 defines the event delivery models that event-based applications can employ. Section 20.5 shows the IDL interfaces supplied by the Event Service, and Section 20.6 provides examples of how to implement the event delivery models. Finally, Sections 20.7 and 20.8 discuss how to choose the best event model for your application and describe some of the limitations of the Event Service.

20.2 Introduction

All the examples in the previous chapters are based on synchronous request invocations. With synchronous requests, a client actively invokes requests on passive servers; after sending a request, the client blocks waiting for the response. Clients are aware of the destinations of requests because they hold object references to the target objects, and each request has a single destination denoted by the object reference used to invoke it. If the target object no longer exists or for some reason is unreachable, the invoking client receives an exception.

Many distributed applications find the synchronous request invocation model too restrictive despite its obvious utility. These applications generally require a means of decoupling the suppliers of information from the consumers interested in it. For example, in our climate control system we might want to have the thermometers send alarm messages if the temperature falls below or rises above a specified range, or we might want to be notified if a thermostat is set too high or too low. Making the Thermometer and Thermostat objects responsible for disseminating these messages to all interested parties unnecessarily complicates their implementations, and it scales poorly as the number of interested consumers rises.

The OMG Event Service provides support for decoupled communications between objects. It allows suppliers to send messages to one or more consumers with a single call. In fact, suppliers using an implementation of the Event Service need not be aware of any of the consumers of its messages; the Event Service acts as a mediator that decouples suppliers from consumers. An Event Service implementation also shields suppliers from exceptions resulting from any of the consumer objects being unreachable or poorly behaved.

20.3 Distributed Callbacks

793

IT-SC book: Advanced CORBA® Programming with C++

Before we present details of the OMG Event Service, let us first explore the concept of distributed callbacks as a means of showing why an Event Service can be useful. To properly define a distributed callback, we must first clarify the definitions of client and server. For synchronous requests, the client is the one that invokes the request, and the server is the one that receives it and responds to it. Thus, the terms client and server are meaningful only with respect to a single request. The client of one request may be the server for another, and it is not uncommon for a single application to fulfill both roles simultaneously.

A distributed callback requires something of a role reversal because essentially it requires a server to call back to a client. The usual flow of events is as follows.

A client invokes a request on a server, passing it an object reference for an object in the client application. These invocations are often made using oneway semantics (see Section 4.12) with the intent of preventing the client from blocking waiting for the response.

The server receives the request and performs the required service.

To notify the client of details concerning the original request, the server calls back to the client by invoking an operation on the object reference that was passed with the original request.

The client object receives the callback.

The information sent in a callback depends on the application. For example, a server that performs long-running calculations might call back to the client to inform it of progress during the calculation as well as to deliver the results after it finishes the calculation.

As this series of steps shows, applications that participate in distributed call-backs act as both clients and servers. Because it is unusual for a CORBA application to be either a pure client or a pure server, CORBA systems are usually categorized as peer-to-peer systems rather than as client-server systems.

20.3.1 Callback Example

Assume that we want to add a graphical monitoring application to our climate control system. The application allows the operator to select devices and monitor them for changes in temperature (for thermometers) and for changes to their settings (for thermostats).

One way to implement the monitoring application would be to use polling to check device status. Whenever we needed to know the settings of a device or the temperature it is sensing, our application could simply invoke an operation on the target to obtain the desired information. Although simple, this approach suffers from several drawbacks.

794

IT-SC book: Advanced CORBA® Programming with C++

If our monitoring application is multithreaded, it is capable of sending many polling requests in a short time. It is also likely that multiple instances of the monitoring application are being run at the same time. The combined polling requests from all monitoring instances may cause server saturation depending on how the server is implemented. For example, if the server handles each request in a separate thread, an excessive amount of CPU and memory resources may be consumed. Furthermore, the throughput of the CCS server might be limited by the bandwidth of the ICP device control network that it uses to communicate with the thermometers and thermostats.

Even if our server has no problem handling all the polling requests sent to it, overall system performance may still suffer because of heavy network utilization caused by polling. If our monitoring applications flood the network with polling requests, the throughput and response times of all applications using the network suffer due to increased traffic. If network congestion becomes extreme, the entire CCS system could grind to a halt.

Presumably, our monitoring application is multithreaded to allow it to perform other useful work, such as updating its graphical interface, while it waits for polling results. Unfortunately, multithreading makes our application more complex. This is especially true given that the multithreading is required directly in the application code—so that it can perform polling requests in separate threads—rather than being hidden in the underlying ORB or in the graphical interface libraries.

By employing distributed callbacks, we can solve some of the problems caused by polling. Our monitoring application can register an object reference that the Thermometer and Thermostat objects it is interested in can use to call it back. When the object detects the desired temperature or settings, it invokes the callback object reference to inform the monitor.

Using distributed callbacks in this manner solves the problems with our original polling solutions. It solves the server and network saturation problems by avoiding the need for polling. It also takes care of the multithreading application complexity problem because the monitor no longer needs to be multithreaded in order to obtain reasonable performance.

20.3.2 Problems with Callbacks

Although they solve our problems with polling, distributed callbacks themselves suffer from a number of serious problems. These have to do with callback object reference registration and with notification scalability.

Object Reference Equality

Assume that the monitoring application registers a callback by passing an object reference along with information that indicates the circumstances under which a callback

795

IT-SC book: Advanced CORBA® Programming with C++

should occur. The following example shows some hypothetical additions to the IDL for our CCS module to support callbacks.

module CCS {

struct CallbackInfo {

// contents omitted for this example

};

 

 

interface Callback

 

void notify(in any data);

 

};

 

 

interface Thermometer {

 

void

register_callback(

cb,

 

in Callback

 

in CallbackInfo why

 

);

 

exception NotRegistered {};

void unregister_callback(in Callback cb) raises(NotRegistered);

// ...

};

};

To create a callback object, the monitoring application implements the Callback interface. It then registers a reference for it by invoking register_callback on a Thermometer object, passing it a struct that indicates the conditions under which it wants to be called back. When it wants to unregister the callback object, the application passes the registered object reference to unregister_callback. Unfortunately, with this design the monitoring application will have trouble unregistering its callback objects because CORBA does not support comparison of object references as a way of determining whether they unequivocally refer to the same object.

CORBA provides the is_equivalent operation (in the CORBA::Object interface), which allows applications to ask whether two object references refer to the same object. Because there are cases when determining equivalence is too expensive or is not possible—such as when one object reference is indirected through a proxy in a firewall— the CORBA specification does not require ORBs to perform this operation at all costs. Rather, it allows is_equivalent to return false if the ORB for some reason cannot determine equivalency, even if the two object references in question actually refer to the same object. In other words, a true return value means that the object references refer to the same object, but a false return means either that they do not or that the ORB was unable to make the determination.

Because of the weak semantics CORBA provides for is_equivalent, applications should not count on it as a tool for determining object identity and object reference equality. Instead, applications must either avoid designs that require object reference

796

IT-SC book: Advanced CORBA® Programming with C++

comparison or support operations in their interfaces that allow object identity to be determined.

We can eliminate the need to compare object references in our callback example by returning an object from register_callback that allows the application to perform an unregister operation.

module CCS {

struct CallbackInfo {

// contents omitted for this example

};

interface Callback {

void notify(in any data);

};

 

interface CBRegistration {

 

void unregister();

 

};

 

interface Thermometer {

 

CBRegistration register_callback(

cb,

in Callback

in CallbackInfo why

);

// ...

};

};

The CallbackInfo structure and the Callback interfaces are the same as before, but we have added a CBRegistration interface and changed the return type of the register_callback operation. Now, a CBRegistration object is created as the result of register_callback. When the application wants to unregister its callback, it invokes unregister on the CBRegistration returned from register_callback. The CBRegistration object is created to represent only a single callback registration, so it leaves no ambiguity as to which callback object should be unregistered. The unregister operation also implicitly destroys the callback registration, so invoking unregister after the callback has already been unregistered will result in an OBJECT_NOT_EXIST exception. An additional benefit of this approach is that it does not open a hole for applications to cancel callbacks for other applications, something that the original solution allowed.

Callback Persistence

To maintain server activation transparency for the monitoring applications, we must modify the CCS server to save callback information in persistent storage. Otherwise, if the CCS server application stopped because of inactivity or for maintenance or if it crashed because of an application defect, the monitoring applications would not know that their callback registrations had suddenly become invalid.

797

IT-SC book: Advanced CORBA® Programming with C++

This requirement to save callback information in persistent storage may seem innocuous, but it is not. Depending on the number of callback registrations that must be persistently stored, we might be able to use a simple text file for storage, or we might need a fullblown relational or object database. Either way, this new requirement adds a significant complication to the CCS server application.

Callback Failure

When an event occurs, the CCS server must deliver a notification to each registered callback. Depending on the number of registered callbacks and whether the monitoring applications are careful to unregister callbacks when they are no longer needed, it is likely that not all callback objects will still exist and be reachable when a callback must be delivered. If a particular callback results in an OBJECT_NOT_EXIST exception, the CCS server must know to unregister that call-back object. If a callback results in a TRANSIENT exception with a completion status of COMPLETED_NO, the CCS server can retry the callback. However, the server either must arbitrarily choose a number of retries before it gives up or must allow that number to be configured somehow (either administratively, or programmatically at callback registration time). Properly planning for and handling these kinds of errors can be difficult.

Scalability

To properly support callbacks, the CCS server must be able to deliver them in a timely fashion. This may or may not be difficult, depending on the total number of callbacks registered and on the types of calculations required to determine which ones must be notified for each event. We can perform as much precalculation as possible of the callback information that is passed with each callback registration, and this could reduce the amount that must be performed when a given temperature is reached or when a certain thermostat setting is detected. Depending on the needs of each application requiring a callback, both the amount of calculation per event and the number of registered callbacks help determine whether we will be able to meet the desired qualities of service.

If any of the clients receiving callbacks is itself handling many other requests or is bogged down performing its own calculations, it may be slow to receive and process any callbacks from the server. This in turn can cause the CCS server to slow down or even hang and thus can affect the rate of delivery for all callback objects known to the server.

In general, it is difficult to write callback-based applications that scale well. If there are enough callbacks to deliver, the work the server application must do to deliver them eventually outweighs the processing it was originally written to perform. If that were not enough, dealing with uncooperative clients can block or even hang the server.

Coupling

Because the server must have an object reference for each callback object, both client and server are tightly coupled because of knowledge of the callback interface. In our CCS callback example, the clients, the servers, and the Callback interface itself all know

798