1ZGOSSIP(3) CZMQ Manual ZGOSSIP(3)
2
3
4
6 zgossip - Class for decentralized configuration management
7
9 // To work with zgossip, use the CZMQ zactor API:
10 //
11 // Create new zgossip instance, passing logging prefix:
12 //
13 // zactor_t *zgossip = zactor_new (zgossip, "myname");
14 //
15 // Destroy zgossip instance
16 //
17 // zactor_destroy (&zgossip);
18 //
19 // Enable verbose logging of commands and activity:
20 //
21 // zstr_send (zgossip, "VERBOSE");
22 //
23 // Bind zgossip to specified endpoint. TCP endpoints may specify
24 // the port number as "*" to acquire an ephemeral port:
25 //
26 // zstr_sendx (zgossip, "BIND", endpoint, NULL);
27 //
28 // Return assigned port number, specifically when BIND was done using an
29 // an ephemeral port:
30 //
31 // zstr_sendx (zgossip, "PORT", NULL);
32 // char *command, *port_str;
33 // zstr_recvx (zgossip, &command, &port_str, NULL);
34 // assert (streq (command, "PORT"));
35 //
36 // Specify configuration file to load, overwriting any previous loaded
37 // configuration file or options:
38 //
39 // zstr_sendx (zgossip, "LOAD", filename, NULL);
40 //
41 // Set configuration path value:
42 //
43 // zstr_sendx (zgossip, "SET", path, value, NULL);
44 //
45 // Save configuration data to config file on disk:
46 //
47 // zstr_sendx (zgossip, "SAVE", filename, NULL);
48 //
49 // Send zmsg_t instance to zgossip:
50 //
51 // zactor_send (zgossip, &msg);
52 //
53 // Receive zmsg_t instance from zgossip:
54 //
55 // zmsg_t *msg = zactor_recv (zgossip);
56 //
57 // This is the zgossip constructor as a zactor_fn:
58 //
59 CZMQ_EXPORT void
60 zgossip (zsock_t *pipe, void *args);
61
62 // Self test of this class
63 CZMQ_EXPORT void
64 zgossip_test (bool verbose);
65 Please add '@interface' section in './../src/zgossip.c'.
66
68 Implements a gossip protocol for decentralized configuration
69 management. Your applications nodes form a loosely connected network
70 (which can have cycles), and publish name/value tuples. Each node
71 re-distributes the new tuples it receives, so that the entire network
72 eventually achieves a consistent state. The current design does not
73 expire tuples.
74
75 Provides these commands (sent as multipart strings to the actor):
76
77 · BIND endpoint — binds the gossip service to specified endpoint
78
79 · PORT — returns the last TCP port, if any, used for binding
80
81 · LOAD configfile — load configuration from specified file
82
83 · SET configpath value — set configuration path = value
84
85 · SAVE configfile — save configuration to specified file
86
87 · CONNECT endpoint — connect the gossip service to the specified peer
88
89 · PUBLISH key value — publish a key/value pair to the gossip cluster
90
91 · STATUS — return number of key/value pairs held by gossip service
92
93 · ZAP DOMAIN domain — set the ZAP DOMAIN domain = value
94
95 Returns these messages:
96
97 · PORT number — reply to PORT command
98
99 · STATUS number — reply to STATUS command
100
101 · DELIVER key value — new tuple delivered from network
102
103 The gossip protocol distributes information around a loosely-connected
104 network of gossip services. The information consists of name/value
105 pairs published by applications at any point in the network. The goal
106 of the gossip protocol is to create eventual consistency between all
107 the using applications.
108
109 The name/value pairs (tuples) can be used for configuration data, for
110 status updates, for presence, or for discovery. When used for
111 discovery, the gossip protocol works as an alternative to e.g. UDP
112 beaconing.
113
114 The gossip network consists of a set of loosely-coupled nodes that
115 exchange tuples. Nodes can be connected across arbitrary transports, so
116 the gossip network can have nodes that communicate over inproc, over
117 IPC, and/or over TCP, at the same time.
118
119 Each node runs the same stack, which is a server-client hybrid using a
120 modified Harmony pattern (from Chapter 8 of the Guide):
121 http://zguide.zeromq.org/page:all#True-Peer-Connectivity-Harmony-Pattern
122
123 Each node provides a ROUTER socket that accepts client connections on
124 an key defined by the application via a BIND command. The state machine
125 for these connections is in zgossip.xml, and the generated code is in
126 zgossip_engine.inc.
127
128 Each node additionally creates outbound connections via DEALER sockets
129 to a set of servers ("remotes"), and under control of the calling app,
130 which sends CONNECT commands for each configured remote.
131
132 The messages between client and server are defined in zgossip_msg.xml.
133 We built this stack using the zeromq/zproto toolkit.
134
135 To join the gossip network, a node connects to one or more peers. Each
136 peer acts as a forwarder. This loosely-coupled network can scale to
137 thousands of nodes. However the gossip protocol is NOT designed to be
138 efficient, and should not be used for application data, as the same
139 tuples may be sent many times across the network.
140
141 The basic logic of the gossip service is to accept PUBLISH messages
142 from its owning application, and to forward these to every remote, and
143 every client it talks to. When a node gets a duplicate tuple, it throws
144 it away. When a node gets a new tuple, it stores it, and forwards it as
145 just described.
146
147 At present there is no way to expire tuples from the network.
148
149 The assumptions in this design are:
150
151 · The data set is slow-changing. Thus, the cost of the gossip
152 protocol is irrelevant with respect to other traffic.
153
155 From zgossip_test method.
156
157 // Test basic client-to-server operation of the protocol
158 zactor_t *server = zactor_new (zgossip, "server");
159 assert (server);
160 if (verbose)
161 zstr_send (server, "VERBOSE");
162 zstr_sendx (server, "BIND", "inproc://zgossip", NULL);
163
164 zsock_t *client = zsock_new (ZMQ_DEALER);
165 assert (client);
166 zsock_set_rcvtimeo (client, 2000);
167 int rc = zsock_connect (client, "inproc://zgossip");
168 assert (rc == 0);
169
170 // Send HELLO, which gets no message
171 zgossip_msg_t *message = zgossip_msg_new ();
172 zgossip_msg_set_id (message, ZGOSSIP_MSG_HELLO);
173 zgossip_msg_send (message, client);
174
175 // Send PING, expect PONG back
176 zgossip_msg_set_id (message, ZGOSSIP_MSG_PING);
177 zgossip_msg_send (message, client);
178 zgossip_msg_recv (message, client);
179 assert (zgossip_msg_id (message) == ZGOSSIP_MSG_PONG);
180 zgossip_msg_destroy (&message);
181
182 zactor_destroy (&server);
183 zsock_destroy (&client);
184
185 // Test peer-to-peer operations
186 zactor_t *base = zactor_new (zgossip, "base");
187 assert (base);
188 if (verbose)
189 zstr_send (base, "VERBOSE");
190 // Set a 100msec timeout on clients so we can test expiry
191 zstr_sendx (base, "SET", "server/timeout", "100", NULL);
192 zstr_sendx (base, "BIND", "inproc://base", NULL);
193
194 zactor_t *alpha = zactor_new (zgossip, "alpha");
195 assert (alpha);
196
197 if (verbose)
198 zstr_send (alpha, "VERBOSE");
199
200 zstr_sendx (alpha, "CONNECT", "inproc://base", NULL);
201
202 zstr_sendx (alpha, "PUBLISH", "inproc://alpha-1", "service1", NULL);
203 zstr_sendx (alpha, "PUBLISH", "inproc://alpha-2", "service2", NULL);
204
205 zactor_t *beta = zactor_new (zgossip, "beta");
206 assert (beta);
207
208 if (verbose)
209 zstr_send (beta, "VERBOSE");
210
211 zstr_sendx (beta, "CONNECT", "inproc://base", NULL);
212
213 zstr_sendx (beta, "PUBLISH", "inproc://beta-1", "service1", NULL);
214 zstr_sendx (beta, "PUBLISH", "inproc://beta-2", "service2", NULL);
215
216 // got nothing
217 zclock_sleep (200);
218
219 zstr_send (alpha, "STATUS");
220 char *command, *status, *key, *value;
221
222 zstr_recvx (alpha, &command, &key, &value, NULL);
223
224 assert (streq (command, "DELIVER"));
225 assert (streq (key, "inproc://alpha-1"));
226 assert (streq (value, "service1"));
227
228 zstr_free (&command);
229 zstr_free (&key);
230 zstr_free (&value);
231
232 zstr_recvx (alpha, &command, &key, &value, NULL);
233 assert (streq (command, "DELIVER"));
234 assert (streq (key, "inproc://alpha-2"));
235 assert (streq (value, "service2"));
236 zstr_free (&command);
237 zstr_free (&key);
238 zstr_free (&value);
239
240 zstr_recvx (alpha, &command, &key, &value, NULL);
241 assert (streq (command, "DELIVER"));
242 assert (streq (key, "inproc://beta-1"));
243 assert (streq (value, "service1"));
244 zstr_free (&command);
245 zstr_free (&key);
246 zstr_free (&value);
247
248 zstr_recvx (alpha, &command, &key, &value, NULL);
249 assert (streq (command, "DELIVER"));
250 assert (streq (key, "inproc://beta-2"));
251 assert (streq (value, "service2"));
252 zstr_free (&command);
253 zstr_free (&key);
254 zstr_free (&value);
255
256 zstr_recvx (alpha, &command, &status, NULL);
257 assert (streq (command, "STATUS"));
258 assert (atoi (status) == 4);
259 zstr_free (&command);
260 zstr_free (&status);
261
262 zactor_destroy (&base);
263 zactor_destroy (&alpha);
264 zactor_destroy (&beta);
265
266 #ifdef CZMQ_BUILD_DRAFT_API
267 // DRAFT-API: Security
268 // curve
269 if (zsys_has_curve()) {
270 if (verbose)
271 printf("testing CURVE support");
272 zclock_sleep (2000);
273 zactor_t *auth = zactor_new(zauth, NULL);
274 assert (auth);
275 if (verbose) {
276 zstr_sendx (auth, "VERBOSE", NULL);
277 zsock_wait (auth);
278 }
279 zstr_sendx(auth,"ALLOW","127.0.0.1",NULL);
280 zsock_wait(auth);
281 zstr_sendx (auth, "CURVE", CURVE_ALLOW_ANY, NULL);
282 zsock_wait (auth);
283
284 server = zactor_new (zgossip, "server");
285 if (verbose)
286 zstr_send (server, "VERBOSE");
287 assert (server);
288
289 zcert_t *client1_cert = zcert_new ();
290 zcert_t *server_cert = zcert_new ();
291
292 zstr_sendx (server, "SET PUBLICKEY", zcert_public_txt (server_cert), NULL);
293 zstr_sendx (server, "SET SECRETKEY", zcert_secret_txt (server_cert), NULL);
294 zstr_sendx (server, "ZAP DOMAIN", "TEST", NULL);
295
296 zstr_sendx (server, "BIND", "tcp://127.0.0.1:*", NULL);
297 zstr_sendx (server, "PORT", NULL);
298 zstr_recvx (server, &command, &value, NULL);
299 assert (streq (command, "PORT"));
300 int port = atoi (value);
301 zstr_free (&command);
302 zstr_free (&value);
303 char endpoint [32];
304 sprintf (endpoint, "tcp://127.0.0.1:%d", port);
305
306 zactor_t *client1 = zactor_new (zgossip, "client");
307 if (verbose)
308 zstr_send (client1, "VERBOSE");
309 assert (client1);
310
311 zstr_sendx (client1, "SET PUBLICKEY", zcert_public_txt (client1_cert), NULL);
312 zstr_sendx (client1, "SET SECRETKEY", zcert_secret_txt (client1_cert), NULL);
313 zstr_sendx (client1, "ZAP DOMAIN", "TEST", NULL);
314
315 const char *public_txt = zcert_public_txt (server_cert);
316 zstr_sendx (client1, "CONNECT", endpoint, public_txt, NULL);
317 zstr_sendx (client1, "PUBLISH", "tcp://127.0.0.1:9001", "service1", NULL);
318
319 zclock_sleep (500);
320
321 zstr_send (server, "STATUS");
322 zclock_sleep (500);
323
324 zstr_recvx (server, &command, &key, &value, NULL);
325 assert (streq (command, "DELIVER"));
326 assert (streq (value, "service1"));
327
328 zstr_free (&command);
329 zstr_free (&key);
330 zstr_free (&value);
331
332 zstr_sendx (client1, "$TERM", NULL);
333 zstr_sendx (server, "$TERM", NULL);
334
335 zclock_sleep(500);
336
337 zcert_destroy (&client1_cert);
338 zcert_destroy (&server_cert);
339
340 zactor_destroy (&client1);
341 zactor_destroy (&server);
342 zactor_destroy (&auth);
343 }
344 #endif
345
346 #if defined (__WINDOWS__)
347 zsys_shutdown();
348 #endif
349
350
352 The czmq manual was written by the authors in the AUTHORS file.
353
355 Main web site:
356
357 Report bugs to the email <zeromq-dev@lists.zeromq.org[1]>
358
360 Copyright (c) the Contributors as noted in the AUTHORS file. This file
361 is part of CZMQ, the high-level C binding for 0MQ:
362 http://czmq.zeromq.org. This Source Code Form is subject to the terms
363 of the Mozilla Public License, v. 2.0. If a copy of the MPL was not
364 distributed with this file, You can obtain one at
365 http://mozilla.org/MPL/2.0/. LICENSE included with the czmq
366 distribution.
367
369 1. zeromq-dev@lists.zeromq.org
370 mailto:zeromq-dev@lists.zeromq.org
371
372
373
374CZMQ 4.2.1 02/01/2021 ZGOSSIP(3)