[Python] RPC实现
单线程同步
- 使用socket传输数据
- 使用json序列化消息体
- struct将消息编码为二进制字节串,进行网络传输
消息协议


1 // 输入
2 {
3 in: "ping",
4 params: "ireader 0"
5 }
6
7 // 输出
8 {
9 out: "pong",
10 result: "ireader 0"
11 }
客户端 client.py


1 # coding: utf-8
2 # client.py
3
4 import json
5 import time
6 import struct
7 import socket
8
9
10 def rpc(sock, in_, params):
11 response = json.dumps({"in": in_, "params": params}) # 请求消息体
12 length_prefix = struct.pack("I", len(response)) # 请求长度前缀
13 sock.sendall(length_prefix)
14 sock.sendall(response)
15 length_prefix = sock.recv(4) # 响应长度前缀
16 length, = struct.unpack("I", length_prefix)
17 body = sock.recv(length) # 响应消息体
18 response = json.loads(body)
19 return response["out"], response["result"] # 返回响应类型和结果
20
21 if __name__ == '__main__':
22 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
23 s.connect(("localhost", 8080))
24 for i in range(10): # 连续发送10个rpc请求
25 out, result = rpc(s, "ping", "ireader %d" % i)
26 print out, result
27 time.sleep(1) # 休眠1s,便于观察
28 s.close() # 关闭连接
服务端 blocking_single.py


1 # coding: utf8
2 # blocking_single.py
3
4 import json
5 import struct
6 import socket
7
8
9 def handle_conn(conn, addr, handlers):
10 print addr, "comes"
11 while True: # 循环读写
12 length_prefix = conn.recv(4) # 请求长度前缀
13 if not length_prefix: # 连接关闭了
14 print addr, "bye"
15 conn.close()
16 break # 退出循环,处理下一个连接
17 length, = struct.unpack("I", length_prefix)
18 body = conn.recv(length) # 请求消息体
19 request = json.loads(body)
20 in_ = request['in']
21 params = request['params']
22 print in_, params
23 handler = handlers[in_] # 查找请求处理器
24 handler(conn, params) # 处理请求
25
26
27 def loop(sock, handlers):
28 while True:
29 conn, addr = sock.accept() # 接收连接
30 handle_conn(conn, addr, handlers) # 处理连接
31
32
33 def ping(conn, params):
34 send_result(conn, "pong", params)
35
36
37 def send_result(conn, out, result):
38 response = json.dumps({"out": out, "result": result}) # 响应消息体
39 length_prefix = struct.pack("I", len(response)) # 响应长度前缀
40 conn.sendall(length_prefix)
41 conn.sendall(response)
42
43
44 if __name__ == '__main__':
45 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 创建一个TCP套接字
46 sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 打开reuse addr选项
47 sock.bind(("localhost", 8080)) # 绑定端口
48 sock.listen(1) # 监听客户端连接
49 handlers = { # 注册请求处理器
50 "ping": ping
51 }
52 loop(sock, handlers) # 进入服务循环
多线程同步
- 使用线程库thread创建原生线程
- 服务器可并行处理多个客户端
服务端 multithread.py

多进程同步
- Python的GIL导致单个进程只能占满一个CPU核心,多线程无法利用多核优势
- os.fork()会生成子进程
- 子进程退出后,父进程需使用waitpid系统调用收割子进程,防止其称为僵尸资源
- 在子进程中关闭服务器套接字后,在父进程中也要关闭服务器套接字
- 因为进程fork后,父子进程都有自己的套接字引用指向内核的同一份套接字对象,套接字引用计数为2,对套接字进程close,即将套接字对象的引用计数减1
服务端 multiprocess.py

