![]()
|
2. Pyro Concepts
IntroductionFor a good understanding of Pyro it is necessary to know the different elements in a Pyro system. This chapter summarizes them. Keep in mind that in a distributed object system, the client/server style is difficult to see. Most of the time all parts of the system switch roles, one moment it's a client calling a remote object, the other moment it is itself an object that is called from other parts of the system. For a good understanding though, it is important to see that during a single method call, there are always two distinct parts of the system: the client part that initiates the method call, and the server part that accepts and executes the call. To be precise, there are actually three parts: between the client and the server is the distributed object middleware, in this case: Pyro.Another issue is that a client can - of course! - use more than one remote object, but also that a single server can have more than one object implementation. Thus, single objects in their own right are neither a client nor a server. I'll define the executable parts of the system that contain the objects as clients and servers (depending on their actual role). For simple Pyro applications, usually the different Python modules clearly show the different parts of the system, and they are the clients and servers I'm talking about here. If you want a technical and more in-depth description of the material presented here, read the chapter on Pyro's implementation, or just browse the source code. Pyro shell scriptsPyro provides several tools to help you during development of a Pyro application. The Pyro Name Server is also started and controlled by two of these scripts. See the chapter on the Name Server for more information about them.Client programThis is the part of your system that sends requests to the server program, to perform certain actions. It's the code that actually uses the remote objects by calling their methods.Pyro client programs look suspiciously like normal Python programs. But that's the whole point of Pyro: it enables you to build distributed object systems with minimal effort. It makes the use of remote objects (almost) transparent. Client code has to perform some initialization and setup steps. And because we're talking remote objects here, they cannot create object instances in the usual way. They have to use a two-step mechanism:
Server programThe server is the home of the objects that are accessed remotely. Every object instance has to be part of a Python program, so this is it. The server has to do several things:
Remote objectAside from the restrictions given in the chapter on Rules and Limitations, a Pyro object is just a regular Python object. The object doesn't know and doesn't have to know it's part of a Pyro server, and called remotely.ProxyA proxy is a special kind of object that acts as if it were the actual -remote- object. Pyro clients have to use proxies to forward method calls to the remote objects, and pass results back to the calling code. Pyro knows two kinds of proxies, and you are free to chose from them:
Pyro daemonIt's getting more technical now. Each server has to have a way of getting remote method calls and dispatching them to the required objects. For this task Pyro provides a Daemon. A server just creates one of those and tells it to sit waiting for incoming requests. The Daemon takes care of everything from that moment on. Every server has one Daemon that knows about all the Pyro objects the server provides.Location discovery and namingOne of the most useful services a distributed object system can have is a Name Server. Such a service is a central database that knows the names and corresponding locations of all objects in the system.Pyro has a Name Server that performs this task very well. Pyro Servers register their objects and location with the Name Server. Pyro Clients query the server for location identifiers. They do this by providing (human-readable) object names. They get a Pyro Universal Resource Identifier (URI) in return (which is not intended for humans. However, as it is, the current PYRO URI's look just like WWW URL's and can be read quite nicely). You might be wondering: how can a Pyro client find the Name Server itself?! Good question. There are three possibilities:
Notice: the Pyro.xxxxx namespace is reserved for Pyro itself (for instance, the Name Server is called Pyro.NameServer ). Don't use it.
There are two other ways to connect to a certain object you know the name of.
These are the Communication protocolThe communication between a client and a server basically consists of two kinds of messages:
pickle facility to create these messages. The transport over the network is done using TCP/IP.
The way I designed Pyro should make it easy to use other protocols, but for now, only the PYRO protocol is implemented.
A different protocol is used for the initial communication with the Name Server. Pyro uses UDP broadcasting over IP to discover the Name Server in the local subnet, and asks it to report back. Once Pyro knows the location ID of the Name Server, it switches to the PYRO protocol, because the Name Server is just another Pyro object! ExceptionsWhat happens when an error occurs in your server? Pyro can't help you when your server crashes. But it does catch the Python exceptions that occur in your remote objects. They are sent back to the calling code and raised again, just as if they occurred locally. The occurrence is logged in the server log. Thus, Pyro makes no distinction between user generated exceptions (in the remote object) or exceptions that occur because of runtime errors, such as divide by zero.
The client (as it should!) has no clean way of telling whether the exception was raised locally or in the remote object. Any Pyro induced errors will be signaled by a There is a slight problem with this scheme: traceback objects and stack traces are virtually meaningless if the exception occurred in the remote object. However, Pyro comes to the rescue: a stack trace of the remote exception is passed over the network to the calling object and can be printed there. So it is possible to see what remote code caused the problem. LoggingA good trace facility is paramount in a complex system. Therefore Pyro provides a simple to use logger. It writes messages to a configurable logfile, annotated with a timestamp. It distinguishes errors, warnings and regular notices. Pyro uses it depending on the tracelevel you configured.You can use the logging facility in your own code too. There is a special user version of the logger that operates independently of the Pyro system logger. You can configure the trace level and logfile location uniquely for both loggers. By default, logging is turned off completely. You can simply turn it on during the course of your program's execution, or beforehand by setting a simple Pyro configuration option.
|