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

1.RPC基本原理

文章目录

  • RPC
    • 1.定义
    • 2.概念
    • 3.优缺点
    • 4.RPC结构
    • 5.RPC消息协议
      • 5.1 消息边界
      • 5.2 内容
      • 5.3 压缩
    • 6.RPC的实现
      • 6.1 divide_protocol.py
      • 6.2 server.py
      • 6.3 client.py

RPC

1.定义

远程过程调用(remote procedure call)

2.概念

广义:所有通过网络进行通讯,的调用统称为RPC调用

狭义:不采用http协议的方式,采用自定义格式的二进制方式

3.优缺点

  • 优点
    • 效率高
    • 发起rpc调用的一方,可以忽略RPC的具体实现,如同编写本地函数调用
  • 缺点
    • 通用性不高

4.RPC结构

  • client(caller):调用者
  • client stub(bundle args/unbundle ret vals):客户端存根
  • client network service
  • server network service
  • server stub(bundle ret vals/unbundle args)

5.RPC消息协议

5.1 消息边界

  • 分隔符(\r\n)
  • 长度声明法(例如HTTP中 Content-Length)

5.2 内容

  • 二进制
  • 文本内容

5.3 压缩

  • 压缩处理是一把双刃剑,减少数据量减轻带宽压力同时,额外增加了压缩和解压缩的时间

6.RPC的实现

6.1 divide_protocol.py

import struct
from io import BytesIOclass InvalidOperation(Exception):...class DivideProtocol(object):"""float divide(1:int num1, 2:int num2=1)"""def _read_all(self, size):"""读取指定长度的字节:param size: 长度:return: 读取出的二进制数据"""if isinstance(self.conn, BytesIO):# BytesIO类型,用于演示buff = b''have = 0while have < size:chunk = self.conn.read(size - have)have += len(chunk)buff += chunkreturn buffelse:# socket类型buff = b''have = 0while have < size:chunk = self.conn.recv(size - have)have += len(chunk)buff += chunk# 客户端关闭了连接if len(chunk) == 0:raise EOFError()return buffdef args_encode(self, num1, num2=1):"""对调用参数进行编码:param num1: int:param num2: int:return: 编码后的二进制数据"""# 处理参数num1, 4字节整型buff = struct.pack('!B', 1)buff += struct.pack('!i', num1)# 处理参数num2, 4字节整型,如为默认值1,则不再放到消息中if num2 != 1:buff += struct.pack('!B', 2)buff += struct.pack('!i', num2)# 处理消息总长度,4字节无符号整型length = len(buff)# 处理方法名,字符串类型name = 'divide'# 字符串长度,4字节无符号整型msg = struct.pack('!I', len(name))msg += name.encode()msg += struct.pack('!I', length) + buffreturn msgdef args_decode(self, connection):"""获取调用参数并进行解码:param connection: 传输工具对象,如socket对象或者BytesIO对象,从中可以读取消息数据:return: 解码后的参数字典"""# 保存到当前对象中,供_read_all方式使用self.conn = connectionparam_name_map = {1: 'num1',2: 'num2'}param_len_map = {1: 4,2: 4}# 用于保存解码后的参数字典args = dict()# 读取消息总长度,4字无节符号整数buff = self._read_all(4)length = struct.unpack('!I', buff)[0]# 记录已读取的长度have = 0# 读取第一个参数,4字节整型buff = self._read_all(1)have += 1param_seq = struct.unpack('!B', buff)[0]param_len = param_len_map[param_seq]buff = self._read_all(param_len)have += param_lenargs[param_name_map[param_seq]] = struct.unpack('!i', buff)[0]if have >= length:return args# 读取第二个参数,4字节整型buff = self._read_all(1)have += 1param_seq = struct.unpack('!B', buff)[0]param_len = param_len_map[param_seq]buff = self._read_all(param_len)have += param_lenargs[param_name_map[param_seq]] = struct.unpack('!i', buff)[0]return argsdef result_encode(self, result):"""对调用的结果进行编码:param result: float 或 InvalidOperation对象:return: 编码后的二进制数据"""if isinstance(result, float):# 没有异常,正常执行# 处理结果类型,1字节无符号整数buff = struct.pack('!B', 1)# 处理结果值, 4字节floatbuff += struct.pack('!f', result)else:# 发生了InvalidOperation异常# 处理结果类型,1字节无符号整数buff = struct.pack('!B', 2)# 处理异常结果值, 字符串# 处理字符串长度, 4字节无符号整数buff += struct.pack('!I', len(result.message))# 处理字符串内容buff += result.message.encode()return buffdef result_decode(self, connection):"""对调用结果进行解码:param connection: 传输工具对象,如socket对象或者BytesIO对象,从中可以读取消息数据:return: 结果数据"""self.conn = connection# 取出结果类型, 1字节无符号整数buff = self._read_all(1)result_type = struct.unpack('!B', buff)[0]if result_type == 1:# float的结果值, 4字节floatbuff = self._read_all(4)result = struct.unpack('!f', buff)[0]return resultelse:# InvalidOperation对象# 取出字符串长度, 4字节无符号整数buff = self._read_all(4)str_len = struct.unpack('!I', buff)[0]buff = self._read_all(str_len)message = buff.decode()return InvalidOperation(message)class MethodProtocol(object):def __init__(self, connection):self.conn = connectiondef _read_all(self, size):"""读取指定长度的字节:param size: 长度:return: 读取出的二进制数据"""if isinstance(self.conn, BytesIO):# BytesIO类型,用于演示buff = b''have = 0while have < size:chunk = self.conn.read(size - have)have += len(chunk)buff += chunkreturn buffelse:# socket类型buff = b''have = 0while have < size:print('have=%d size=%d' % (have, size))chunk = self.conn.recv(size - have)have += len(chunk)buff += chunkif len(chunk) == 0:raise EOFError()return buffdef get_method_name(self):# 获取方法名# 读取字符串长度,4字节无符号整型buff = self._read_all(4)str_len = struct.unpack('!I', buff)[0]# 读取字符串buff = self._read_all(str_len)name = buff.decode()return name

