|
@@
-1,271
+1,266
b''
|
|
1
|
.. _parallel_messages:
|
|
1
|
.. _parallel_messages:
|
|
2
|
|
|
2
|
|
|
3
|
Messaging for Parallel Computing
|
|
3
|
Messaging for Parallel Computing
|
|
4
|
================================
|
|
4
|
================================
|
|
5
|
|
|
5
|
|
|
6
|
This is an extension of the :ref:`messaging <messaging>` doc. Diagrams of the connections
|
|
6
|
This is an extension of the :ref:`messaging <messaging>` doc. Diagrams of the connections
|
|
7
|
can be found in the :ref:`parallel connections <parallel_connections>` doc.
|
|
7
|
can be found in the :ref:`parallel connections <parallel_connections>` doc.
|
|
8
|
|
|
8
|
|
|
9
|
|
|
9
|
|
|
10
|
ZMQ messaging is also used in the parallel computing IPython system. All messages to/from
|
|
10
|
ZMQ messaging is also used in the parallel computing IPython system. All messages to/from
|
|
11
|
kernels remain the same as the single kernel model, and are forwarded through a ZMQ Queue
|
|
11
|
kernels remain the same as the single kernel model, and are forwarded through a ZMQ Queue
|
|
12
|
device. The controller receives all messages and replies in these channels, and saves
|
|
12
|
device. The controller receives all messages and replies in these channels, and saves
|
|
13
|
results for future use.
|
|
13
|
results for future use.
|
|
14
|
|
|
14
|
|
|
15
|
The Controller
|
|
15
|
The Controller
|
|
16
|
--------------
|
|
16
|
--------------
|
|
17
|
|
|
17
|
|
|
18
|
The controller is the central process of the IPython parallel computing model. It has 3
|
|
18
|
The controller is the central process of the IPython parallel computing model. It has 3
|
|
19
|
Devices:
|
|
19
|
Devices:
|
|
20
|
|
|
20
|
|
|
21
|
* Heartbeater
|
|
21
|
* Heartbeater
|
|
22
|
* Multiplexed Queue
|
|
22
|
* Multiplexed Queue
|
|
23
|
* Task Queue
|
|
23
|
* Task Queue
|
|
24
|
|
|
24
|
|
|
25
|
and 3 sockets:
|
|
25
|
and 3 sockets:
|
|
26
|
|
|
26
|
|
|
27
|
* ``XREP`` for both engine and client registration
|
|
27
|
* ``XREP`` for both engine and client registration
|
|
28
|
* ``PUB`` for notification of engine changes
|
|
28
|
* ``PUB`` for notification of engine changes
|
|
29
|
* ``XREP`` for client requests
|
|
29
|
* ``XREP`` for client requests
|
|
30
|
|
|
30
|
|
|
31
|
|
|
31
|
|
|
32
|
|
|
32
|
|
|
33
|
Registration (``XREP``)
|
|
33
|
Registration (``XREP``)
|
|
34
|
***********************
|
|
34
|
***********************
|
|
35
|
|
|
35
|
|
|
36
|
The first function of the Controller is to facilitate and monitor connections of clients
|
|
36
|
The first function of the Controller is to facilitate and monitor connections of clients
|
|
37
|
and engines. Both client and engine registration are handled by the same socket, so only
|
|
37
|
and engines. Both client and engine registration are handled by the same socket, so only
|
|
38
|
one ip/port pair is needed to connect any number of connections and clients.
|
|
38
|
one ip/port pair is needed to connect any number of connections and clients.
|
|
39
|
|
|
39
|
|
|
40
|
Engines register with the ``zmq.IDENTITY`` of their two ``XREQ`` sockets, one for the
|
|
40
|
Engines register with the ``zmq.IDENTITY`` of their two ``XREQ`` sockets, one for the
|
|
41
|
queue, which receives execute requests, and one for the heartbeat, which is used to
|
|
41
|
queue, which receives execute requests, and one for the heartbeat, which is used to
|
|
42
|
monitor the survival of the Engine process.
|
|
42
|
monitor the survival of the Engine process.
|
|
43
|
|
|
43
|
|
|
44
|
Message type: ``registration_request``::
|
|
44
|
Message type: ``registration_request``::
|
|
45
|
|
|
45
|
|
|
46
|
content = {
|
|
46
|
content = {
|
|
47
|
'queue' : 'abcd-1234-...', # the queue XREQ id
|
|
47
|
'queue' : 'abcd-1234-...', # the queue XREQ id
|
|
48
|
'heartbeat' : '1234-abcd-...' # the heartbeat XREQ id
|
|
48
|
'heartbeat' : '1234-abcd-...' # the heartbeat XREQ id
|
|
49
|
}
|
|
49
|
}
|
|
50
|
|
|
50
|
|
|
51
|
The Controller replies to an Engine's registration request with the engine's integer ID,
|
|
51
|
The Controller replies to an Engine's registration request with the engine's integer ID,
|
|
52
|
and all the remaining connection information for connecting the heartbeat process, and
|
|
52
|
and all the remaining connection information for connecting the heartbeat process, and
|
|
53
|
kernel queue socket(s). The message status will be an error if the Engine requests IDs that
|
|
53
|
kernel queue socket(s). The message status will be an error if the Engine requests IDs that
|
|
54
|
already in use.
|
|
54
|
already in use.
|
|
55
|
|
|
55
|
|
|
56
|
Message type: ``registration_reply``::
|
|
56
|
Message type: ``registration_reply``::
|
|
57
|
|
|
57
|
|
|
58
|
content = {
|
|
58
|
content = {
|
|
59
|
'status' : 'ok', # or 'error'
|
|
59
|
'status' : 'ok', # or 'error'
|
|
60
|
# if ok:
|
|
60
|
# if ok:
|
|
61
|
'id' : 0, # int, the engine id
|
|
61
|
'id' : 0, # int, the engine id
|
|
62
|
'queue' : 'tcp://127.0.0.1:12345', # connection for engine side of the queue
|
|
62
|
'queue' : 'tcp://127.0.0.1:12345', # connection for engine side of the queue
|
|
63
|
'control' : 'tcp://...', # addr for control queue
|
|
63
|
'control' : 'tcp://...', # addr for control queue
|
|
64
|
'heartbeat' : (a,b), # tuple containing two interfaces needed for heartbeat
|
|
64
|
'heartbeat' : (a,b), # tuple containing two interfaces needed for heartbeat
|
|
65
|
'task' : 'tcp://...', # addr for task queue, or None if no task queue running
|
|
65
|
'task' : 'tcp://...', # addr for task queue, or None if no task queue running
|
|
66
|
# if error:
|
|
|
|
|
67
|
'reason' : 'queue_id already registered'
|
|
|
|
|
68
|
}
|
|
66
|
}
|
|
69
|
|
|
67
|
|
|
70
|
Clients use the same socket as engines to start their connections. Connection requests
|
|
68
|
Clients use the same socket as engines to start their connections. Connection requests
|
|
71
|
from clients need no information:
|
|
69
|
from clients need no information:
|
|
72
|
|
|
70
|
|
|
73
|
Message type: ``connection_request``::
|
|
71
|
Message type: ``connection_request``::
|
|
74
|
|
|
72
|
|
|
75
|
content = {}
|
|
73
|
content = {}
|
|
76
|
|
|
74
|
|
|
77
|
The reply to a Client registration request contains the connection information for the
|
|
75
|
The reply to a Client registration request contains the connection information for the
|
|
78
|
multiplexer and load balanced queues, as well as the address for direct controller
|
|
76
|
multiplexer and load balanced queues, as well as the address for direct controller
|
|
79
|
queries. If any of these addresses is `None`, that functionality is not available.
|
|
77
|
queries. If any of these addresses is `None`, that functionality is not available.
|
|
80
|
|
|
78
|
|
|
81
|
Message type: ``connection_reply``::
|
|
79
|
Message type: ``connection_reply``::
|
|
82
|
|
|
80
|
|
|
83
|
content = {
|
|
81
|
content = {
|
|
84
|
'status' : 'ok', # or 'error'
|
|
82
|
'status' : 'ok', # or 'error'
|
|
85
|
# if ok:
|
|
83
|
# if ok:
|
|
86
|
'queue' : 'tcp://127.0.0.1:12345', # connection for client side of the MUX queue
|
|
84
|
'queue' : 'tcp://127.0.0.1:12345', # connection for client side of the MUX queue
|
|
87
|
'task' : 'tcp...', # addr for task queue, or None if no task queue running
|
|
85
|
'task' : 'tcp...', # addr for task queue, or None if no task queue running
|
|
88
|
'query' : 'tcp...' # addr for methods to query the controller, like queue_request, etc.
|
|
86
|
'query' : 'tcp...' # addr for methods to query the controller, like queue_request, etc.
|
|
89
|
'control' : 'tcp...' # addr for control methods, like abort, etc.
|
|
87
|
'control' : 'tcp...' # addr for control methods, like abort, etc.
|
|
90
|
}
|
|
88
|
}
|
|
91
|
|
|
89
|
|
|
92
|
Heartbeat
|
|
90
|
Heartbeat
|
|
93
|
*********
|
|
91
|
*********
|
|
94
|
|
|
92
|
|
|
95
|
The controller uses a heartbeat system to monitor engines, and track when they become
|
|
93
|
The controller uses a heartbeat system to monitor engines, and track when they become
|
|
96
|
unresponsive. As described in :ref:`messages <messages>`, and shown in :ref:`connections
|
|
94
|
unresponsive. As described in :ref:`messages <messages>`, and shown in :ref:`connections
|
|
97
|
<parallel_connections>`.
|
|
95
|
<parallel_connections>`.
|
|
98
|
|
|
96
|
|
|
99
|
Notification (``PUB``)
|
|
97
|
Notification (``PUB``)
|
|
100
|
**********************
|
|
98
|
**********************
|
|
101
|
|
|
99
|
|
|
102
|
The controller published all engine registration/unregistration events on a PUB socket.
|
|
100
|
The controller published all engine registration/unregistration events on a PUB socket.
|
|
103
|
This allows clients to have up-to-date engine ID sets without polling. Registration
|
|
101
|
This allows clients to have up-to-date engine ID sets without polling. Registration
|
|
104
|
notifications contain both the integer engine ID and the queue ID, which is necessary for
|
|
102
|
notifications contain both the integer engine ID and the queue ID, which is necessary for
|
|
105
|
sending messages via the Multiplexer Queue.
|
|
103
|
sending messages via the Multiplexer Queue.
|
|
106
|
|
|
104
|
|
|
107
|
Message type: ``registration_notification``::
|
|
105
|
Message type: ``registration_notification``::
|
|
108
|
|
|
106
|
|
|
109
|
content = {
|
|
107
|
content = {
|
|
110
|
'id' : 0, # engine ID that has been registered
|
|
108
|
'id' : 0, # engine ID that has been registered
|
|
111
|
'queue' : 'engine_id' # the IDENT for the engine's queue
|
|
109
|
'queue' : 'engine_id' # the IDENT for the engine's queue
|
|
112
|
}
|
|
110
|
}
|
|
113
|
|
|
111
|
|
|
114
|
Message type : ``unregistration_notification``::
|
|
112
|
Message type : ``unregistration_notification``::
|
|
115
|
|
|
113
|
|
|
116
|
content = {
|
|
114
|
content = {
|
|
117
|
'id' : 0 # engine ID that has been unregistered
|
|
115
|
'id' : 0 # engine ID that has been unregistered
|
|
118
|
}
|
|
116
|
}
|
|
119
|
|
|
117
|
|
|
120
|
|
|
118
|
|
|
121
|
Client Queries (``XREP``)
|
|
119
|
Client Queries (``XREP``)
|
|
122
|
*************************
|
|
120
|
*************************
|
|
123
|
|
|
121
|
|
|
124
|
The controller monitors and logs all queue traffic, so that clients can retrieve past
|
|
122
|
The controller monitors and logs all queue traffic, so that clients can retrieve past
|
|
125
|
results or monitor pending tasks. Currently, this information resides in memory on the
|
|
123
|
results or monitor pending tasks. Currently, this information resides in memory on the
|
|
126
|
Controller, but will ultimately be offloaded to a database over an additional ZMQ
|
|
124
|
Controller, but will ultimately be offloaded to a database over an additional ZMQ
|
|
127
|
connection. The interface should remain the same or at least similar.
|
|
125
|
connection. The interface should remain the same or at least similar.
|
|
128
|
|
|
126
|
|
|
129
|
:func:`queue_request` requests can specify multiple engines to query via the `targets`
|
|
127
|
:func:`queue_request` requests can specify multiple engines to query via the `targets`
|
|
130
|
element. A verbose flag can be passed, to determine whether the result should be the list
|
|
128
|
element. A verbose flag can be passed, to determine whether the result should be the list
|
|
131
|
of `msg_ids` in the queue or simply the length of each list.
|
|
129
|
of `msg_ids` in the queue or simply the length of each list.
|
|
132
|
|
|
130
|
|
|
133
|
Message type: ``queue_request``::
|
|
131
|
Message type: ``queue_request``::
|
|
134
|
|
|
132
|
|
|
135
|
content = {
|
|
133
|
content = {
|
|
136
|
'verbose' : True, # whether return should be lists themselves or just lens
|
|
134
|
'verbose' : True, # whether return should be lists themselves or just lens
|
|
137
|
'targets' : [0,3,1] # list of ints
|
|
135
|
'targets' : [0,3,1] # list of ints
|
|
138
|
}
|
|
136
|
}
|
|
139
|
|
|
137
|
|
|
140
|
The content of a reply to a :func:queue_request request is a dict, keyed by the engine
|
|
138
|
The content of a reply to a :func:queue_request request is a dict, keyed by the engine
|
|
141
|
IDs. Note that they will be the string representation of the integer keys, since JSON
|
|
139
|
IDs. Note that they will be the string representation of the integer keys, since JSON
|
|
142
|
cannot handle number keys.
|
|
140
|
cannot handle number keys.
|
|
143
|
|
|
141
|
|
|
144
|
Message type: ``queue_reply``::
|
|
142
|
Message type: ``queue_reply``::
|
|
145
|
|
|
143
|
|
|
146
|
content = {
|
|
144
|
content = {
|
|
147
|
'0' : {'completed' : 1, 'queue' : 7},
|
|
145
|
'0' : {'completed' : 1, 'queue' : 7},
|
|
148
|
'1' : {'completed' : 10, 'queue' : 1}
|
|
146
|
'1' : {'completed' : 10, 'queue' : 1}
|
|
149
|
}
|
|
147
|
}
|
|
150
|
|
|
148
|
|
|
151
|
Clients can request individual results directly from the controller. This is primarily for
|
|
149
|
Clients can request individual results directly from the controller. This is primarily for
|
|
152
|
use gathering results of executions not submitted by the particular client, as the client
|
|
150
|
use gathering results of executions not submitted by the particular client, as the client
|
|
153
|
will have all its own results already. Requests are made by msg_id, and can contain one or
|
|
151
|
will have all its own results already. Requests are made by msg_id, and can contain one or
|
|
154
|
more msg_id.
|
|
152
|
more msg_id.
|
|
155
|
|
|
153
|
|
|
156
|
Message type: ``result_request``::
|
|
154
|
Message type: ``result_request``::
|
|
157
|
|
|
155
|
|
|
158
|
content = {
|
|
156
|
content = {
|
|
159
|
'msg_ids' : ['uuid','...'] # list of strs
|
|
157
|
'msg_ids' : ['uuid','...'] # list of strs
|
|
160
|
}
|
|
158
|
}
|
|
161
|
|
|
159
|
|
|
162
|
The :func:`result_request` reply contains the content objects of the actual execution
|
|
160
|
The :func:`result_request` reply contains the content objects of the actual execution
|
|
163
|
reply messages
|
|
161
|
reply messages
|
|
164
|
|
|
162
|
|
|
165
|
|
|
163
|
|
|
166
|
Message type: ``result_reply``::
|
|
164
|
Message type: ``result_reply``::
|
|
167
|
|
|
165
|
|
|
168
|
content = {
|
|
166
|
content = {
|
|
169
|
'status' : 'ok', # else error
|
|
167
|
'status' : 'ok', # else error
|
|
170
|
# if ok:
|
|
168
|
# if ok:
|
|
171
|
msg_id : msg, # the content dict is keyed by msg_ids,
|
|
169
|
msg_id : msg, # the content dict is keyed by msg_ids,
|
|
172
|
# values are the result messages
|
|
170
|
# values are the result messages
|
|
173
|
'pending' : ['msg_id','...'], # msg_ids still pending
|
|
171
|
'pending' : ['msg_id','...'], # msg_ids still pending
|
|
174
|
'completed' : ['msg_id','...'], # list of completed msg_ids
|
|
172
|
'completed' : ['msg_id','...'], # list of completed msg_ids
|
|
175
|
# if error:
|
|
|
|
|
176
|
'reason' : "explanation"
|
|
|
|
|
177
|
}
|
|
173
|
}
|
|
178
|
|
|
174
|
|
|
179
|
For memory management purposes, Clients can also instruct the controller to forget the
|
|
175
|
For memory management purposes, Clients can also instruct the controller to forget the
|
|
180
|
results of messages. This can be done by message ID or engine ID. Individual messages are
|
|
176
|
results of messages. This can be done by message ID or engine ID. Individual messages are
|
|
181
|
dropped by msg_id, and all messages completed on an engine are dropped by engine ID. This will likely no longer
|
|
177
|
dropped by msg_id, and all messages completed on an engine are dropped by engine ID. This will likely no longer
|
|
182
|
be necessary once we move to a DB-based message logging backend.
|
|
178
|
be necessary once we move to a DB-based message logging backend.
|
|
183
|
|
|
179
|
|
|
184
|
If the msg_ids element is the string ``'all'`` instead of a list, then all completed
|
|
180
|
If the msg_ids element is the string ``'all'`` instead of a list, then all completed
|
|
185
|
results are forgotten.
|
|
181
|
results are forgotten.
|
|
186
|
|
|
182
|
|
|
187
|
Message type: ``purge_request``::
|
|
183
|
Message type: ``purge_request``::
|
|
188
|
|
|
184
|
|
|
189
|
content = {
|
|
185
|
content = {
|
|
190
|
'msg_ids' : ['id1', 'id2',...], # list of msg_ids or 'all'
|
|
186
|
'msg_ids' : ['id1', 'id2',...], # list of msg_ids or 'all'
|
|
191
|
'engine_ids' : [0,2,4] # list of engine IDs
|
|
187
|
'engine_ids' : [0,2,4] # list of engine IDs
|
|
192
|
}
|
|
188
|
}
|
|
193
|
|
|
189
|
|
|
194
|
The reply to a purge request is simply the status 'ok' if the request succeeded, or an
|
|
190
|
The reply to a purge request is simply the status 'ok' if the request succeeded, or an
|
|
195
|
explanation of why it failed, such as requesting the purge of a nonexistent or pending
|
|
191
|
explanation of why it failed, such as requesting the purge of a nonexistent or pending
|
|
196
|
message.
|
|
192
|
message.
|
|
197
|
|
|
193
|
|
|
198
|
Message type: ``purge_reply``::
|
|
194
|
Message type: ``purge_reply``::
|
|
199
|
|
|
195
|
|
|
200
|
content = {
|
|
196
|
content = {
|
|
201
|
'status' : 'ok', # or 'error'
|
|
197
|
'status' : 'ok', # or 'error'
|
|
202
|
|
|
|
|
|
203
|
# if error:
|
|
|
|
|
204
|
'reason' : "KeyError: no such msg_id 'whoda'"
|
|
|
|
|
205
|
}
|
|
198
|
}
|
|
206
|
|
|
199
|
|
|
207
|
:func:`apply` and :func:`apply_bound`
|
|
200
|
:func:`apply` and :func:`apply_bound`
|
|
208
|
*************************************
|
|
201
|
*************************************
|
|
209
|
|
|
202
|
|
|
210
|
The `Namespace <http://gist.github.com/483294>`_ model suggests that execution be able to
|
|
203
|
The `Namespace <http://gist.github.com/483294>`_ model suggests that execution be able to
|
|
211
|
use the model::
|
|
204
|
use the model::
|
|
212
|
|
|
205
|
|
|
213
|
client.apply(f, *args, **kwargs)
|
|
206
|
ns.apply(f, *args, **kwargs)
|
|
214
|
|
|
207
|
|
|
215
|
which takes `f`, a function in the user's namespace, and executes ``f(*args, **kwargs)``
|
|
208
|
which takes `f`, a function in the user's namespace, and executes ``f(*args, **kwargs)``
|
|
216
|
on a remote engine, returning the result (or, for non-blocking, information facilitating
|
|
209
|
on a remote engine, returning the result (or, for non-blocking, information facilitating
|
|
217
|
later retrieval of the result). This model, unlike the execute message which just uses a
|
|
210
|
later retrieval of the result). This model, unlike the execute message which just uses a
|
|
218
|
code string, must be able to send arbitrary (pickleable) Python objects. And ideally, copy
|
|
211
|
code string, must be able to send arbitrary (pickleable) Python objects. And ideally, copy
|
|
219
|
as little data as we can. The `buffers` property of a Message was introduced for this
|
|
212
|
as little data as we can. The `buffers` property of a Message was introduced for this
|
|
220
|
purpose.
|
|
213
|
purpose.
|
|
221
|
|
|
214
|
|
|
222
|
Utility method :func:`build_apply_message` in :mod:`IPython.zmq.streamsession` wraps a
|
|
215
|
Utility method :func:`build_apply_message` in :mod:`IPython.zmq.streamsession` wraps a
|
|
223
|
function signature and builds the correct buffer format for minimal data copying (exactly
|
|
216
|
function signature and builds a sendable buffer format for minimal data copying (exactly
|
|
224
|
zero copies of numpy array data).
|
|
217
|
zero copies of numpy array data or buffers or large strings).
|
|
225
|
|
|
218
|
|
|
226
|
Message type: ``apply_request``::
|
|
219
|
Message type: ``apply_request``::
|
|
227
|
|
|
220
|
|
|
228
|
content = {
|
|
221
|
content = {
|
|
229
|
'bound' : True, # whether to execute in the engine's namespace or unbound
|
|
222
|
'bound' : True, # whether to execute in the engine's namespace or unbound
|
|
230
|
'after' : [msg_ids,...], # list of msg_ids or output of Dependency.as_dict()
|
|
223
|
'after' : [msg_ids,...], # list of msg_ids or output of Dependency.as_dict()
|
|
231
|
'follow' : [msg_ids,...], # list of msg_ids or output of Dependency.as_dict()
|
|
224
|
'follow' : [msg_ids,...], # list of msg_ids or output of Dependency.as_dict()
|
|
232
|
|
|
225
|
|
|
233
|
}
|
|
226
|
}
|
|
234
|
buffers = ['...'] # at least 3 in length
|
|
227
|
buffers = ['...'] # at least 3 in length
|
|
235
|
# as built by build_apply_message(f,args,kwargs)
|
|
228
|
# as built by build_apply_message(f,args,kwargs)
|
|
236
|
|
|
229
|
|
|
|
|
|
230
|
after/follow represent task dependencies
|
|
|
|
|
231
|
|
|
237
|
Message type: ``apply_reply``::
|
|
232
|
Message type: ``apply_reply``::
|
|
238
|
|
|
233
|
|
|
239
|
content = {
|
|
234
|
content = {
|
|
240
|
'status' : 'ok' # 'ok' or 'error'
|
|
235
|
'status' : 'ok' # 'ok' or 'error'
|
|
241
|
# other error info here, as in other messages
|
|
236
|
# other error info here, as in other messages
|
|
242
|
}
|
|
237
|
}
|
|
243
|
buffers = ['...'] # either 1 or 2 in length
|
|
238
|
buffers = ['...'] # either 1 or 2 in length
|
|
244
|
# a serialization of the return value of f(*args,**kwargs)
|
|
239
|
# a serialization of the return value of f(*args,**kwargs)
|
|
245
|
# only populated if status is 'ok'
|
|
240
|
# only populated if status is 'ok'
|
|
246
|
|
|
241
|
|
|
247
|
|
|
242
|
|
|
248
|
|
|
243
|
|
|
249
|
|
|
244
|
|
|
250
|
Implementation
|
|
245
|
Implementation
|
|
251
|
--------------
|
|
246
|
--------------
|
|
252
|
|
|
247
|
|
|
253
|
There are a few differences in implementation between the `StreamSession` object used in
|
|
248
|
There are a few differences in implementation between the `StreamSession` object used in
|
|
254
|
the parallel computing fork and the `Session` object, the main one being that messages are
|
|
249
|
the parallel computing fork and the `Session` object, the main one being that messages are
|
|
255
|
sent in parts, rather than as a single serialized object. `StreamSession` objects also
|
|
250
|
sent in parts, rather than as a single serialized object. `StreamSession` objects also
|
|
256
|
take pack/unpack functions, which are to be used when serializing/deserializing objects.
|
|
251
|
take pack/unpack functions, which are to be used when serializing/deserializing objects.
|
|
257
|
These can be any functions that translate to/from formats that ZMQ sockets can send
|
|
252
|
These can be any functions that translate to/from formats that ZMQ sockets can send
|
|
258
|
(buffers,bytes, etc.).
|
|
253
|
(buffers,bytes, etc.).
|
|
259
|
|
|
254
|
|
|
260
|
Split Sends
|
|
255
|
Split Sends
|
|
261
|
***********
|
|
256
|
***********
|
|
262
|
|
|
257
|
|
|
263
|
Previously, messages were bundled as a single json object and one call to
|
|
258
|
Previously, messages were bundled as a single json object and one call to
|
|
264
|
:func:`socket.send_json`. Since the controller inspects all messages, and doesn't need to
|
|
259
|
:func:`socket.send_json`. Since the controller inspects all messages, and doesn't need to
|
|
265
|
see the content of the messages, which can be large, messages are now serialized and sent in
|
|
260
|
see the content of the messages, which can be large, messages are now serialized and sent in
|
|
266
|
pieces. All messages are sent in at least 3 parts: the header, the parent header, and the
|
|
261
|
pieces. All messages are sent in at least 3 parts: the header, the parent header, and the
|
|
267
|
content. This allows the controller to unpack and inspect the (always small) header,
|
|
262
|
content. This allows the controller to unpack and inspect the (always small) header,
|
|
268
|
without spending time unpacking the content unless the message is bound for the
|
|
263
|
without spending time unpacking the content unless the message is bound for the
|
|
269
|
controller. Buffers are added on to the end of the message, and can be any objects that
|
|
264
|
controller. Buffers are added on to the end of the message, and can be any objects that
|
|
270
|
present the buffer interface.
|
|
265
|
present the buffer interface.
|
|
271
|
|
|
266
|
|