远程调用使得调用远程服务器的对象、方法的方式就和调用本地对象、方法的方式差不多,因为我们通过网络编程把这些都隐藏起来了。远程调用是分布式系统的基础。
远程调用一般分为两种,远程过程调用(RPC)和远程方法调用(RMI)。
RPC
RPC属于函数级别的远程调用,其多是通过HTTP传输数据,数据形式有XML、JSON、序列化数据等。在此,用python做一个xml-rpc的示例。 先给服务器端server.py:
1
2
3
4
5
6
7
|
from
SimpleXMLRPCServer
import
SimpleXMLRPCServer
def
add
(
x
,
y
)
:
return
x
+
y
if
__name__
==
'__main__'
:
s
=
SimpleXMLRPCServer
(
(
'127.0.0.1'
,
8080
)
)
s
.
register_function
(
add
)
s
.
serve_forever
(
)
|
s是一个绑定了本地8080端口的服务器对象,register_function()方法将函数add注册到s中。serve_forever()启动服务器。
再给个客户端client.py:
1
2
3
4
5
|
from
xmlrpclib
import
ServerProxy
if
__name__
==
'__main__'
:
s
=
ServerProxy
(
"http://127.0.0.1:8080"
)
print
s
.
add
(
3
,
4
)
|
现在,运行server.py,然后运行client.py,client.py所在的console会输出7。
我们用wireshark看一下这期间传递的数据是什么样子的,请求的数据:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
<?
xml
version
=
'1.0'
?>
<
methodCall
>
<
methodName
>
add
<
/
methodName
>
<
params
>
<
param
>
<
value
>
<
int
>
3
<
/
int
>
<
/
value
>
<
/
param
>
<
param
>
<
value
>
<
int
>
4
<
/
int
>
<
/
value
>
<
/
param
>
<
/
params
>
<
/
methodCall
>
|
响应的数据:
1
2
3
4
5
6
7
8
9
10
|
<?
xml
version
=
'1.0'
?>
<
methodResponse
>
<
params
>
<
param
>
<
value
>
<
int
>
7
<
/
int
>
<
/
value
>
<
/
param
>
<
/
params
>
<
/
methodResponse
>
|
好吧,言简意赅,不做赘述。
RMI
RMI意为远程方法调用,粒度比RPC要大,因为它的基本单位是对象。其大致思路是这样的:创建RMI服务器对象,将实例化的某个对象以指定的服务名称(也可以是多个对象,但是服务名称不应相同)注册到RMI服务器对象中,之后启动RMI服务器。服务器等待客户端发送的数据(包括服务名称、函数名、参数),将处理结果返回给客户端。 Pyro4是一个基于python的RMI实现,下面我们用Pyro4创建一个RMI服务器,请看server2.py:
1
2
3
4
5
6
7
8
9
10
11
|
import
Pyro4
class
GreetingMaker
(
object
)
:
def
get_fortune
(
self
,
name
)
:
return
"Hello, {0}. \n"
.
format
(
name
)
greeting_maker
=
GreetingMaker
(
)
daemon
=
Pyro4
.
Daemon
(
)
uri
=
daemon
.
register
(
greeting_maker
)
print
"Ready. Object uri ="
,
uri
daemon
.
requestLoop
(
)
|
uri变量是Pyro4用自己的方法为greeting_maker对象生成的uri,其中包括套接字以及为greeting_maker生成的唯一的id。这个id相当于服务名称,当然也可以指定更易懂的服务名称。
下面是客户端client2.py:
1
2
3
4
5
|
import
Pyro4
uri
=
raw_input
(
" Pyro uri : "
)
.
strip
(
)
name
=
raw_input
(
"Your name: "
)
.
strip
(
)
greeting_maker
=
Pyro4
.
Proxy
(
uri
)
print
greeting_maker
.
get_fortune
(
name
)
|
这其中要输入的uri也就是server2.py生成的uri。通过给Pyro4.Proxy传递greeting_maker的uri,可以认为和服务器端的greeting_maker建立的连接,然后调用greeting_maker的get_fortune()方法。如果name是letian,那么print greeting_maker.get_fortune(name)的结果是Hello, letian.。