6.2 server.py

import socket
import threadingfrom customize_rpc.divide_protocol import DivideProtocol, MethodProtocol, InvalidOperationclass Handlers:@staticmethoddef divide(num1, num2=1):"""除法:param num1::param num2::return:"""if num2 == 0:raise InvalidOperation()val = num1 / num2return valclass ServerStub(object):def __init__(self, connection, handlers):"""服务器存根:param connection: 与客户端的socket连接:param handlers: 存放被调用的方法"""self._process_map = {'divide': self._process_divide,}self.conn = connectionself.method_proto = MethodProtocol(self.conn)self.handlers = handlersdef process(self):"""被服务器调用的入口,服务器收到请求后调用该方法"""# 获取解析调用请求的方法名name = self.method_proto.get_method_name()# 调用对应的处理方法self._process_map[name]()def _process_divide(self):"""执行divide本地调用,并将结果返回给客户端"""# 接收调用参数proto = DivideProtocol()args = proto.args_decode(self.conn)# 进行本地divide调用try:result = self.handlers.divide(**args)except InvalidOperation as e:result = e# 构造返回值消息并返回result = proto.result_encode(result)self.conn.sendall(result)class Server(object):def __init__(self, host, port, handlers):self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)self.host = hostself.port = portself.sock.bind((host, port))self.handlers = handlersdef serve(self):"""开始服务"""self.sock.listen(128)print("开始监听")while True:conn, addr = self.sock.accept()print("建立链接%s" % str(addr))stub = ServerStub(conn, self.handlers)try:while True:stub.process()except EOFError:print("客户端关闭连接")# 关闭服务端连接conn.close()class ThreadServer(object):def __init__(self, host, port, handlers):self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)self.host = hostself.port = portself.sock.bind((host, port))self.handlers = handlersdef serve(self):"""开始服务"""self.sock.listen(128)print("开始监听")while True:conn, addr = self.sock.accept()print("建立链接%s" % str(addr))t = threading.Thread(target=self.handle, args=(conn,))t.start()def handle(self, client):stub = ServerStub(client, self.handlers)try:while True:stub.process()except EOFError:print("客户端关闭连接")client.close()if __name__ == '__main__':server = Server('127.0.0.1', 8000, Handlers)server.serve()    

6.3 client.py

