管理 Perspective 的客户端¶
- 作者:
Kevin Turner
概述¶
在我们之前展示的所有 IPerspective
使用中,我们忽略了 mind
参数,并为每个连接创建了一个新的 Avatar
。这通常是一个简单的设计选择,并且在简单的情况下效果很好。
在更复杂的情况下,例如一个代表游戏宇宙中持久存在的玩家对象的 Avatar
,我们希望来自同一玩家的连接使用相同的 Avatar
。
在更复杂的场景中,另一个必要的事情是异步通知玩家。当然,允许玩家调用 perspective_remoteListener(referencable)
是可能的,但这意味着代码重复和更高的登录延迟,两者都不好。
在前面的部分中,所有领域看起来都是相同的。在本部分中,我们将展示领域在实现这两个目标方面的有用性。
管理 Avatar¶
管理持久化 Avatar 的最简单方法是使用直接的缓存机制
from zope.interface import implementer
class SimpleAvatar(pb.Avatar):
greetings = 0
def __init__(self, name):
self.name = name
def perspective_greet(self):
self.greetings += 1
return "<%d>hello %s" % (self.greetings, self.name)
@implementer(portal.IRealm)
class CachingRealm:
def __init__(self):
self.avatars = {}
def requestAvatar(self, avatarId, mind, *interfaces):
if pb.IPerspective not in interfaces: raise NotImplementedError
if avatarId in self.avatars:
p = self.avatars[avatarId]
else:
p = self.avatars[avatarId] = SimpleAvatar(avatarId)
return pb.IPerspective, p, lambda:None
这为我们提供了一个 Perspective,它计算了发送给客户端的问候数量。实现缓存策略,而不是生成包含正确 Avatar 的领域,通常更容易。这使得向门户添加新的检查器或向检查器数据库添加新用户变得透明。否则,需要在检查器和 Avatar 之间进行仔细的同步(就像 UNIX 的 /etc/shadow
和 /etc/passwd
之间的同步一样)。
但是,有时 Avatar 会需要足够的每个连接状态,以至于生成新的 Avatar 并缓存其他内容会更容易。以下是一个示例
from zope.interface import implementer
class Greeter:
greetings = 0
def hello(self):
self.greetings += 1
return "<%d>hello" % (self.greetings, self.name)
class SimpleAvatar(pb.Avatar):
def __init__(self, name, greeter):
self.name = name
self.greeter = greeter
def perspective_greet(self):
return self.greeter.hello()+' '+self.name
@implementer(portal.IRealm)
class CachingRealm:
def __init__(self):
self.greeters = {}
def requestAvatar(self, avatarId, mind, *interfaces):
if pb.IPerspective not in interfaces: raise NotImplementedError
if avatarId in self.greeters:
p = self.greeters[avatarId]
else:
p = self.greeters[avatarId] = Greeter()
return pb.IPerspective, SimpleAvatar(avatarId, p), lambda:None
使用这种模式来拥有一个被通知新连接的 Avatar 似乎很诱人。但是,这里的问题有两个:它会导致一个需要转发所有方法的薄类,并且无法知道何时发生断开连接。幸运的是,有一个更好的模式
from zope.interface import implementer
class SimpleAvatar(pb.Avatar):
greetings = 0
connections = 0
def __init__(self, name):
self.name = name
def connect(self):
self.connections += 1
def disconnect(self):
self.connections -= 1
def perspective_greet(self):
self.greetings += 1
return "<%d>hello %s" % (self.greetings, self.name)
@implementer(portal.IRealm)
class CachingRealm:
def __init__(self):
self.avatars = {}
def requestAvatar(self, avatarId, mind, *interfaces):
if pb.IPerspective not in interfaces: raise NotImplementedError
if avatarId in self.avatars:
p = self.avatars[avatarId]
else:
p = self.avatars[avatarId] = SimpleAvatar(avatarId)
p.connect()
return pb.IPerspective, p, p.disconnect
可以使用这种模式来定义并发连接数的任意限制
from zope.interface import implementer
class SimpleAvatar(pb.Avatar):
greetings = 0
connections = 0
def __init__(self, name):
self.name = name
def connect(self):
self.connections += 1
def disconnect(self):
self.connections -= 1
def perspective_greet(self):
self.greetings += 1
return "<%d>hello %s" % (self.greetings, self.name)
@implementer(portal.IRealm)
class CachingRealm:
def __init__(self, max=1):
self.avatars = {}
self.max = max
def requestAvatar(self, avatarId, mind, *interfaces):
if pb.IPerspective not in interfaces: raise NotImplementedError
if avatarId in self.avatars:
p = self.avatars[avatarId]
else:
p = self.avatars[avatarId] = SimpleAvatar(avatarId)
if p.connections >= self.max:
raise ValueError("too many connections")
p.connect()
return pb.IPerspective, p, p.disconnect
管理客户端¶
到目前为止,我们所有的领域都忽略了 mind
参数。在 PB 的情况下,mind
是由远程登录方法提供的对象 - 通常,当它通过网络传输时,它会变成一个 pb.RemoteReference
。此对象允许在连接建立并经过身份验证后立即向客户端发送消息。
这是一个简单的远程时钟应用程序,它展示了 mind
参数的有用性
from zope.interface import implementer
class SimpleAvatar(pb.Avatar):
def __init__(self, client):
self.s = internet.TimerService(1, self.telltime)
self.s.startService()
self.client = client
def telltime(self):
self.client.callRemote("notifyTime", time.time())
def perspective_setperiod(self, period):
self.s.stopService()
self.s = internet.TimerService(period, self.telltime)
self.s.startService()
def logout(self):
self.s.stopService()
@implementer(portal.IRealm)
class Realm:
def requestAvatar(self, avatarId, mind, *interfaces):
if pb.IPerspective not in interfaces: raise NotImplementedError
p = SimpleAvatar(mind)
return pb.IPerspective, p, p.logout
在更复杂的情况下,您可能希望缓存 Avatar 并为每个 Avatar 提供一组“当前客户端”或类似的东西。