【RPC-Python】多线程同步模型

上节我们编写了一个最简单的 RPC 服务器模型,简单到同时只能处理单个连接。本节我们为服务器增加多线程并发处理能力,同时可以处理多个客户端连接。后来的客户端连接再也不用排队了。这也是古典 RPC 服务最常见的处理模型。

既然要使用多线程,自然离不开 Python 内置的多线程编程库。我们在上节引出的 socket、struct 和 json 三个库的基础上再增加第四个内置库 thread,本节程序的多线程功能将由它来打开。

thead

thread 是 Python 内置的线程库,用户可以使用 thread 库创建原生的线程。
在python中可以尝试以下方式创建一个线程:

def play(params):
    pass

t = threading.Thread(target=play, args=(1,2...))
t.start()

这样每个线程接收一个连接后进行业务处理,最后关闭连接。

多线程同步模型

服务器可以并行处理多个客户端,每来一个新连接,则开启一个新的线程单独进行处理。每个线程都是同步读写客户端连接。

另外新增一个服务,用来计算斐波那契数列。

直接上代码:

#server
import json
import struct
import socket
import threading


def handle_conn(conn, addr, handlers):
    print('client: {0} connect'.format(addr))

    #循环读写
    while True:
        length_prefix = conn.recv(4) #接收请求头长度
        if not length_prefix: #连接关闭
            print('client: {0} close'.format(addr))
            conn.close()
            break
        length, = struct.unpack('I', length_prefix)
        body = conn.recv(length)
        request = json.loads(body)
        print('recv :', request)
        in_ = request['in']
        params = request['params']
        handler = handlers[in_] #查找请求对应的处理函数
        handler(conn, params) #处理请求

def fib(conn, params):

    result = []
    a = 0
    b = 1
    max = int(params)
    while b < max:
        result.append(b)
        a, b = b, a + b

    send_result(conn, 'result', result)


def loop(sock, handlers):
    while True:
        conn , addr = sock.accept() #接收连接
        t = threading.Thread(target=handle_conn,  args=(conn, addr,  handlers))
        t.start()

def ping(conn, params):
    send_result(conn, 'pong', params)

def send_result(conn, out, result):
    response = json.dumps({'out' : out, 'result':result}) # 响应消息体
    lenght_prefix = struct.pack('I', len(response))
    conn.sendall(lenght_prefix)
    conn.sendall(str.encode(response))


if __name__ == '__main__':
    # 创建一个 TCP 套接字
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 打开 reuse addr 选项
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sock.bind(("localhost", 8080))  # 绑定端口
    sock.listen(1)  # 监听客户端连接

    print('listen')
    #注册请求处理器
    handlers = {
        'ping' : ping , #心跳应答
        'fib' : fib ,   #求斐波那契数列
    }
    loop(sock, handlers)

开启客户端结果如下:

import json
import time
import struct
import socket

#```
# //.1
# // 输入
# {
#     in: "ping",
#     params: "ireader 0"
# }
#
# // 输出
# {
#     out: "pong",
#     result: "ireader 0"
# }
#
# //.2
# // 输入
# {
#     in: "fib",
#     params: "5"
# }
#
# // 输出
# {
#     out: "result",
#     result: [1,2,3]
# }
# ```

def rpc(sock, in_, params):
    request = json.dumps({"in": in_, "params": params})
    length_prefix =  struct.pack('I', len(request))  #发送4字节长度信息
    sock.sendall(length_prefix)
    sock.sendall(str.encode(request))   #发送完毕
    print('send data:', str.encode(request))

    #等待响应包
    length_prefix = sock.recv(4) #响应包长度
    length, = struct.unpack('I', length_prefix) #unpack返回一个元组
    body = sock.recv(length)        # 响应消息体
    print('recv:', body)
    response = json.loads(body)
    return response['out'], response['result'] # 返回响应类型和结果

if __name__ == '__main__':
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect(('localhost', 8080))

    out, result = rpc(s, 'fib', '1000')
    print(out, result)

    s.close()

服务计算结果并返回数组:
这里写图片描述
这里写图片描述

猜你喜欢

转载自blog.csdn.net/gx864102252/article/details/81750071