import time
import socketfrom customize_rpc.divide_protocol import DivideProtocol, InvalidOperationclass Channel(object):"""连接通道"""def __init__(self, host, port):self.host = hostself.port = portdef get_connection(self):"""获取一个tcp连接"""sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)sock.connect((self.host, self.port))return sockclass ClientStub(object):"""客户端存根"""def __init__(self, channel: Channel):self.channel = channelself.conn = self.channel.get_connection()def divide(self, num1, num2=1):# 构造proto = DivideProtocol()args = proto.args_encode(num1, num2)self.conn.sendall(args)result = proto.result_decode(self.conn)if isinstance(result, InvalidOperation):raise resultelse:return resultif __name__ == '__main__':channel = Channel('127.0.0.1', 8000)stub = ClientStub(channel)for i in range(5):try:val = stub.divide(i * 100, 10)except InvalidOperation as e:print(e.message)else:print(val)time.sleep(1)

相关文章:

1.RPC基本原理

文章目录 RPC1.定义2.概念3.优缺点4.RPC结构5.RPC消息协议5.1 消息边界5.2 内容5.3 压缩 6.RPC的实现6.1 divide_protocol.py6.2 server.py6.3 client.py RPC 1.定义 远程过程调用(remote procedure call) 2.概念 广义:所有通过网络进行通讯,的调用统称为RPC调用 狭义:不采…...

vue2/3,Spring Boot以及生产环境跨域解决方案

vue2和vue3跨域解决方案 Vue 2 (基于 Webpack) 的跨域解决方案 1. 创建或编辑 vue.config.js 文件 Vue CLI为Webpack项目提供了简单的代理配置方式。你可以通过创建或编辑项目的根目录下的 vue.config.js 文件来设置开发服务器的代理规则&#xff1a; // vue.config.js mod…...

【centos8 镜像修改】centos8 镜像修改阿里云

要将 CentOS 8 的镜像源修改为阿里云镜像&#xff0c;你需要编辑 /etc/yum.repos.d/ 目录下的 .repo 文件。以下是具体的步骤&#xff1a; 备份原始的 .repo 文件&#xff1a; 在编辑之前&#xff0c;建议备份原始的 .repo 文件&#xff0c;以便在出现问题时可以恢复。 sudo cp…...

多线程编程初探:掌握基本概念与核心原理

目录 1 初识线程 1.1 线程的由来 1.2 线程的产生 1.3 进程 VS 线程 1.4 关于系统内部关于线程和进程的资源调度问题 2 页表、虚拟地址和物理地址 2.1 对物理地址的描述 2.2 对于页表设计的解析 3 线程的控制 3.1 进程创建 3.1.1 pthread_create 3.2 线程退出 3.2.1 主…...

【信息系统项目管理师】第13章:项目资源管理过程详解

更多内容请见: 备考信息系统项目管理师-专栏介绍和目录 文章目录 一、规划资源管理1、输入2、工具与技术3、输出二、估算活动资源1、输入2、工具与技术3、输出三、获取资源1、输入2、工具与技术3、输出四、建设团队1、输入2、工具与技术3、输出五、管理团队1、输入2、工具与技…...

vue3封装而成的APP ,在版本更新后,页面显示空白

一、问题展示 更新之后页面空白&#xff0c;打不开 &#xff0c;主要是由于缓存造成的 二、解决办法 1、随机数代码实现 使用随机数来动态的生成静态资源目录名可以避免浏览器缓存&#xff0c;但同时每次也会导致浏览器每次都下载最新的资源。如果静态资源过大&#xff0c;可…...

GEE云计算、多源遥感、高光谱遥感技术蓝碳储量估算;红树林植被指数计算及提取

大气温室气体浓度不断增加&#xff0c;导致气候变暖加剧&#xff0c;随之会引发一系列气象、生态和环境灾害。如何降低温室气体浓度和应对气候变化已成为全球关注的焦点。海洋是地球上最大的“碳库”,“蓝碳”即海洋活动以及海洋生物&#xff08;特别是红树林、盐沼和海草&…...

【知识】cuda检测GPU是否支持P2P通信及一些注意事项

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你&#xff0c;欢迎[点赞、收藏、关注]哦~ 代码流程 先检查所有GPU之间是否支持P2P通信&#xff1b;然后尝试启用GPU之间的P2P通信&#xff1b;再次检查所有GPU之间是否支持P2P通信。 test.cu&…...

用 Python 生成功能强大的二维码工具(支持自定义颜色与 Logo)

在很多项目中&#xff0c;二维码作为一种便捷的方式传递信息越来越常见。今天&#xff0c;我们将介绍如何用 Python 编写一个功能更全的二维码生成工具&#xff0c;它不仅支持自定义二维码的颜色&#xff0c;还能在二维码中间添加 logo。 1. 环境准备 首先&#xff0c;我们需…...

RTX5 数据队列传输流程

