python分布式编程XLMRPC
简介
rpc:远程过程调用协议。简单的来说就是客户端可以很方便得远程调用服务端的接口程序,而不用管底层是如何实现的。
XML-RPC的全称是XML Remote Procedure Call,即XML(标准通用标记语言下的一个子集)远程过程调用。它是一套允许运行在不同操作系统、不同环境的程序实现基于Internet过程调用的规范和一系列的实现。这种远程过程调用使用http作为传输协议,XML作为传送信息的编码格式。Xml-Rpc的定义尽可能的保持了简单,但同时能够传送、处理、返回复杂的数据结构。这个过程也被大家称为“分布式计算”。
xmlrpc:使用http协议作为传输协议的rpc机制。
1、注册函数:register_function
以下是定义了一个函数的xmlrpc服务端。
register_function用于注册一个供调用的函数,第一个参数为自己实现的方法名,第二个参数为供客户端调用的方法名。
1
2
3
4
5
6
|
from xmlrpc.server import SimpleXMLRPCServer #导入模块 s = SimpleXMLRPCServer(( "10.55.22.11" , 4242 )) #服务端ip假设为10.55.22.11,端口假设为4242 def twice(x): #定义函数 return x * 2 s.register_function(twice) #注册函数(让客户端发现) s.serve_forever() #启动服务端 |
下面是该服务端对应的xmlrpc客户端
根据url和端口号初始化一个服务器对象,然后调用需要的方法即可:
1
2
3
|
import xmlrpc.client #导入模块 s = xmlrpc.client.ServerProxy( 'http://10.55.22.11:4242' ) #链接服务端 print (s.twice( 2 )) #调用函数 |
调用成功成功后服务端会显示
2、注册类方法函数:register_instance
服务端如:
1
2
3
4
5
6
7
8
9
|
from xmlrpc.server import SimpleXMLRPCServer # 导入模块 s = SimpleXMLRPCServer(( "10.81.10.57" , 4242 )) # 设置服务端ip和端口 class Animal: #定义类 def rabit( self , amount): return 4 * amount def chicken( self , amount): return 2 * amount s.register_instance(Animal()) # 注册类方法函数(让客户端发现) s.serve_forever() # 启动服务端 |
对应的客户端如下:
1
2
3
4
|
import xmlrpc.client # 导入模块 s = xmlrpc.client.ServerProxy( 'http://10.81.10.57:4242' ) # 链接服务端 print (s.rabit( 2 )) # 调用函数 print (s.chicken( 2 )) |
3、dispatch方法
接下来要修改一些注册类方法函数接口的参数,对应的修改一下类定义。通过在类定义中添加—_dispatch方法可以在远程调用接口的时候经过_dispach。
服务端如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
from xmlrpc.server import SimpleXMLRPCServer # 导入模块 s = SimpleXMLRPCServer(( "10.81.10.57" , 4242 )) # 设置服务端ip和端口 class Animal: # 定义类 def _dispatch( self , method, param): # 定义_dispatch方法 print (method) # 在服务端显示调用的方法名 func = getattr ( self , method) # 调用接口 return func( * param) def rabit( self , amount): return 4 * amount def chicken( self , amount): return 2 * amount s.register_instance(Animal()) # 注册函数(让客户端发现) s.serve_forever() # 启动服务端 |
客户端如下:
1
2
3
4
|
import xmlrpc.client # 导入模块 s = xmlrpc.client.ServerProxy( 'http://10.81.10.57:4242' ) # 链接服务端 print (s.rabit( 2 )) # 调用函数 print (s.chicken( 2 )) |
注: 服务器端运行结果:
4、多线程访问
初始化服务器用的不再是SimpleXMLRPCServer了,而是自定义的一个类,继承自两个基类,ThreadingMixIn使其能够支持多线程,其余的操作方式还是和普通的一样。
并且我们新增了一个函数,接受两个参数,计算和,可以看到无论参数数量多少,我们注册函数的时候都只写函数名。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
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( "cloudox" )) # 调用函数1并传参 print (server.add( 8 , 8 )) # 调用函数2并传参 |
5、文件上传&下载
RPC除了传参以外还可以在客户端与服务器之间传输文件——客户端既可以从服务器下载文件,也可以上传文件到服务器。
传输文件要用到xmlrpc.client.Binary这个库,如果要实现从服务器下载文件的功能,那么服务器端也需要导入这个库,即使它名义上属于client库。
传输文件的基本步骤是:
- 用open打开一个文件(没有的话会创建),确定是读权限还是写权限;
- 在文件发送端通过调用xmlrpc.client.Binary来进行文件的传输,接收端通过值.data来获取内容(详见代码);
- 关闭文件。
服务器:
初始化服务器时多了个参数allow_none=True,这是允许不返回参数给客户端,因为文件上传的函数都是没有返回值的,不设置这个参数会报错,实际上这里也应该返回一个值告诉客户端是否上传成功。
文件上传的代码中可以看到,写入的是data.data,单单data是会报错的,因为实际上要写入的是Binary.data,这在下面的客户端代码下载文件时也会看到。
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() # 保持等待调用状态 |
客户端:
可以看到,下载文件时写入的也是获取到的返回值.data,而不是返回值本身,这个一定要注意。
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() |