PreForking同步
- 进程比线程耗费资源,通过PreForking进程池模型对服务器开辟的进程数量进行限制,避免服务器负载过重
- 如果并行的连接数量超过了prefork进程数量,后来的客户端请求将会阻塞
单进程异步
- 通过事件轮询API,查询相关套接字是否有响应的读写事件,有则携带事件列表返回,没有则阻塞
- 拿到读写事件后,可对事件相关的套接字进行读写操作
- 设置读写缓冲区
- Nginx/Nodejs/Redis都是基于异步模型
- 异步模型编码成本高,易出错,通常在公司业务代码中采用同步模型,仅在讲究高并发高性能的场合才使用异步模型
PreForking异步
- Tornado/Nginx采用了多进程PreForking异步模型,具有良好的高并发处理能力
参考
Python多线程和多进程
https://www.cnblogs.com/yssjun/p/11302500.html
[Python] RPC实现的更多相关文章
- Python RPC 之 gRPC
gRPC 简介: gRPC 是一款高性能.开源的 RPC 框架,产自 Google,基于 ProtoBuf 序列化协议进行开发,支持多种语言(Golang.Python.Java等),本篇只介绍 Py ...
- Python RPC 不会?不妨看看这篇文章
1. 前言 大家好,我是安果! RPC,全程为 Remote Procedure Call,是一种进程间的通信方式,它采用「 服务端 / 客户机 」模式,是一种请求响应模型 其中,服务端负责提供服务程 ...
- python rpc 的实现
所谓RPC,是远程过程调用(Remote Procedure Call)的简写,网上解释很多,简单来说,就是在当前进程调用其他进程的函数时,体验就像是调用本地写的函数一般.本文实现的是在本地调用远端的 ...
- python使用grpc调用rpc接口
proto文件: syntax = "proto3"; package coupon; // //message UnsetUseC2URequest { // int64 bid ...
- python thrift使用实例
前言 Apache Thrift 是 Facebook 实现的一种高效的.支持多种编程语言的远程服务调用的框架.本文将从 Python开发人员角度简单介绍 Apache Thrift 的架构.开发和使 ...
- RPC简介及框架选择
简单介绍RPC协议及常见框架,对比传统restful api和RPC方式的优缺点.常见RPC框架,gRPC及序列化方式Protobuf等 HTTP协议 http协议是基于tcp协议的,tcp协议是流式 ...
- 服务治理与RPC · 跬步
以前写过Django中使用zerorpc的方法,但是由于我们的Django是运行在gevent下,而zeromq需要启动一个后台进程处理消息,与gevent使用的greenlet携程是冲突的. 在Ja ...
- Odoo14 rpc
odoo14中rpc调用分为两种. 一种是外部调用rpc来访问odoo数据,这个时候你需要登录授权. 另一种是我们自己编写的widget小部件或者自定义视图时候通过js通过rpc去获取数据. 这里说的 ...
- [源码解析] PyTorch 分布式 Autograd (3) ---- 上下文相关
[源码解析] PyTorch 分布式 Autograd (3) ---- 上下文相关 0x00 摘要 我们已经知道 dist.autograd 如何发送和接受消息,本文再来看看如何其他支撑部分,就是如 ...
随机推荐
- [模拟]P1046 陶陶摘苹果
陶陶摘苹果 ## 题目描述 陶陶家的院子里有一棵苹果树,每到秋天树上就会结出10个苹果.苹果成熟的时候,陶陶就会跑去摘苹果.陶陶有个30厘米高的板凳,当她不能直接用手摘到苹果的时候,就会踩到板凳上再试 ...
- java面试-JVM内存结构
一.JVM内存结构 二.类加载(classLoader)机制 java中的ClassLoader详解 java类加载机制面试题 java类加载机制面试题 虚拟机把描述类的数据从Class文件加载到内存 ...
- Egress-Assess-出口数据安全功能测试
简介 Egress-Assess是一款用于测试出口数据检测功能的工具,该工具可辅助完成数据安全模型测试. 在各种情况下,我们的团队都会尝试从我们正在运行的网络中提取数据,并将其移至另一个位置以进行脱机 ...
- Netcore webapi action swagger response返回参数使用匿名类型
问题:action中返回匿名对象时,swagger只能按强类型生成返回值描述 解决办法:使用roslyn在内存中动态执行代码,使用json.net反序列化匿名对象,向swagger返回动态匿名对象 效 ...
- 如何保证Redis的高并发和高可用?
就是如果你用redis缓存技术的话,肯定要考虑如何用redis来加多台机器,保证redis是高并发的,还有就是如何让Redis保证自己不是挂掉以后就直接死掉了,redis高可用 redis高并发:主从 ...
- Day12 _63_获取当前线程对象
给线程起名 * 1. Thread.currentThread(); 可以获取到当前线程对象,出现在哪就是获取哪个线程. * 2. thread.setName(); 给该线程起名字 * 3. thr ...
- 【Redis破障之路】三:Redis单线程架构
众所周知,Redis是一个单线程架构的NoSQL数据库,但是是单线程模型的Redis为什么性能如此之高?这就是我们接下来要探究学习的内容. 1.Redis的单线程架构 1.1.Redis单线程简介 首 ...
- math random模块
math --- 数学函数 该模块提供了对C标准定义的数学函数的访问,返回值除非有明确说明,否则所有返回值均为浮点数 math.ceil(x) 返回 x 的上限,即大于或者等于 x 的最小整数. 如果 ...
- 去css 冗余方法
1.float在某种意义上而言与display:inline-block属性的作用是一模一样的, 所以类似于display:block; float:left; 就是说.float:left 可以让行 ...
- Laravel 队列功能 简单应用
生成任务类 默认情况下,应用程序的所有可排队任务都存储在 app/Jobs 目录下.如果 app/Jobs 目录不存在,则会在运行 make:job Artisan 命令时将创建它.你可以使用 Art ...