1、首先获取当前内存是否有值 rptr = (net_mpool_t*)osMemoryPoolAlloc(id_mp_net,0U); 说明:通过相同的key,可以操作值。 2、设值到队列中 如果有值,则将rptr变量的值放入消息队列id_mp_net rptr->len = USART2_RxBfr[0]+1;memcpy (rptr->Recvbuf, &USART2_Rx…...

24.try块怎么用 C#例子

这是一个用英语写的try-catch例子 简单来说就是一个try&#xff0c;try里面的代码可能会出错&#xff0c;然后有两个catch&#xff0c;规定了具体的错误是什么 如果发生相应的错误&#xff0c;就会把错误信息存到err里&#xff0c;err.Message是一个字符串格式的提示信息&…...

【机器学习 | 数据挖掘】智能推荐算法

【作者主页】Francek Chen 【专栏介绍】 ⌈ ⌈ ⌈智能大数据分析 ⌋ ⌋ ⌋ 智能大数据分析是指利用先进的技术和算法对大规模数据进行深入分析和挖掘&#xff0c;以提取有价值的信息和洞察。它结合了大数据技术、人工智能&#xff08;AI&#xff09;、机器学习&#xff08;ML&a…...

120.【C语言】数据结构之快速排序(详解Hoare排序算法)

目录 1.Hoare单趟排序思想 2.单趟排序代码 初次写的代码 运行结果 查找问题原因 尝试解决问题 初次修正后代码 运行结果 正确的单趟排序代码 3.将单趟排序嵌入 如何递归? 递归结束的条件 1.较容易分析的结束条件:leftright 2.以{2,1}为例分析另一个结束条件 完整…...

uniapp通过v-if进行判断时,会出现闪屏?【已解决】

1.问题&#xff1a;按钮切换时&#xff0c;通过v-if来判断&#xff0c;会出现闪烁情况&#xff0c;影响用户体验 2.v-if 闪烁问题可能的原因 ‌条件切换频繁‌&#xff1a;如果 v-if 指令的条件在短时间内频繁切换&#xff0c;会导致元素不断被销毁和重新创建&#xff0c;从而…...

各种网站(学习资源、常用工具及其他,持续更新中~)

欢迎围观笔者的个人博客~ 也欢迎通过RSS网址https://kangaroogao.github.io/atom.xml进行订阅~ 大学指南 上海交通大学生存手册中国科学技术大学人工智能与数据科学学院本科进阶指南USTC不完全入学指南大学生活质量指北科研论 信息搜集 AI信息搜集USTC飞跃网站计算机保研 技…...

网络技术-QoS策略以及如何定义 流分类,流行为,流策略

一&#xff1a;QoS策略简介 QoS策略由如下部分组成&#xff1a; 类&#xff0c;定义了对报文进行识别的规则。 流行为&#xff0c;定义了一组针对类识别后的报文所做的QoS动作。 通过将类和流行为关联起来&#xff0c;QoS策略可对符合分类规则的报文执行流行为中定义的…...

线程晨考day20

1.线程的五种状态 创建 就绪 运行 阻塞 死亡 2.创建线程的两种方式 继承Thread类 重写run方法 实现Runnable接口 重写run方法 3.调用start和调用run方法的区别 调用start方法表示会开启新的线程 run方法不会开启新的线程 4.线程调度常用的方法 sleep() join() yield() 5.进程和…...

【ES6复习笔记】迭代器(10)

什么是迭代器&#xff1f; 迭代器&#xff08;Iterator&#xff09;是一种对象&#xff0c;它能够遍历并访问一个集合中的元素。在 JavaScript 中&#xff0c;迭代器提供了一种统一的方式来处理各种集合&#xff0c;如数组、字符串、Map、Set 等。通过迭代器&#xff0c;我们可…...

MySQL的TIMESTAMP类型字段非空和默认值属性的影响

同事说他通过某款商业数据同步软件将一个 MySQL 5.7.28 的库同步到 MySQL 5.7.20 的库时&#xff0c;如果表中含有 TIMESTAMP 数据类型、缺省值为 current_timestamp 的字段&#xff0c;这些表的同步任务就都失败了&#xff0c;而另外的一些包含了 DATETIME 数据类型的表就同步…...

【Linux进程】初悉进程

学习编程就得循环渐进&#xff0c;扎实基础&#xff0c;勿在浮沙筑高台 循环渐进Forward-CSDN博客 进程调度简介 1.2进程查看命令 1.3进程的几个要素 二、进程的生命周期 2.1进程状态文字描述 2.2进程状态的切换 2.3task_struct数据结构 2.4进程优先级 ⑴优先级的代…...

Docker、Wsl 打包迁移环境

电脑需要开启wsl2 可以使用wsl -v 查看当前的版本 wsl -v WSL 版本&#xff1a; 2.2.4.0 内核版本&#xff1a; 5.15.153.1-2 WSLg 版本&#xff1a; 1.0.61 MSRDC 版本&#xff1a; 1.2.5326 Direct3D 版本&#xff1a; 1.611.1-81528511 DXCore 版本&#xff1a; 10.0.2609…...

宠物车载安全座椅市场报告:解读行业趋势与投资前景

一、什么是宠物车载安全座椅&#xff1f; 宠物车载安全座椅是一种专为宠物设计的车内固定装置&#xff0c;旨在保障宠物在乘车过程中的安全性与舒适性。它通常由高强度材料制成&#xff0c;具备良好的缓冲性能&#xff0c;并可通过安全带或ISOFIX接口固定于车内。 近年来&…...

Go爬虫开发学习记录

Go爬虫开发学习记录 基础篇&#xff1a;使用net/http库 Go的标准库net/http提供了完善的HTTP客户端功能&#xff0c;是构建爬虫的基石&#xff1a; package mainimport ("fmt""io""net/http" )func fetchPage(url string) string {// 创建自定…...

分布式光纤声振传感技术原理与瑞利散射机制解析

分布式光纤传感技术&#xff08;Distributed Fiber Optic Sensing&#xff0c;简称DFOS&#xff09;作为近年来迅速发展的新型感知手段&#xff0c;已广泛应用于边界安防、油气管道监测、结构健康诊断、地震探测等领域。其子类技术——分布式光纤声振传感&#xff08;Distribut…...

.Net Framework 4/C# 面向对象编程进阶

一、继承 (一)使用继承 子类可以继承父类原有的属性和方法,也可以增加原来父类不具备的属性和方法,或者直接重写父类中的某些方法。 C# 中使用“:”来表示两个类的继承。子类不能访问父类的私有成员,但是可以访问其公有成员,即只要使用 public 声明类成员,就既可以让一…...

[学习笔记]使用git rebase做分支差异化同步

在一个.NET 项目中&#xff0c;使用了Volo.Abp库&#xff0c;但出于某种原因&#xff0c;需要源码调试&#xff0c;因此&#xff0c;使用源码方式集成的项目做了一个分支archive-abp-source 其中引用方式变更操作的提交为&#xff1a;7de53907 后续&#xff0c;在master分支中…...

低代码采购系统搭建:鲸采云+能源行业订单管理自动化案例

在能源行业数字化转型浪潮下&#xff0c;某大型能源集团通过鲸采云低代码平台&#xff0c;仅用3周时间就完成了采购订单管理系统的定制化搭建。本文将揭秘这一成功案例的实施路径与关键成效。 项目背景与挑战 该企业面临&#xff1a; 供应商分散&#xff1a;200供应商使用不同…...

1 Studying《蓝牙核心规范5.3》

目录 [Vol 0][Part B 蓝牙规范要求] 3 定义 3.1 蓝牙产品类型 4 核心配置 4.1 基本速率核心配置 4.2 增强型数据速率核心配置 4.4 低功耗核心配置 4.5 基本速率和低功耗结合的核心配置 4.6 主机控制器接口核心配置 [Vol 1][Part A 架构]1 概述 1.1 BR/EDR操作概述 …...

OpenEuler服务器警告邮件自动化发送:原理、配置与安全实践

OpenEuler服务器警告邮件自动化发送&#xff1a;原理、配置与安全实践 在服务器的运维管理过程中&#xff0c;及时感知系统异常状态至关重要。当OpenEuler系统运行时&#xff0c;将服务器的警告信息实时推送至邮箱&#xff0c;能帮助运维人员快速响应潜在问题&#xff0c;保障…...

AUTOSAR实战教程--DoIP_01_配置项解释

配置项 解释 备注 DoIPChannelSARef 引用DoIP Tester的源地址&#xff0c;就是你在DoIP Tester这个Containter中配置的Tester实例。 DoIPChannelTARef 引用目标地址。就是你在DoIPTargetAddress这个Container中的配置。 DoIPPduRRxPduId 为该pdu设置一个ID用于DoIP…...