ช่วงนี้มีความจำเป็นต้องใช้ remote call บน python, ค้น google ดูก็พบเจ้า
Pyroหลังจากอ่านหลักการแล้วก็อ๋อ เพราะทำงานแนวเดิียวกับ java RMI
หลักการทำงาน
- Name Server
เพื่อให้ client สามารถ lookup service ได้ง่ายๆ, Pyro ก็เลย provide Name Server มาให้ด้วย
เจ้า name server ที่ pyro ให้มา มันแบ่งออกเป็น 2 ประเภทคือ
- Regular, non-persistent
ตัวนี้พอ name server process ตาย, ค่าต่างๆใน registry ก็หายไปด้วย
- Persistent
ค่าต่างๆใน registry จะเก็บไว้บน disk
เพื่อเพิ่ม availability, เจ้าตัว name server นี้ ยังสามารถ start ในแบบ Paired mode ได้อีกด้วย โดยทั้งคู่จะทำ replicate registry ซึ่งกันและกันไว้
- Client
การที่ client จะติดต่อ server ได้นั้น ขั้นแรกก็ต้องหา Name Server ให้เจอเสียก่อน
วิธีการหา Name Server ก็คือ
locator = Pyro.naming.NameServerLocator()
ns = locator.getNS()
ขั้นตอนการทำงานภายในก็คือ มันจะทำการ broadcast message ออกไป
เจ้า Name server (ซึ่ง implement broadcast listener) ก็จะตอบกลับมา
Note: กรณีของผม ผมลองใช้ boardcast บน production server แล้ว, message มันหายต๋อมไป (ด้วยความขี้เกียจไปไล่หาสาเหตุ)
ผมก็เลย config ให้ NameServerLocator มันต่อตรงไปที่ Name Server โดยตรงเลย แทนที่จะ broadcast
การ config ให้ต่อตรงทำได้โดย
Pyro.config.PYRO_NS_HOSTNAME='YOUR_HOSTNAME'
หลังจากหา Name Server เจอแล้ว ขั้นถัดไปก็คือ ทำการถามหา URI ของ server ที่ต้องการ
uri = ns.resolve('my_service')
เมื่อได้ uri แล้ว สุดท้ายก็คือทำการสร้าง proxy object ซึ่งเจ้า Pyro แบ่งประเภท Proxy ออกเป็น 2 แบบคือ
- Proxy ธรรมดา
- Proxy ที่สามารถ access attribute ของ remote object ได้ด้วย
วิธีการสร้าง proxy ก็คือ
obj = uri.getProxy()
obj = uri.getAttrProxy()
ที่เหลือ ก็เป็นการเรียกใช้ method ต่างๆบน remote object ที่เราได้มา
- Server
การทำงานฝั่ง server เริ่มต้นด้วยการสร้าง daemon object
(มี parameter เป็น name server object ซึ่ง lookup มาด้วยวิธีเดียวกันกับ client)
daemon = Pyro.core.Daemon()
daemon.useNameServer(ns)
ข้อควรระวังก็คือ ต้องระวังเรื่อง reference ถึง object daemon ของเรา
มิเช่นนั้น garbage collection อาจกวาด daemon เราทิ้งไปได้
หลังจากได้ daemon มา ก็ทำการสร้าง Object instance ที่ต้องการให้ client ต่อใช้
เนื่องจาก pyro ต้องหลอก object ของเราว่ามันทำงานเหมือนกับอยู่ในสภาพแวดล้อมแบบ stand-alone
ดังนั้น Remote Object ของเราก็เลยต้องถูกห่อไว้ใน Pyro.core.ObjBase
ซึ่งเราสามารถทำได้ 3 วิธีคือ
- ให้ Remote class ของเรา extend Pyro.core.ObjBase ตรงๆเลย
- delegate pattern
บางครั้งเราอยากเขียน Remote Class โดยไม่อยากให้มี dependency ถึง Pyro เลย
obj = Pyro.core.ObjBase()
myservice = MyService()
obj.delegateTo(myservice)
- สร้าง class ใหม่ที่ extend ทั้ง Pyro.core.ObjBase และ Service class ของเรา
class ServiceImpl(Pyro.core.ObjBase, MyService):
def __init__(self):
Pyro.core.ObjBase.__init__(self)
MyService.__init__(self)
ขั้นถัดไปก็คือทำการ binding
โดยสั่ง connect daemon กับ Object instance
ซึ่งเจ้า daemon จะจัดการไปคุยกับ Name Server ให้เราเอง
daemon.connect(obj, 'my_service')
สุดท้ายก็คือการสั่งให้ daemon วน loop รับ request จาก client
daemon.requestLoop()
สิ่งที่ต้องระวังในการ implement remote object ของเราก็คือ
มันต้องเป็น thread safe เนื่องจาก daemon จะใช้วิธีแตก thread เมื่อมี incoming connection วิ่งเข้ามา
feature ที่น่าสนใจในประเด็นนี้ ก็คือ TLS (Thread Local Storage)
ซึ่งช่วยให้เราเก็บข้อมูลโดยการ bind local data เข้ากับ thread แทน
feature ที่น่าสนใจอีกอย่างก็คือ Mobile code
case นี้เป็นกรณีที่ client call ไปยัง remote object โดยส่ง argument เป็น object ที่ไม่มี code อยู่บน server มาด้วย
ซึ่งโดยปกติ มันจะเกิด error ประเภท NoModuleError
แต่ถ้าเรากำหนด configuration ให้ Pyro ใช้ feature Mobile code
เวลาที่ server เจอ code ที่ไม่รู้จัก มันก็จะ request code มายัง client
กรณี mobile code นี้ support 2-way
นั่นคือ support กรณี server return object ที่ไม่มี code บน client ด้วย