Python实现RPC微型服务器

RPC

先说说什么是RPC,RPC(Remote Procedure Call)——远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI网络通信模型中,RPC跨越了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。

RPC采用客户机/服务器模式。请求程序就是一个客户机,而服务提供程序就是一个服务器。首先,客户机调用进程发送一个有进程参数的调用信息到服务进程,然后等待应答信息。在服务器端,进程保持睡眠状态直到调用信息到达为止。当一个调用信息到达,服务器获得进程参数,计算结果,发送答复信息,然后等待下一个调用信息,最后,客户端调用进程接收答复信息,获得进程结果,然后调用执行继续进行。

说白了,就是一种远程调用函数接口的方式,客户端和服务端之间约定一种契约(函数接口),然后服务端一直等待客户端的调用。有点像平常的WEB网络请求,不过这种方式非常轻量,不涉及HTTP这些东西,待会可以看到实现很简单。

上面说了,一种用途是在多台服务器之间互相进行调用,另一个用途则在于,不同编程语言之间都支持这种方式,像Python更是内置对其的支持,不需要额外安装什么库,所以可以直接在多语言的服务器之间互相进行调用,很简单。

xmlrpc库

在Python2(网上大部分是Python2使用RPC的资料)中,服务端需要用到SimpleXMLRPCServer库,客户端需要用到ServerProxy库,而在Python3中,两者被整合到了同一个xmlrpc库中,分为xmlrpc.server和xmlrpc.client两部分。所以如果在Python3下使用,就需要导入这个库了。

简单版

  • 服务器端
1
2
3
4
5
6
7
8
9
10
11
12
13
from xmlrpc.server import SimpleXMLRPCServer


# 调用函数
def respon_string(str):
return "get string:%s" % str


if __name__ == '__main__':
server = SimpleXMLRPCServer(('localhost', 8888)) # 初始化
server.register_function(respon_string, "get_string") # 注册函数
print("Listening for Client")
server.serve_forever() # 保持等待调用状态
  • 客户端
1
2
3
4
5
from xmlrpc.client import ServerProxy

if __name__ == '__main__':
server = ServerProxy("http://localhost:8888") # 初始化服务器
print(server.get_string("cloud")) # 调用函数并传参

多线程版

  • 服务器端
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from xmlrpc.server import SimpleXMLRPCServer
from socketserver import ThreadingMixIn


class ThreadXMLRPCServer(ThreadingMixIn, SimpleXMLRPCServer):
pass


# 调用函数1
def respon_string(str):
return "get string:%s" % str


# 调用函数2
def add(x, y):
return x + y


if __name__ == '__main__':
server = ThreadXMLRPCServer(('localhost', 8888)) # 初始化
server.register_function(respon_string, "get_string") # 注册函数1
server.register_function(add, 'add') # 注册函数2
print("Listening for Client")
server.serve_forever() # 保持等待调用状态
  • 客户端
1
2
3
4
5
6
from xmlrpc.client import ServerProxy

if __name__ == '__main__':
server = ServerProxy("http://localhost:8888") # 初始化服务器
print(server.get_string("cloud")) # 调用函数1并传参
print(server.add(8, 8)) # 调用函数2并传参

文件上传下载

  • 服务器端
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
from xmlrpc.server import SimpleXMLRPCServer
from socketserver import ThreadingMixIn
import xmlrpc.client


class ThreadXMLRPCServer(ThreadingMixIn, SimpleXMLRPCServer):
pass


# 供客户端下载文件
def image_get():
handle = open("boy.jpg", 'rb')
return xmlrpc.client.Binary(handle.read())


# 供客户端上传文件
def image_put(data):
handle = open("get_girl.jpg", 'wb')
handle.write(data.data)
handle.close()


if __name__ == '__main__':
server = ThreadXMLRPCServer(('localhost', 8888), allow_none=True) # 初始化
server.register_function(image_put, 'image_put')
server.register_function(image_get, 'image_get')
print("Listening for Client")
server.serve_forever() # 保持等待调用状态
  • 客户端
1
2
3
4
5
6
7
8
9
10
11
12
13
from xmlrpc.client import ServerProxy
import xmlrpc.client

if __name__ == '__main__':
server = ServerProxy("http://localhost:8888", allow_none=True)
# 上传文件
put_handle = open("girl.jpg", 'rb')
server.image_put(xmlrpc.client.Binary(put_handle.read()))
put_handle.close()
# 下载文件
get_handle = open("get_boy.jpg", 'wb')
get_handle.write(server.image_get().data)
get_handle.close()