• RPC开发模式


    随着企业的发展,我们的服务架构也变得庞大、复杂,在不同内部功能模块之间像调用函数一样进行数据通信,架构演变成微服务架构是一个不错的解决方案。

    微服务这种分布式的架构如何实现不同服务、不同编程语言、不同进程之间的简单、高效通信? 

    微服务除了基于HTTP协议进行API、消息队列进行数据交互,也可以统一使用gRPC协议的Protobuf数据格式进行更加简单、高效的数据交互。

    使用RPC协议和HTTP协议实现微服务的数据交互区别是什么?如何完成Golang、Python...数据交互?

    什么是RPC?

    RPC是Remote Procedure Call的缩写,就是像调用本地函数一样调用远程服务器上运行程序的函数,并返回调用结果。

    说起来很简单做起来难,把原来本地的函数放到另一台服务器上去进行远程调用,会面临以下几大问题。

    Call的id映射:服务端提供了很多可调用服务就跟Django的视图函数,如何保证客户端发送远程调用时可以区别不同可调用服务、并可以返回结果是关键。

    序列化和反序列化:数据进行序列化/反序列化的速度,和二进制数据的大小会影响网络传输速度。

    网络传输:既然远程调用肯定要通过网络协议进行传输,是采用TCP还HTTP网络传输协议呢?

    RPC框架的组成

    一个基本RPC框架由4部分组成,分别是:客户端、客户端存根、服务端、服务端存根

    客户端(Client):服务调用的发起方,也称为消费者。

    客户端存根(ClientStub):

    该程序运行在客户端所在的计算机上,主要用来存储 要调用服务器地址,对客户端发送的数据进行序列化、建立网络连接之后发送数据包给Server端的存根程序。

    接收Server端存根程序响应的消息,对消息进行反序列化。

    服务端(Server):提供客户端想要调用的函数

    服务端存根(ServerStub):接收客户端的消息并反序列化,对server端响应的消息进行序列化并响应给Client端

    总体来讲客户端和服务端的Stub在底层帮助我们实现了Call ID的映射、数据的序列化和反序列化、网络传输

    这样RPC客户端和RPC服务端 只需要专注于服务端和客户端的业务逻辑层。

    RPC和HTTP的区别

    RPC是一种解决客户端和服务端之间数据通信的方案

    HTTP协议可以实现RPC即在服务端和客户端之间完成远程过程调用,但是HTTP仅仅是实现RPC的方式之一并非唯一方式。

    传输性能角度来说HTTP协议并不是实现RPC的最优方案

    但是从客户端兼容性角度来说支持HTTP协议的客户端非常广泛,尤其是浏览器天然支持HTTP协议,HTTP客户端的开发成本也比较低。

    import json
    from http.server import HTTPServer, BaseHTTPRequestHandler
    from urllib.parse import urlparse, parse_qsl
    
    host = ("", 8003)
    
    
    class AddHandler(BaseHTTPRequestHandler):
        def do_GET(self):
            # 获取当前访问URL
            current_url = urlparse(self.path)
            # 获取URL携带的参数
            query_args = dict(parse_qsl(current_url.query))
            print(query_args)
            arg1, arg2 = int(query_args.get("arg1", 1)), int(query_args.get("arg2", 1))
            self.send_response(200)
            self.send_header("content-type", "application/json")
            self.end_headers()
            self.wfile.write(json.dumps({"result":arg1 + arg2},ensure_ascii=False).encode("utf-8"))
    
    
    if __name__ == '__main__':
        server = HTTPServer(host, AddHandler)
        print("启动服务器")
        server.serve_forever()
    http实现远程调用demo服务端

    客户端

    import json
    
    import requests
    
    #自己实现1个rpc框架
    class ClientStub():
        def __init__(self, url):
            self.url = url
    
        def add(self, arg1, arg2):
            remote_call_result = requests.get(url="%s?arg1=%s&arg2=%s" % (self.url, arg1, arg2))
            remote_call_result = json.loads(remote_call_result.text).get("result", 0)
            return remote_call_result
    
    
    # http的调用
    # 1.每个函数调用我们都得记住url的地址,参数如何传递?返回数据如何解析?
    client = ClientStub(url="http://127.0.0.1:8003/")
    print(client.add(2, 2))
    print(client.add(22, 33))
    print(client.add(33, 80))
    RPC客户端Demo

    Python之RPC开发模式

    上面我们提到一个基本RPC框架必须实现服务端客户端存根和客户端存根服务,我们才能在RPC客户端像调用函数一样去调用RPC服务端注册的函数并返回结果。

    在Python中有一些RPC框架,但是它们仅仅支持在不同的Python进程间通信。无法向gRPC一样支持

     

    所以在学习gRPC之前先使用Python体验一下 RPC开发模式和之前Web框架开发模式的区别。

     

    XMLRPC框架

    Python内置了1个SimpleXMLRPCServer库,实现了RPC,基于XML数据格式完成不同进程(微服务)之间的数据交互。

    from xmlrpc.server import SimpleXMLRPCServer
    
    
    class CalculateService():
        # 服务端 加运算
        def add(self, x, y):
            return x + y
    
        # 服务端 减运算
        def subtract(self, x, y):
            return abs(x - y)
    
        # 服务端 乘运算
        def multiply(self, x, y):
            return x * y
    
        # 服务端 除运算
        def divide(self, x, y):
            return x / y
    
    
    obj = CalculateService()
    # SimpleXMLRPCServer相当于RPC服务端Stub:
    # 处理RPC服务端数据序列化、反序列化、数据传输到RPC客户端、处理服客户端Stub的网络请求、并把RPC服务端产生的数据响应给RPC客户端的STUC
    server = SimpleXMLRPCServer(("127.0.0.1", 8002))
    # 只需要把我们写的Python类注册给RPC框架,我们的方法就会暴露给RPC客户端,
    # 这样RPC客户端就可以像调用本地函数一样调用RPC服务端的暴露的服务
    server.register_instance(obj)
    print("远程调用服务端开启")
    server.serve_forever()
    RPC服务端

    --------------

    from xmlrpc import client
    
    #ServerProxy:
    # 相当于客户端Stub:负责客户端数据序列化、反序列化、和服务端Stub建立网络连接、并把RPC客户端数据发送给服务端Stus。
    RPC_server=client.ServerProxy("http://127.0.0.1:8002")
    
    #在RPC客户端像调用本地函数一样调用 在RPC服务端注册的函数
    print(RPC_server.add(2,5))
    print(RPC_server.subtract(2,5))
    print(RPC_server.multiply(2,5))
    print(RPC_server.divide(2,5))
    RPC客户端

    JRPC框架

    jsonrpclib-pelix是基于json数据格式进行RPC的库。RPC server端支持线程池。切

    安装:

    pip install jsonrpclib-pelix -i http://pypi.douban.com/simple --trusted-host pypi.douban.com

    使用

    from jsonrpclib.SimpleJSONRPCServer import SimpleJSONRPCServer
    
    #简单的rpc server端
    # 1.实例化1个rpc server stub
    server = SimpleJSONRPCServer(("127.0.0.1", 8002))
    # 2.将函数注册到rpc server stub中
    server.register_function(lambda x, y: x + y, "add")
    server.register_function(lambda x: x, "ping")
    # 3启动rpc server stub
    server.serve_forever()
    
    #线程池rpc server端
    RPC-server端

    -----------------

    import jsonrpclib
    
    remote_sercer=jsonrpclib.ServerProxy("http://127.0.0.1:8002")
    print(remote_sercer.ping("Hellow"))
    print(remote_sercer.add(33,33))
    rpc-client端

    zerorpc框架

    以上2种RPC框架,只能在2个python进程之间相互调用,如果是不同语言Node.js和Python呢?

    zerorpc就支持Node.js和Python相互之间数据交互

    除此之外以上2种RPC框架的RPC Server端和RPC Client端都是通过直连的通信方式。

    zerorp是基于zeroMQ消息队列 + msgpack消息序列化-比http的json数据格式更加高效的协议,来实现类似跨语言的远程调用。

    在RPC Server 和 RPC Client中间增加1个消息队列,对它们进行解耦,那么RPC就会变成异步操作

    zero RPCPythonserver端使用协程提升并发效果。

    安装

    pip install zerorpc -i http://pypi.douban.com/simple --trusted-host pypi.douban.com

    启动zerorpc的服务端

    D:微服务pythonStart> zerorpc --server --bind tcp://*:1234 time
    binding to "tcp://*:1234"
    serving "time"

    zerorpc客户端调用服务端 

    D:zhanggen>zerorpc --client --connect tcp://127.0.0.1:1234 strftime %Y/%m/%d
    connecting to "tcp://127.0.0.1:1234"
    '2021/03/12'

    一元调用

    import zerorpc
    
    class HelloRPC(object):
        def hello(self,name):
            return "Hello %s"%(name)
        def add(self,a,b):
            return a+b
    
    server=zerorpc.Server(HelloRPC())
    server.bind("tcp://127.0.0.1:86")
    server.run()
    rpc-server

    --------------

    import zerorpc
    rpc_client=zerorpc.Client()
    rpc_client.connect("tcp://127.0.0.1:86")
    print(rpc_client.hello("张根"))
    print(rpc_client.add(1,1))
    rpc-clien

    流式调用

    tcp数据传输是流式的我们可以慢慢获取执行结果

    import zerorpc
    import os
    
    #rpc-server 流式响应
    class StreamingRPC(object):
        @zerorpc.stream
        def streaming_range(self, fr, to, step):
            return range(fr, to, step)
    
        @zerorpc.stream
        def run_cmd(self,cmd):
            tmp = os.popen(cmd).readlines()
            return tmp
    
    s = zerorpc.Server(StreamingRPC())
    s.bind("tcp://0.0.0.0:4242")
    s.run()
    server端

    --------

    import zerorpc
    
    c = zerorpc.Client()
    c.connect("tcp://127.0.0.1:4242")
    
    flag=True
    while flag:
        cmd=input("请输入cmd: ".strip())
        if cmd=="exit":
            flag=False
        else:
            ret=c.run_cmd(cmd)
            for i in ret:
                print(i)
    client端

    Golang之RPC开发模式

    总结

    1.选择RPC框架要考虑的因素

    在微服务架构中使用哪款RPC框架,需要考虑一下几种因素。 

    语言生态:RPC框架是否支持 主流编程语言 

    数据传输效率:考虑RPC网络传输使用的网络协议,以及数据传输格式其中包括:

    • 数据格式序列化之后的大小:压缩的越小越节省带宽,在网络中的传输速度越快。
    • 数据序列化和反序列化的速度:json.dups()和json.loads()的速度更快。

    超时机制:客户端连接服务端超时之后重试

    服务高可用性:RPC服务的高可用,尤其是RPC Server端

    负载均衡:服务如何进行快速的横向扩展

    2.RPC应用场景

    RPC模式和之前Web框架开发模式 相比起来简单了很多。

    但是我们使用基于web框架也可以达到这种效果,而且RPC开发模式限制了我们使用的客户端。

    我们使用RPC进行开发的优势在于:

    架构:和传统的后端服务相比我们使用RPC构架的业务会比较容易灵活扩展。

    网络协议选择多样性:我们可以选择 RPC的网络传输协议,你使用web框架都是基于HTTP协议。

    传输效率高:可以基于thrift实现高效的二进制传输。

    负载均衡:RPC服务端有些已经支持了负载均衡,我们也不需要在搭建Nginx。

    限流:如果RCP消息通过消息队列传输,我们还可以对客户端和服务端进行异步、解耦和限流。

    OpenStack的架构是RPC应用场景的典型案例之一,OpenStack内部组件间通过RPC进行通信,通过 RESTfull API提供对外服务。

    RPC适用于分布式架构中不同内部服务组件之间进行数据交互,HTTP适用于提供对外服务。

    参考

  • 相关阅读:
    android websocket推送
    proguardgui.bat来混淆已有的jar包
    android raw与assets区别
    Eclipse开发Android报错Jar mismatch! Fix your dependencies
    gc overhead limit exceeded
    如何签名apk,并让baidu地图正常显示
    Eclipse--Team--SVN--URL修改
    监听EditText
    android 注销
    从Android手机中取出已安装的app包,导出apk
  • 原文地址:https://www.cnblogs.com/sss4/p/14513202.html
Copyright © 2020-2023  润新知