實際上這章壓根不需要我來說,twisted官網的Doc裡面有專門介紹的章節。寫的非常詳細。
http://twistedmatrix.com/documents/current/core/howto/index.html
我隻能膚淺的說說firefly裡面對PB的運用。
首先firefly使用PB的目的是實作各個子產品之間的通信,做到“分布式”,邏輯分離。
比如master子產品專門負責控制,gate做分發,game1做遊戲邏輯,net做網絡相關。如果不用twisted.pb的話,我們就要自己寫複雜socket邏輯,來實作各個節點之間的資料通訊。
現在有了twisted.pb,“媽媽再也不用擔心我的學習”。
Firefly所有的分布式相關代碼都在firefly/distribute/目錄
__init__.py child.py manager.py node.py reference.py root.py
root.py 實作PB的server功能
node.py 實作PB的client功能。
child.py 每個client連接配接上root都會初始化一個child來儲存該client的相關資訊,并且将這些child通過manager來管理。
manager.py 管理root的child,通過一個字典self._childs = {},實作一個增删改的小管理器。
reference.py 如果你看了前面twisted官網的介紹就會知道,node隻要執行個體化一個 pb.Referenceable 類,并把它傳遞給root,那麼root就能夠把這個pb.Referenceble當成句柄來遠端調用client的函數。
前面章節提到master子產品實作了一個PBRoot作為server等待client端的連接配接。我們這裡先拿DB子產品來說明。(DB子產品的其它功能,和我改寫的部分後面會詳細介紹。)
master子產品裡面實作的代碼如下(這個是我改過的代碼,稍後上傳git):
38 def__startRoot(self):
39 GlobalObject().root = PBRoot("rootservice")
40 reactor.listenTCP(self.rootport,BilateralFactory(GlobalObject().root))
其中PBRoot類有2個關鍵函數。
defremote_register(self,name,transport):
"""設定代理通道
@param addr:(hostname,port)hostname 根節點的主機名,根節點的端口
"""
log.msg('node [%s]registerdd' % name)
child =Child(name,transport)
# child.setTransport(transport)
self.childsmanager.addChild(child)
defremote_callTarget(self,command,*args,**kw):
"""遠端調用方法
@param commandId:int 指令号
@param data: str 調用參數
"""
data =self.service.callTarget(command,*args,**kw)
return data
remote_register(),這個函數名稱被我改了,原先好像叫做remote_takeproxy()。大家了解的角度不一樣,原先作者lan可能是認為這個函數的功能是root取得其它子產品提供給他的代理。我認為,這個函數是其它子產品注冊到root。
PB的約定是,本地函數起名remote_xxx(),遠端函數調用直接callremote(“XXX”),是以按照習慣,大家看到的remote_xxx()函數都是提供給對方調用的。
另外,這裡補充一下,twisted官網提到,PB一旦建立好連接配接以後,server和client的行為其實是對等的,大家權限,調用都一樣。
再看一下上面2個函數,regist可以看出就是用child類來儲存一下注冊過來的client。callTarget函數就是通過services來執行遠端的調用指令。具體的callTarget邏輯後面有空再介紹。
下面 我們看client端,拿DB來說。和master子產品不一樣,其它子產品,包括dbfront,啟動過程依賴配置檔案config.json的設定後面詳細讨論。這裡我們隻關注PB相關。
下面的代碼取自firefly/server/server.py
(實際上已經被我整理過,但具體代碼邏輯還是一樣)
59 if masterconf: #這裡一定為True
60 masterport = masterconf.get('rootport')
61 self.master_remote =RemoteObject(servername)
62 addr = ('localhost',masterport)
63 self.master_remote.connect(addr)
64 GlobalObject().masterremote =self.master_remote
這裡的RemoteObject類的初始化__init__函數如下:(firefly/distribute/node.py)
def__init__(self,name):
23 """初始化遠端調用對象
24 @param port: int遠端分布服的端口号
25 @param rootaddr:根節點伺服器位址
26 """
27 self._name = name
28 self._factory = pb.PBClientFactory()
29 self._reference = ProxyReference()#這個就是pb.Referenceable的子類
30 self._addr =None
可以看出我們實作了一個RemoteObject類,這個類包括了pb.PBClientFactory 和pb.Referenceble。在line 63對應的代碼裡面,我們connect的時候
reactor.connectTCP(addr[0], addr[1], self._factory)
就建立了一個root和node的連接配接。然後再調用下面的函數。
def register(self):
"""把本節點注冊到RootNode,并且向RootNode發送代理通道對象
"""
deferedRemote =self._factory.getRootObject()#取得root的調用句柄。
deferedRemote.addCallback(callBack,'register',self._name,self._reference)#callBack函數會調用pb.callRemote()
這個函數就2行,第一行是twisted.pb的client取得root的句柄,有了這個句柄,我們就能夠通過callRemote來調用root的相應函數。這裡調用的regist,對應root的remote_regist()函數,并且把自己的referenceble傳遞給root,那麼後面root就可以通過這個referenceble來調用自己(node)了。
OK,firefly對twisted.pb的封裝和實作就介紹到這裡。PB的介紹先告一段落,由于俺能力實在有限,可能大家還沒有看清楚。
别擔心,我們後面接着介紹各個子產品的過程中也會穿插firefly的PB運用的細節介紹。之後如果有時間精力我們再對各個子產品中運用PB實作的功能做個總結。