1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
|
<?xml version="1.0" encoding="utf-8"?><!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'><html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Twisted Documentation: Introduction to Perspective Broker</title>
<link href="stylesheet.css" rel="stylesheet" type="text/css"/>
</head>
<body bgcolor="white">
<h1 class="title">Introduction to Perspective Broker</h1>
<div class="toc"><ol><li><a href="#auto0">Introduction</a></li><li><a href="#auto1">Object Roadmap</a></li><ul><li><a href="#auto2">Subclassing and Implementing</a></li></ul><li><a href="#auto3">Things you can Call Remotely</a></li><li><a href="#auto4">Things you can Copy Remotely</a></li></ol></div>
<div class="content">
<span/>
<h2>Introduction<a name="auto0"/></h2>
<p>Suppose you find yourself in control of both ends of the wire: you
have two programs that need to talk to each other, and you get to use any
protocol you want. If you can think of your problem in terms of objects that
need to make method calls on each other, then chances are good that you can
use twisted's Perspective Broker protocol rather than trying to shoehorn
your needs into something like HTTP, or implementing yet another RPC
mechanism<a href="#footnote-1" title="Most of Twisted is like this. Hell, most of unix is like this: if you think it would be useful, someone else has probably thought that way in the past, and acted on it, and you can take advantage of the tool they created to solve the same problem you're facing now."><super>1</super></a>.</p>
<p>The Perspective Broker system (abbreviated <q>PB</q>, spawning numerous
sandwich-related puns) is based upon a few central concepts:</p>
<ul>
<li><em>serialization</em>: taking fairly arbitrary objects and types,
turning them into a chunk of bytes, sending them over a wire, then
reconstituting them on the other end. By keeping careful track of object
ids, the serialized objects can contain references to other objects and
the remote copy will still be useful. </li>
<li><em>remote method calls</em>: doing something to a local object and
causing a method to get run on a distant one. The local object is called a
<code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.spread.pb.RemoteReference.html" title="twisted.spread.pb.RemoteReference">RemoteReference</a></code>, and you
<q>do something</q> by running its <code>.callRemote</code> method.
</li>
</ul>
<p>This document will contain several examples that will (hopefully) appear
redundant and verbose once you've figured out what's going on. To begin
with, much of the code will just be labelled <q>magic</q>: don't worry about how
these parts work yet. It will be explained more fully later.</p>
<h2>Object Roadmap<a name="auto1"/></h2>
<p>To start with, here are the major classes, interfaces, and
functions involved in PB, with links to the file where they are
defined (all of which are under twisted/, of course). Don't worry
about understanding what they all do yet: it's easier to figure them
out through their interaction than explaining them one at a time.</p>
<ul>
<li><em><code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.internet.protocol.Factory.html" title="twisted.internet.protocol.Factory">Factory</a></code></em>
: <code>internet/protocol.py</code></li>
<li><em><code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.spread.pb.PBServerFactory.html" title="twisted.spread.pb.PBServerFactory">PBServerFactory</a></code></em>
: <code>spread/pb.py</code></li>
<li><em><code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.spread.pb.Broker.html" title="twisted.spread.pb.Broker">Broker</a></code></em>
: <code>spread/pb.py</code></li>
</ul>
<p>Other classes that are involved at some point:</p>
<ul>
<li> <em><code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.spread.pb.RemoteReference.html" title="twisted.spread.pb.RemoteReference">RemoteReference</a></code></em>
: <code>spread/pb.py</code> </li>
<li> <em><code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.spread.pb.Root.html" title="twisted.spread.pb.Root">pb.Root</a></code></em>
: <code>spread/pb.py</code>, actually defined as
<code>twisted.spread.flavors.Root</code>
in <code>spread/flavors.py</code> </li>
<li> <em><code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.spread.pb.Referenceable.html" title="twisted.spread.pb.Referenceable">pb.Referenceable</a></code></em>
: <code>spread/pb.py</code>, actually defined as
<code>twisted.spread.flavors.Referenceable</code>
in <code>spread/flavors.py</code> </li>
</ul>
<p>Classes and interfaces that get involved when you start to care
about authorization and security:</p>
<ul>
<li><em><code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.cred.portal.Portal.html" title="twisted.cred.portal.Portal">Portal</a></code></em>
: <code>cred/portal.py</code></li>
<li><em><code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.cred.portal.IRealm.html" title="twisted.cred.portal.IRealm">IRealm</a></code></em>
: <code>cred/portal.py</code></li>
<li><em><code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.spread.pb.IPerspective.html" title="twisted.spread.pb.IPerspective">IPerspective</a></code></em>
: <code>spread/pb.py</code>, which you will usually be interacting
with via pb.Avatar (a basic implementor of the interface).</li>
</ul>
<h3>Subclassing and Implementing<a name="auto2"/></h3>
<p>Technically you can subclass anything you want, but technically you
could also write a whole new framework, which would just waste a lot
of time. Knowing which classes are useful to subclass or which
interfaces to implement is one of the bits of knowledge that's crucial
to using PB (and all of Twisted) successfully. Here are some hints to
get started:</p>
<ul>
<li><code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.spread.pb.Root.html" title="twisted.spread.pb.Root">pb.Root</a></code>, <code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.spread.pb.Referenceable.html" title="twisted.spread.pb.Referenceable">pb.Referenceable</a></code>: you'll
subclass these to make remotely-referenceable objects (i.e., objects
which you can call methods on remotely) using PB. You don't need to
change any of the existing behavior, just inherit all of it and add
the remotely-accessible methods that you want to export.</li>
<li><code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.spread.pb.Avatar.html" title="twisted.spread.pb.Avatar">pb.Avatar</a></code>: You'll
be subclassing this when you get into PB programming with
authorization. This is an implementor of IPerspective.</li>
<li><code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.cred.checkers.ICredentialsChecker.html" title="twisted.cred.checkers.ICredentialsChecker">ICredentialsChecker</a></code>: Implement this if
you want to authenticate your users against some sort of data store:
i.e., an LDAP database, an RDBMS, etc. There are already a few
implementations of this for various back-ends in
twisted.cred.checkers.</li>
</ul>
<h2>Things you can Call Remotely<a name="auto3"/></h2>
<p>At this writing, there are three <q>flavors</q> of objects that can
be accessed remotely through <code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.spread.pb.RemoteReference.html" title="twisted.spread.pb.RemoteReference">RemoteReference</a></code> objects. Each of these
flavors has a rule for how the <code class="python">callRemote</code>
message is transformed into a local method call on the server. In
order to use one of these <q>flavors</q>, subclass them and name your
published methods with the appropriate prefix.
<ul>
<li><code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.spread.pb.IPerspective.html" title="twisted.spread.pb.IPerspective">twisted.spread.pb.IPerspective</a></code> implementors
<p>This is the first interface we deal with. It is a <q>perspective</q>
onto your PB application. Perspectives are slightly special because
they are usually the first object that a given user can access in
your application (after they log on). A user should only receive a
reference to their <em>own</em> perspective. PB works hard to
verify, as best it can, that any method that can be called on a
perspective directly is being called on behalf of the user who is
represented by that perspective. (Services with unusual
requirements for <q>on behalf of</q>, such as simulations with the
ability to posess another player's avatar, are accomplished by
providing indirected access to another user's perspective.)
</p>
<p>Perspectives are not usually serialized as remote references, so
do not return an IPerspective-implementor directly. </p>
<p>The way most people will want to implement IPerspective is by
subclassing pb.Avatar. Remotely accessible methods on pb.Avatar
instances are named with the <code class="python">perspective_</code> prefix. </p>
</li>
<li><code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.spread.pb.Referenceable.html" title="twisted.spread.pb.Referenceable">twisted.spread.pb.Referenceable</a></code>
<p>Referenceable objects are the simplest kind of PB object. You can call
methods on them and return them from methods to provide access to other
objects' methods. </p>
<p>However, when a method is called on a Referenceable, it's not possible to
tell who called it.</p>
<p>Remotely accessible methods on Referenceables are named with the
<code class="python">remote_</code> prefix.</p>
</li>
<li><code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.spread.pb.Viewable.html" title="twisted.spread.pb.Viewable">twisted.spread.pb.Viewable</a></code>
<p>Viewable objects are remotely referenceable objects which have the
additional requirement that it must be possible to tell who is calling them.
The argument list to a Viewable's remote methods is modified in order to
include the Perspective representing the calling user.</p>
<p>Remotely accessible methods on Viewables are named with the
<code class="python">view_</code> prefix.</p>
</li>
</ul>
</p>
<h2>Things you can Copy Remotely<a name="auto4"/></h2>
<p>In addition to returning objects that you can call remote methods on, you
can return structured copies of local objects.</p>
<p>There are 2 basic flavors that allow for copying objects remotely. Again,
you can use these by subclassing them. In order to specify what state you want
to have copied when these are serialized, you can either use the Python default
<code class="python">__getstate__</code> or specialized method calls for that
flavor.</p>
<p>
<ul>
<li><code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.spread.pb.Copyable.html" title="twisted.spread.pb.Copyable">twisted.spread.pb.Copyable</a></code>
<p>This is the simpler kind of object that can be copied. Every time this
object is returned from a method or passed as an argument, it is serialized
and unserialized.</p>
<p><code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.spread.pb.Copyable.html" title="twisted.spread.pb.Copyable">Copyable</a></code>
provides a method you can override, <code class="py-prototype">getStateToCopyFor(perspective)</code>, which
allows you to decide what an object will look like for the
perspective who is requesting it. The <code class="python">perspective</code> argument will be the perspective
which is either passing an argument or returning a result an
instance of your Copyable class. </p>
<p>For security reasons, in order to allow a particular Copyable class to
actually be copied, you must declare a <code class="python">RemoteCopy</code>
handler for
that Copyable subclass. The easiest way to do this is to declare both in the
same module, like so:
<pre class="python"><p class="py-linenumber">1
2
3
4
5
6
</p><span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">spread</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">flavors</span>
<span class="py-src-keyword">class</span> <span class="py-src-identifier">Foo</span>(<span class="py-src-parameter">flavors</span>.<span class="py-src-parameter">Copyable</span>):
<span class="py-src-keyword">pass</span>
<span class="py-src-keyword">class</span> <span class="py-src-identifier">RemoteFoo</span>(<span class="py-src-parameter">flavors</span>.<span class="py-src-parameter">RemoteCopy</span>):
<span class="py-src-keyword">pass</span>
<span class="py-src-variable">flavors</span>.<span class="py-src-variable">setUnjellyableForClass</span>(<span class="py-src-variable">Foo</span>, <span class="py-src-variable">RemoteFoo</span>)
</pre>
In this case, each time a Foo is copied between peers, a RemoteFoo will be
instantiated and populated with the Foo's state. If you do not do this, PB
will complain that there have been security violations, and it may close the
connection.
</p>
</li>
<li><code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.spread.pb.Cacheable.html" title="twisted.spread.pb.Cacheable">twisted.spread.pb.Cacheable</a></code>
<p>Let me preface this with a warning: Cacheable may be hard to understand.
The motivation for it may be unclear if you don't have some experience with
real-world applications that use remote method calling of some kind. Once
you understand why you need it, what it does will likely seem simple and
obvious, but if you get confused by this, forget about it and come back
later. It's possible to use PB without understanding Cacheable at all.
</p>
<p>Cacheable is a flavor which is designed to be copied only when necessary,
and updated on the fly as changes are made to it. When passed as an argument
or a return value, if a Cacheable exists on the side of the connection it is
being copied to, it will be referred to by ID and not copied.</p>
<p>Cacheable is designed to minimize errors involved in replicating an object
between multiple servers, especially those related to having stale
information. In order to do this, Cacheable automatically registers
observers and queries state atomically, together. You can override the
method <code class="py-prototype">getStateToCacheAndObserveFor(self,
perspective, observer)</code> in order to specify how your observers will be
stored and updated.
</p>
<p>Similar to
<code class="python">getStateToCopyFor</code>,
<code class="python">getStateToCacheAndObserveFor</code> gets passed a
perspective. It also gets passed an
<code class="python">observer</code>, which is a remote reference to a
<q>secret</q> fourth referenceable flavor:
<code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.spread.pb.RemoteCache.html" title="twisted.spread.pb.RemoteCache">RemoteCache</a></code>.</p>
<p>A <code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.spread.pb.RemoteCache.html" title="twisted.spread.pb.RemoteCache">RemoteCache</a></code> is simply
the object that represents your
<code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.spread.pb.Cacheable.html" title="twisted.spread.pb.Cacheable">Cacheable</a></code> on the other side
of the connection. It is registered using the same method as
<code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.spread.pb.RemoteCopy.html" title="twisted.spread.pb.RemoteCopy">RemoteCopy</a></code>, above.
RemoteCache is different, however, in that it will be referenced by its peer.
It acts as a Referenceable, where all methods prefixed with
<code class="python">observe_</code> will be callable remotely. It is
recommended that your object maintain a list (note: library support for this
is forthcoming!) of observers, and update them using
<code class="python">callRemote</code> when the Cacheable changes in a way
that should be noticeable to its clients. </p>
<p>Finally, when all references to a
<code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.spread.pb.Cacheable.html" title="twisted.spread.pb.Cacheable">Cacheable</a></code> from a given
perspective are lost,
<code class="py-prototype">stoppedObserving(perspective, observer)</code>
will be called on the
<code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.spread.pb.Cacheable.html" title="twisted.spread.pb.Cacheable">Cacheable</a></code>, with the same
perspective/observer pair that <code>getStateToCacheAndObserveFor</code> was
originally called with. Any cleanup remote calls can be made there, as well
as removing the observer object from any lists which it was previously in.
Any further calls to this observer object will be invalid.</p>
</li>
</ul>
</p>
<h2>Footnotes</h2><ol><li><a name="footnote-1"><span class="footnote">Most of Twisted is like this. Hell, most of
unix is like this: if <em>you</em> think it would be useful, someone else has
probably thought that way in the past, and acted on it, and you can take
advantage of the tool they created to solve the same problem you're facing
now.</span></a></li></ol></div>
<p><a href="index.html">Index</a></p>
<span class="version">Version: 10.0.0</span>
</body>
</html>
|