إضافة طرق إلى خادم RPC بسيط بطريقة نظيفة وفصل
-
02-10-2019 - |
سؤال
لقد خلقت بسيطة RPC الخادم لأداء مهام معينة شائعة في فرقنا ، ولكن يتم استدعاؤها من شبكات مختلفة. يبدو الخادم هكذا (لا أتضمن معالجة الأخطاء للإيجاز):
from twisted.internet.protocol import Protocol, Factory
from twisted.internet import reactor
import json
class MyProtocol(Protocol):
def dataReceived(self, data):
req = json.loads(data) # create a dictionary from JSON string
method = getattr(self, req['method']) # get the method
method(req['params']) # call the method
def add(self, params):
result = {} # initialize a dictionary to convert later to JSON
result['result'] = sum(params)
result['error'] = None
result['id'] = 1
self.transport.write(json.dumps(result)) # return a JSON string
self.transport.loseConnection() # close connection
factory = Factory()
factory.protocol = MyProtocol
reactor.listenTCP(8080, factory)
reactor.run()
هذا أمر بسيط للغاية: يتلقى الخادم طلب JSON RPC من العميل ، ويبحث عن الطريقة ، ويدعو الطريقة التي تمرير المعلمات. الطريقة نفسها هي التي تعيد استجابة JSON RPC. لأقل دراية ، أ JSON RPC يبدو مثل هذا:
request:
{"method":"my_method", "params":[1,2,3], "id":"my_id"}
response:
{"result":"my_result", "error":null, "id":"my_id"}
خادم RPC كما لديّ يخدم أغراضي الحالية جيدًا (كما يمكنك أن تتخيل ، فإن مهمتي بسيطة للغاية). لكنني سأحتاج إلى مواصلة إضافة طرق مع زيادة تعقيد المهمة.
لا أريد فتح الملف الرئيسي وإضافة آخر def method3(...)
وبعد أسبوعين ، أضف def method4(...)
وهكذا دواليك؛ سوف ينمو الرمز بسرعة كبيرة وستكون الصيانة أصعب وأصعب.
لذا ، سؤالي هو: كيف يمكنني إنشاء بنية تسمح لي بذلك تسجيل طرق في الخادم. تتمثل المكافأة في الحصول على مجلد منفصل يحمل ملفًا واحدًا لكل طريقة ، بحيث يمكن مشاركتها بسهولة وصيانتها. ستسمح لي هذه "الهندسة المعمارية" أيضًا بتأجيل صيانة بعض الأساليب لشخص آخر ، بغض النظر عن فهمه للملتوية.
لا يهمني إذا كنت بحاجة إلى إعادة تشغيل الخادم في كل مرة يتم فيها تسجيل طريقة جديدة ، لكن ميزة واضحة ستكون إذا لا لديك أيضا :).
شكرًا.
المحلول
القليل من الترتيب اللامشق ؛) ولكن إليك بعض الخطوات الأولية بالنسبة لك (تسخر من السخرية الشديدة ، التفاصيل الملتوية في الأمثلة):
# your twisted imports...
import json
class MyProtocol(object): # Would be Protocol instead of object in real code
def dataReceived(self, data):
req = json.loads(data) # create a dictionary from JSON string
modname, funcname = req['method'].split('.')
m = __import__(modname)
method = getattr(m, funcname) # get the method
method(self, req['params']) # call the method
على افتراض أنك تجربها كما لو قمنا بتنفيذ هذا:
mp = MyProtocol()
mp.dataReceived('{"method":"somemod.add", "params":[1,2,3]}')
لديك وحدة نمطية somemod.py
في نفس الدليل مثل المثال مع المحتويات التالية (يعكس طريقة المثال الخاص بك .add()
في الاعلى):
import json
def add(proto, params):
result = {} # initialize a dictionary to convert later to JSON
result['result'] = sum(params)
result['error'] = None
result['id'] = 1
proto.transport.write(json.dumps(result)) # return a JSON string
proto.transport.loseConnection() # close connection
هذا يتيح لك الحصول على وحدة واحدة لكل طريقة يتم تقديمها. ال method(..
المكالمة أعلاه سوف تمر دائمًا MyProtocol
مثال على التقديم القابل للاستدعاء. (إذا كنت تريد حقًا طرق المثيل ، فإليك إرشادات حول كيفية إضافة طرق باستخدام Python: http://irrepupavel.com/documents/python/instancemethod/ )
ستحتاج إلى الكثير من معالجة الأخطاء المضافة. على سبيل المثال ، تحتاج إلى الكثير من الأخطاء في فحص split()
استدعاء الخط 2 من dataReceived()
.
مع هذا ، يمكنك الحصول على ملفات منفصلة مع وظيفة واحدة فيها لكل طريقة تحتاج إلى دعمها. لا يوجد أي مثال كامل بأي حال من الأحوال ولكنه قد يجعلك مستمراً ، لأن ما تبحث عنه هو معقد للغاية.
لتسجيل أكثر رسمية ، أوصي dict
في MyProtocol
مع أسماء الطرق التي تدعمها ، على غرار:
# in MyProtocol's __init__() method:
self.methods = {}
وطريقة التسجيل ..
def register(self, name, callable):
self.methods[name] = callable
..تعديل dataReceived()
..
def dataReceived(self, data):
# ...
modname, funcname = self.methods.get(req['method'], False)
# ..continue along the lines of the dataReceived() method above
ملخص سريع لمشاركة طويلة جدًا: __import__
وظيفة ( http://docs.python.org/library/functions.html ) بالتأكيد سيكون جزءًا رئيسيًا من الحل الخاص بك.