当前位置: 首页 > news >正文

一个简单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. 方案还是非常粗糙的,实际有非常多优化的地方。


相关文章:

  • UVa 123042D Geometry 110 in 1! [平面几何]
  • 【实用代码片段】将json数据绑定到html元素 (转)
  • HNUSTOJ 1516:Loky的烦恼
  • MySQL运维命令大全
  • 蓝盾股份增资参股云海麒麟 布局国产云计算业务
  • 2017年十大技术发展趋势概述
  • wxWidgets第十课 渲染字体
  • Centos x64 6.9下载地址
  • 十二个 ASP.NET Core 例子——1.1版本 EF MySql快速搭建
  • Kubernetes PodGC Controller源码分析
  • CodeMirror使用
  • Sencha Cmd 6 和 Ext JS 6 指南文档(部分官方文档中文翻译)
  • 给你的手机加上安全保障,请设置SIM卡PIN码
  • Linux之RPM包
  • 高性能JavaScript阅读简记(三)
  • 2018天猫双11|这就是阿里云!不止有新技术,更有温暖的社会力量
  • Angular 4.x 动态创建组件
  • Angular数据绑定机制
  • Docker 笔记(1):介绍、镜像、容器及其基本操作
  • ES6 学习笔记(一)let,const和解构赋值
  • ES6系列(二)变量的解构赋值
  • Intervention/image 图片处理扩展包的安装和使用
  • Java程序员幽默爆笑锦集
  • jdbc就是这么简单
  • Less 日常用法
  • React 快速上手 - 07 前端路由 react-router
  • Vue2.x学习三:事件处理生命周期钩子
  • 阿里云前端周刊 - 第 26 期
  • 高性能JavaScript阅读简记(三)
  • 近期前端发展计划
  • 深度学习在携程攻略社区的应用
  • 我看到的前端
  • 一个普通的 5 年iOS开发者的自我总结,以及5年开发经历和感想!
  • 硬币翻转问题,区间操作
  • 运行时添加log4j2的appender
  • 主流的CSS水平和垂直居中技术大全
  • ​​快速排序(四)——挖坑法,前后指针法与非递归
  • #!/usr/bin/python与#!/usr/bin/env python的区别
  • #每日一题合集#牛客JZ23-JZ33
  • (13):Silverlight 2 数据与通信之WebRequest
  • (C#)获取字符编码的类
  • (echarts)echarts使用时重新加载数据之前的数据存留在图上的问题
  • (Matlab)基于蝙蝠算法实现电力系统经济调度
  • (zt)基于Facebook和Flash平台的应用架构解析
  • (免费分享)基于springboot,vue疗养中心管理系统
  • (转)linux自定义开机启动服务和chkconfig使用方法
  • (转)利用PHP的debug_backtrace函数,实现PHP文件权限管理、动态加载 【反射】...
  • (转载)CentOS查看系统信息|CentOS查看命令
  • (转载)利用webkit抓取动态网页和链接
  • .net 无限分类
  • .NET(C#) Internals: as a developer, .net framework in my eyes
  • .NET下ASPX编程的几个小问题
  • 。Net下Windows服务程序开发疑惑
  • /*在DataTable中更新、删除数据*/
  • @entity 不限字节长度的类型_一文读懂Redis常见对象类型的底层数据结构