• 一个简单RPC框架是怎样炼成的(VI)——引入服务注冊机制


    开局篇我们说了。RPC框架的四个核心内容

    1. RPC数据的传输
    2. RPC消息 协议
    3. RPC服务注冊
    4. RPC消息处理
    接下来处理RPC服务的注冊机制。所谓注冊机制,就是Server须要声明支持哪些rpc方法。然后当client发送调用某个声明的rpc方法之后,服务端能自己主动找到运行该请求的详细方法。以实际的样例为例。这是如今server端处理RPC请求的代码
        def procRequest(self):
            # 循环读取并处理收到的client请求
            while True:
                req = self.conn.recv()
                rsp = Response()
                rsp.id = req.id   
                if req.command == 'sayHello':
                    rsp.result = self.sayHello()
                elif req.command == 'whoAreYou':
                    rsp.result = self.whoAreYou()
                else:
                    raise Exception("unknown command")
                
                self.conn.send(rsp)

    上面的代码有一个非常不好的地方,非常难稳定。Server端每次新增一个支持的rpc方法,就要改动这个procRequest方法。

    有什么办法能够避免吗?有,就是引入服务注冊机制。在这里。实际就是将command与详细的function object绑定起来,说穿了就是生成一个dict,

    {‘sayHello’ : self.sayHello,     'whoAreYou': self.whoAreYou}。

    有这种dict之后,收到req 之后,仅仅要提取出command字段。然后从dict中找出相应的function。调用该function就可以。



    基本想法已定,

    首先我们实现一个比較原始的服务注冊机制。 

    这个实现非常easy。self.services就是上面的dict。通过register()去注冊服务。通过get_service()去获取服务名相应的function

    class ServiceRegister(object):
        '''
        @服务注冊  不考虑线程安全,这里简化起见,也不引入反射机制。
        '''
    
    
        def __init__(self):
            '''
            Constructor
            '''
            self.services = {}
            
        ## 注冊详细的服务
        #  @param servicename: 服务名
        #  @param obj: 详细的对象
        def register(self, obj, servicename):
            if servicename in self.services:
                print('warning: %s is already registered' % servicename)
            else:
                self.services[servicename] = obj
        
        def get_service(self, servicename):
            return self.services[servicename]
        
        def list_service(self, servicename=None):
            if servicename:
                return str({servicename, self.services[servicename]})
            else:
                return str(self.services)

    使用时。就是这个样子的

    服务注冊:

    self.services.register(self.sayHello, 'Server.sayHello', )
            self.services.register(self.whoAreYou, 'Server.whoAreYou')
            self.services.register(self.add, 'Server.add')
    服务查找
    def proc(self, req):
            rsp = Response()
            rsp.id = req.id
            rsp.result = ServiceCaller.call(self.services.get_service(req.command), req.parameter)
    ......

    上面serviceCaller的实现,就是在RPC消息,实现带參数的RPC请求中。提到的 func(**args)的技巧
    class ServiceCaller():
        def __init__(self):
            pass
        
        @classmethod
        def call(cls, caller, parameter):
            if not parameter or len(parameter) == 0:
                return caller()
            return caller(**parameter)


    以下我再引入一个自己主动注冊服务的实现

    直接上代码
    class AutoServiceRegister(AbstractServiceRegister):
        def register_class(self, obj, predicate=None):
            if not (hasattr(obj, '__class__') and inspect.isclass(obj.__class__)): 
                return False
            servicename = obj.__class__.__name__
            for (name, attr) in inspect.getmembers(obj, predicate):
                # 系统方法或者私有方法,不加入
                if name.startswith('__') or name.startswith('_' + servicename + '__'): continue
                #print(name)
                if inspect.ismethod(attr): self.register_method(attr)
                elif inspect.isfunction(attr): self.register_function(attr, servicename)
            return True


    使用
    if __name__ == '__main__':
        class AServer(object):
            def __init__(self):
                pass
            
            def sayHello(self):
                return 'Hello World'
            
            def whoAreYou(self):
                return 'I am server'
            
            def __kaos(self):
                pass
            
            def _kaos(self):
                pass
            
        obj = AServer()
        
        service = AutoServiceRegister()
        print(service.register_class(obj))
        print(service.list_services())
        print(service.get_service('AServer.sayHello')) 

    运行结果例如以下
    True
    {'AServer': {'sayHello': <bound method AServer.sayHello of <__main__.AServer object at 0x000000000294EA90>>, 'whoAreYou': <bound method AServer.whoAreYou of <__main__.AServer object at 0x000000000294EA90>>, '_kaos': <bound method AServer._kaos of <__main__.AServer object at 0x000000000294EA90>>}}
    <bound method AServer.sayHello of <__main__.AServer object at 0x000000000294EA90>>



    具体说明 一下原理,利用了类似的反射的技术。

    有兴趣的同学能够先去了解一下inspect

    • register_class表示自己主动搜索一个类对象中的成员方法,并将其作为server端的rpc方法注冊进去。
      以上面AServer为例, 会自己主动将sayHello, whoAreYou 这两个方法自己主动注冊进来。

      同一时候像__init__, __kaos, _kaos之类的系统固有方法,或者私有方法。会自己主动剔除。

    • 注冊时。传入的參数obj必须是class的instance,也就是类实例。

      尽管在python中,也支持类对象,但假设直接传递类对象,就会遇到怎样初始化的难题。所以这里一致要求,必须是类的实例。


      if not (hasattr(obj, '__class__') and inspect.isclass(obj.__class__)): 
                  return False
      类实例的特点就是,包括__class__成员,并且__class__成员的值就是该类的类对象。

      inspect.isclass就是检測是不是类对象

    • inspect.getmembers()返回的是类对象的全部成员。包括系统固有方法以及私有方法
      所以,先要将系统方法和私有方法剔除。然后通过inspect,检查该成员是不是真的是function,就是能够被调用的。

      假设是,就注冊进来

    • register_fucntion, register_method与普通的服务注冊基本一样。就是加入(key,value)对。


    总结:

    1. 引入服务注冊的方式也是为了代码解耦,将req的处理与详细的req消息内容解耦。

    2. 上面我们 引入了两种服务注冊的方式。一种方式是普通的方式,逐个加入方法。

    还有一种方式通过python的“反射”技术,自己主动查找一个类里面的方法。并自己主动加入。

    3. 方案还是非常粗糙的,实际有非常多优化的地方。


  • 相关阅读:
    linux下/proc/sysrq-trigger文件的功能
    Windows下bat命令
    转——802.11协议
    转 ——eclipse下进行Python开发 环境配置
    U盘装centos7系统过程
    django框架介绍
    2-事件之间关系(概率论与数理统计学记笔记)
    1-基本概念(概率论与数理统计学习笔记)
    舆情正负面判断

  • 原文地址:https://www.cnblogs.com/blfbuaa/p/7294150.html
Copyright © 2020-2023  润新知