|
5 | 5 | ----------
|
6 | 6 | 问题
|
7 | 7 | ----------
|
8 |
| -You want an easy way to execute functions or methods in Python programs running on |
9 |
| -remote machines. |
| 8 | +你想找到一个简单的方式去执行运行在远程机器上面的Python程序中的函数或方法。 |
10 | 9 |
|
11 | 10 | |
|
12 | 11 |
|
13 | 12 | ----------
|
14 | 13 | 解决方案
|
15 | 14 | ----------
|
16 |
| -Perhaps the easiest way to implement a simple remote procedure call mechanism is to |
17 |
| -use XML-RPC. Here is an example of a simple server that implements a simple key- |
18 |
| -value store: |
19 |
| - |
20 |
| -from xmlrpc.server import SimpleXMLRPCServer |
21 |
| - |
22 |
| -class KeyValueServer: |
23 |
| - _rpc_methods_ = ['get', 'set', 'delete', 'exists', 'keys'] |
24 |
| - def __init__(self, address): |
25 |
| - self._data = {} |
26 |
| - self._serv = SimpleXMLRPCServer(address, allow_none=True) |
27 |
| - for name in self._rpc_methods_: |
28 |
| - self._serv.register_function(getattr(self, name)) |
29 |
| - |
30 |
| - def get(self, name): |
31 |
| - return self._data[name] |
32 |
| - |
33 |
| - def set(self, name, value): |
34 |
| - self._data[name] = value |
35 |
| - |
36 |
| - def delete(self, name): |
37 |
| - del self._data[name] |
38 |
| - |
39 |
| - def exists(self, name): |
40 |
| - return name in self._data |
41 |
| - |
42 |
| - def keys(self): |
43 |
| - return list(self._data) |
44 |
| - |
45 |
| - def serve_forever(self): |
46 |
| - self._serv.serve_forever() |
47 |
| - |
48 |
| -# Example |
49 |
| -if __name__ == '__main__': |
50 |
| - kvserv = KeyValueServer(('', 15000)) |
51 |
| - kvserv.serve_forever() |
52 |
| - |
53 |
| -Here is how you would access the server remotely from a client: |
54 |
| - |
55 |
| ->>> from xmlrpc.client import ServerProxy |
56 |
| ->>> s = ServerProxy('http://localhost:15000', allow_none=True) |
57 |
| ->>> s.set('foo', 'bar') |
58 |
| ->>> s.set('spam', [1, 2, 3]) |
59 |
| ->>> s.keys() |
60 |
| -['spam', 'foo'] |
61 |
| ->>> s.get('foo') |
62 |
| -'bar' |
63 |
| ->>> s.get('spam') |
64 |
| -[1, 2, 3] |
65 |
| ->>> s.delete('spam') |
66 |
| ->>> s.exists('spam') |
67 |
| -False |
68 |
| ->>> |
| 15 | +实现一个远程方法调用的最简单方式是使用XML-RPC。下面我们演示一下一个实现了键-值存储功能的简单服务器: |
| 16 | + |
| 17 | +.. code-block:: python |
| 18 | +
|
| 19 | + from xmlrpc.server import SimpleXMLRPCServer |
| 20 | +
|
| 21 | + class KeyValueServer: |
| 22 | + _rpc_methods_ = ['get', 'set', 'delete', 'exists', 'keys'] |
| 23 | + def __init__(self, address): |
| 24 | + self._data = {} |
| 25 | + self._serv = SimpleXMLRPCServer(address, allow_none=True) |
| 26 | + for name in self._rpc_methods_: |
| 27 | + self._serv.register_function(getattr(self, name)) |
| 28 | +
|
| 29 | + def get(self, name): |
| 30 | + return self._data[name] |
| 31 | +
|
| 32 | + def set(self, name, value): |
| 33 | + self._data[name] = value |
| 34 | +
|
| 35 | + def delete(self, name): |
| 36 | + del self._data[name] |
| 37 | +
|
| 38 | + def exists(self, name): |
| 39 | + return name in self._data |
| 40 | +
|
| 41 | + def keys(self): |
| 42 | + return list(self._data) |
| 43 | +
|
| 44 | + def serve_forever(self): |
| 45 | + self._serv.serve_forever() |
| 46 | +
|
| 47 | + # Example |
| 48 | + if __name__ == '__main__': |
| 49 | + kvserv = KeyValueServer(('', 15000)) |
| 50 | + kvserv.serve_forever() |
| 51 | +
|
| 52 | +下面我们从一个客户端机器上面来访问服务器: |
| 53 | + |
| 54 | +.. code-block:: python |
| 55 | +
|
| 56 | + >>> from xmlrpc.client import ServerProxy |
| 57 | + >>> s = ServerProxy('http://localhost:15000', allow_none=True) |
| 58 | + >>> s.set('foo', 'bar') |
| 59 | + >>> s.set('spam', [1, 2, 3]) |
| 60 | + >>> s.keys() |
| 61 | + ['spam', 'foo'] |
| 62 | + >>> s.get('foo') |
| 63 | + 'bar' |
| 64 | + >>> s.get('spam') |
| 65 | + [1, 2, 3] |
| 66 | + >>> s.delete('spam') |
| 67 | + >>> s.exists('spam') |
| 68 | + False |
| 69 | + >>> |
69 | 70 |
|
70 | 71 | |
|
71 | 72 |
|
72 | 73 | ----------
|
73 | 74 | 讨论
|
74 | 75 | ----------
|
75 |
| -XML-RPC can be an extremely easy way to set up a simple remote procedure call service. |
76 |
| -All you need to do is create a server instance, register functions with it using the regis |
77 |
| -ter_function() method, and then launch it using the serve_forever() method. This |
78 |
| -recipe packages it up into a class to put all of the code together, but there is no such |
79 |
| -requirement. For example, you could create a server by trying something like this: |
80 |
| - |
81 |
| -from xmlrpc.server import SimpleXMLRPCServer |
82 |
| -def add(x,y): |
83 |
| - return x+y |
84 |
| - |
85 |
| -serv = SimpleXMLRPCServer(('', 15000)) |
86 |
| -serv.register_function(add) |
87 |
| -serv.serve_forever() |
88 |
| - |
89 |
| -Functions exposed via XML-RPC only work with certain kinds of data such as strings, |
90 |
| -numbers, lists, and dictionaries. For everything else, some study is required. For in‐ |
91 |
| -stance, if you pass an instance through XML-RPC, only its instance dictionary is |
92 |
| -handled: |
93 |
| - |
94 |
| ->>> class Point: |
95 |
| -... def __init__(self, x, y): |
96 |
| -... self.x = x |
97 |
| -... self.y = y |
98 |
| -... |
99 |
| ->>> p = Point(2, 3) |
100 |
| ->>> s.set('foo', p) |
101 |
| ->>> s.get('foo') |
102 |
| -{'x': 2, 'y': 3} |
103 |
| ->>> |
104 |
| - |
105 |
| -Similarly, handling of binary data is a bit different than you expect: |
106 |
| - |
107 |
| ->>> s.set('foo', b'Hello World') |
108 |
| ->>> s.get('foo') |
109 |
| -<xmlrpc.client.Binary object at 0x10131d410> |
110 |
| - |
111 |
| ->>> _.data |
112 |
| -b'Hello World' |
113 |
| ->>> |
114 |
| - |
115 |
| -As a general rule, you probably shouldn’t expose an XML-RPC service to the rest of the |
116 |
| -world as a public API. It often works best on internal networks where you might want |
117 |
| -to write simple distributed programs involving a few different machines. |
118 |
| -A downside to XML-RPC is its performance. The SimpleXMLRPCServer implementa‐ |
119 |
| -tion is only single threaded, and wouldn’t be appropriate for scaling a large application, |
120 |
| -although it can be made to run multithreaded, as shown in Recipe 11.2. Also, since |
121 |
| -XML-RPC serializes all data as XML, it’s inherently slower than other approaches. |
122 |
| -However, one benefit of this encoding is that it’s understood by a variety of other pro‐ |
123 |
| -gramming languages. By using it, clients written in languages other than Python will be |
124 |
| -able to access your service. |
125 |
| -Despite its limitations, XML-RPC is worth knowing about if you ever have the need to |
126 |
| -make a quick and dirty remote procedure call system. Oftentimes, the simple solution |
127 |
| -is good enough. |
| 76 | +XML-RPC 可以让我们很容易的构造一个简单的远程调用服务。你所需要做的仅仅是创建一个服务器实例, |
| 77 | +通过它的方法 ``register_function()`` 来注册函数,然后使用方法 ``serve_forever()`` 启动它。 |
| 78 | +在上面我们将这些步骤放在一起写到一个类中,不够这并不是必须的。比如你还可以像下面这样创建一个服务器: |
| 79 | + |
| 80 | +.. code-block:: python |
| 81 | +
|
| 82 | + from xmlrpc.server import SimpleXMLRPCServer |
| 83 | + def add(x,y): |
| 84 | + return x+y |
| 85 | +
|
| 86 | + serv = SimpleXMLRPCServer(('', 15000)) |
| 87 | + serv.register_function(add) |
| 88 | + serv.serve_forever() |
| 89 | +
|
| 90 | +XML-RPC暴露出来的函数只能适用于部分数据类型,比如字符串、整形、列表和字典。 |
| 91 | +对于其他类型就得需要做些额外的功课了。 |
| 92 | +例如,如果你想通过 XML-RPC 传递一个对象实例,实际上只有他的实例字典被处理: |
| 93 | + |
| 94 | +.. code-block:: python |
| 95 | +
|
| 96 | + >>> class Point: |
| 97 | + ... def __init__(self, x, y): |
| 98 | + ... self.x = x |
| 99 | + ... self.y = y |
| 100 | + ... |
| 101 | + >>> p = Point(2, 3) |
| 102 | + >>> s.set('foo', p) |
| 103 | + >>> s.get('foo') |
| 104 | + {'x': 2, 'y': 3} |
| 105 | + >>> |
| 106 | +
|
| 107 | +类似的,对于二进制数据的处理也跟你想象的不太一样: |
| 108 | + |
| 109 | +.. code-block:: python |
| 110 | +
|
| 111 | + >>> s.set('foo', b'Hello World') |
| 112 | + >>> s.get('foo') |
| 113 | + <xmlrpc.client.Binary object at 0x10131d410> |
| 114 | +
|
| 115 | + >>> _.data |
| 116 | + b'Hello World' |
| 117 | + >>> |
| 118 | +
|
| 119 | +一般来讲,你不应该将 XML-RPC 服务以公共API的方式暴露出来。 |
| 120 | +对于这种情况,通常分布式应用程序会是一个更好的选择。 |
| 121 | + |
| 122 | +XML-RPC的一个缺点是它的性能。``SimpleXMLRPCServer`` 的实现是单线程的, |
| 123 | +所以它不适合于大型程序,尽管我们在11.2小节中演示过它是可以通过多线程来执行的。 |
| 124 | +另外,由于 XML-RPC 将所有数据都序列化为XML格式,所以它会比其他的方式运行的慢一些。 |
| 125 | +但是它也有优点,这种方式的编码可以被绝大部分其他编程语言支持。 |
| 126 | +通过使用这种方式,其他语言的客户端程序都能访问你的服务。 |
| 127 | + |
| 128 | +虽然XML-RPC有很多缺点,但是如果你需要快速构建一个简单远程过程调用系统的话,它仍然值得去学习的。 |
| 129 | +有时候,简单的方案就已经足够了。 |
0 commit comments