'\" t .\" Title: zgossip .\" Author: [see the "AUTHORS" section] .\" Generator: DocBook XSL Stylesheets vsnapshot .\" Date: 03/02/2024 .\" Manual: CZMQ Manual .\" Source: CZMQ 4.2.1 .\" Language: English .\" .TH "ZGOSSIP" "3" "03/02/2024" "CZMQ 4\&.2\&.1" "CZMQ Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" zgossip \- Class for decentralized configuration management .SH "SYNOPSIS" .sp .nf // To work with zgossip, use the CZMQ zactor API: // // Create new zgossip instance, passing logging prefix: // // zactor_t *zgossip = zactor_new (zgossip, "myname"); // // Destroy zgossip instance // // zactor_destroy (&zgossip); // // Enable verbose logging of commands and activity: // // zstr_send (zgossip, "VERBOSE"); // // Bind zgossip to specified endpoint\&. TCP endpoints may specify // the port number as "*" to acquire an ephemeral port: // // zstr_sendx (zgossip, "BIND", endpoint, NULL); // // Return assigned port number, specifically when BIND was done using an // an ephemeral port: // // zstr_sendx (zgossip, "PORT", NULL); // char *command, *port_str; // zstr_recvx (zgossip, &command, &port_str, NULL); // assert (streq (command, "PORT")); // // Specify configuration file to load, overwriting any previous loaded // configuration file or options: // // zstr_sendx (zgossip, "LOAD", filename, NULL); // // Set configuration path value: // // zstr_sendx (zgossip, "SET", path, value, NULL); // // Save configuration data to config file on disk: // // zstr_sendx (zgossip, "SAVE", filename, NULL); // // Send zmsg_t instance to zgossip: // // zactor_send (zgossip, &msg); // // Receive zmsg_t instance from zgossip: // // zmsg_t *msg = zactor_recv (zgossip); // // This is the zgossip constructor as a zactor_fn: // CZMQ_EXPORT void zgossip (zsock_t *pipe, void *args); // Self test of this class CZMQ_EXPORT void zgossip_test (bool verbose); Please add \*(Aq@interface\*(Aq section in \*(Aq\&./\&.\&./src/zgossip\&.c\*(Aq\&. .fi .SH "DESCRIPTION" .sp Implements a gossip protocol for decentralized configuration management\&. Your applications nodes form a loosely connected network (which can have cycles), and publish name/value tuples\&. Each node re\-distributes the new tuples it receives, so that the entire network eventually achieves a consistent state\&. The current design does not expire tuples\&. .sp Provides these commands (sent as multipart strings to the actor): .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} BIND endpoint \(em binds the gossip service to specified endpoint .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} PORT \(em returns the last TCP port, if any, used for binding .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} LOAD configfile \(em load configuration from specified file .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} SET configpath value \(em set configuration path = value .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} SAVE configfile \(em save configuration to specified file .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} CONNECT endpoint \(em connect the gossip service to the specified peer .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} PUBLISH key value \(em publish a key/value pair to the gossip cluster .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} STATUS \(em return number of key/value pairs held by gossip service .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} ZAP DOMAIN domain \(em set the ZAP DOMAIN domain = value .RE .sp Returns these messages: .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} PORT number \(em reply to PORT command .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} STATUS number \(em reply to STATUS command .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} DELIVER key value \(em new tuple delivered from network .RE .sp The gossip protocol distributes information around a loosely\-connected network of gossip services\&. The information consists of name/value pairs published by applications at any point in the network\&. The goal of the gossip protocol is to create eventual consistency between all the using applications\&. .sp The name/value pairs (tuples) can be used for configuration data, for status updates, for presence, or for discovery\&. When used for discovery, the gossip protocol works as an alternative to e\&.g\&. UDP beaconing\&. .sp The gossip network consists of a set of loosely\-coupled nodes that exchange tuples\&. Nodes can be connected across arbitrary transports, so the gossip network can have nodes that communicate over inproc, over IPC, and/or over TCP, at the same time\&. .sp Each node runs the same stack, which is a server\-client hybrid using a modified Harmony pattern (from Chapter 8 of the Guide): \m[blue]\fBhttp://zguide\&.zeromq\&.org/page:all#True\-Peer\-Connectivity\-Harmony\-Pattern\fR\m[] .sp Each node provides a ROUTER socket that accepts client connections on an key defined by the application via a BIND command\&. The state machine for these connections is in zgossip\&.xml, and the generated code is in zgossip_engine\&.inc\&. .sp Each node additionally creates outbound connections via DEALER sockets to a set of servers ("remotes"), and under control of the calling app, which sends CONNECT commands for each configured remote\&. .sp The messages between client and server are defined in zgossip_msg\&.xml\&. We built this stack using the zeromq/zproto toolkit\&. .sp To join the gossip network, a node connects to one or more peers\&. Each peer acts as a forwarder\&. This loosely\-coupled network can scale to thousands of nodes\&. However the gossip protocol is NOT designed to be efficient, and should not be used for application data, as the same tuples may be sent many times across the network\&. .sp The basic logic of the gossip service is to accept PUBLISH messages from its owning application, and to forward these to every remote, and every client it talks to\&. When a node gets a duplicate tuple, it throws it away\&. When a node gets a new tuple, it stores it, and forwards it as just described\&. .sp At present there is no way to expire tuples from the network\&. .sp The assumptions in this design are: .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} The data set is slow\-changing\&. Thus, the cost of the gossip protocol is irrelevant with respect to other traffic\&. .RE .SH "EXAMPLE" .PP \fBFrom zgossip_test method\fR. .sp .if n \{\ .RS 4 .\} .nf // Test basic client\-to\-server operation of the protocol zactor_t *server = zactor_new (zgossip, "server"); assert (server); if (verbose) zstr_send (server, "VERBOSE"); zstr_sendx (server, "BIND", "inproc://zgossip", NULL); zsock_t *client = zsock_new (ZMQ_DEALER); assert (client); zsock_set_rcvtimeo (client, 2000); int rc = zsock_connect (client, "inproc://zgossip"); assert (rc == 0); // Send HELLO, which gets no message zgossip_msg_t *message = zgossip_msg_new (); zgossip_msg_set_id (message, ZGOSSIP_MSG_HELLO); zgossip_msg_send (message, client); // Send PING, expect PONG back zgossip_msg_set_id (message, ZGOSSIP_MSG_PING); zgossip_msg_send (message, client); zgossip_msg_recv (message, client); assert (zgossip_msg_id (message) == ZGOSSIP_MSG_PONG); zgossip_msg_destroy (&message); zactor_destroy (&server); zsock_destroy (&client); // Test peer\-to\-peer operations zactor_t *base = zactor_new (zgossip, "base"); assert (base); if (verbose) zstr_send (base, "VERBOSE"); // Set a 100msec timeout on clients so we can test expiry zstr_sendx (base, "SET", "server/timeout", "100", NULL); zstr_sendx (base, "BIND", "inproc://base", NULL); zactor_t *alpha = zactor_new (zgossip, "alpha"); assert (alpha); if (verbose) zstr_send (alpha, "VERBOSE"); zstr_sendx (alpha, "CONNECT", "inproc://base", NULL); zstr_sendx (alpha, "PUBLISH", "inproc://alpha\-1", "service1", NULL); zstr_sendx (alpha, "PUBLISH", "inproc://alpha\-2", "service2", NULL); zactor_t *beta = zactor_new (zgossip, "beta"); assert (beta); if (verbose) zstr_send (beta, "VERBOSE"); zstr_sendx (beta, "CONNECT", "inproc://base", NULL); zstr_sendx (beta, "PUBLISH", "inproc://beta\-1", "service1", NULL); zstr_sendx (beta, "PUBLISH", "inproc://beta\-2", "service2", NULL); // got nothing zclock_sleep (200); zstr_send (alpha, "STATUS"); char *command, *status, *key, *value; zstr_recvx (alpha, &command, &key, &value, NULL); assert (streq (command, "DELIVER")); assert (streq (key, "inproc://alpha\-1")); assert (streq (value, "service1")); zstr_free (&command); zstr_free (&key); zstr_free (&value); zstr_recvx (alpha, &command, &key, &value, NULL); assert (streq (command, "DELIVER")); assert (streq (key, "inproc://alpha\-2")); assert (streq (value, "service2")); zstr_free (&command); zstr_free (&key); zstr_free (&value); zstr_recvx (alpha, &command, &key, &value, NULL); assert (streq (command, "DELIVER")); assert (streq (key, "inproc://beta\-1")); assert (streq (value, "service1")); zstr_free (&command); zstr_free (&key); zstr_free (&value); zstr_recvx (alpha, &command, &key, &value, NULL); assert (streq (command, "DELIVER")); assert (streq (key, "inproc://beta\-2")); assert (streq (value, "service2")); zstr_free (&command); zstr_free (&key); zstr_free (&value); zstr_recvx (alpha, &command, &status, NULL); assert (streq (command, "STATUS")); assert (atoi (status) == 4); zstr_free (&command); zstr_free (&status); zactor_destroy (&base); zactor_destroy (&alpha); zactor_destroy (&beta); #ifdef CZMQ_BUILD_DRAFT_API // DRAFT\-API: Security // curve if (zsys_has_curve()) { if (verbose) printf("testing CURVE support"); zclock_sleep (2000); zactor_t *auth = zactor_new(zauth, NULL); assert (auth); if (verbose) { zstr_sendx (auth, "VERBOSE", NULL); zsock_wait (auth); } zstr_sendx(auth,"ALLOW","127\&.0\&.0\&.1",NULL); zsock_wait(auth); zstr_sendx (auth, "CURVE", CURVE_ALLOW_ANY, NULL); zsock_wait (auth); server = zactor_new (zgossip, "server"); if (verbose) zstr_send (server, "VERBOSE"); assert (server); zcert_t *client1_cert = zcert_new (); zcert_t *server_cert = zcert_new (); zstr_sendx (server, "SET PUBLICKEY", zcert_public_txt (server_cert), NULL); zstr_sendx (server, "SET SECRETKEY", zcert_secret_txt (server_cert), NULL); zstr_sendx (server, "ZAP DOMAIN", "TEST", NULL); zstr_sendx (server, "BIND", "tcp://127\&.0\&.0\&.1:*", NULL); zstr_sendx (server, "PORT", NULL); zstr_recvx (server, &command, &value, NULL); assert (streq (command, "PORT")); int port = atoi (value); zstr_free (&command); zstr_free (&value); char endpoint [32]; sprintf (endpoint, "tcp://127\&.0\&.0\&.1:%d", port); zactor_t *client1 = zactor_new (zgossip, "client"); if (verbose) zstr_send (client1, "VERBOSE"); assert (client1); zstr_sendx (client1, "SET PUBLICKEY", zcert_public_txt (client1_cert), NULL); zstr_sendx (client1, "SET SECRETKEY", zcert_secret_txt (client1_cert), NULL); zstr_sendx (client1, "ZAP DOMAIN", "TEST", NULL); const char *public_txt = zcert_public_txt (server_cert); zstr_sendx (client1, "CONNECT", endpoint, public_txt, NULL); zstr_sendx (client1, "PUBLISH", "tcp://127\&.0\&.0\&.1:9001", "service1", NULL); zclock_sleep (500); zstr_send (server, "STATUS"); zclock_sleep (500); zstr_recvx (server, &command, &key, &value, NULL); assert (streq (command, "DELIVER")); assert (streq (value, "service1")); zstr_free (&command); zstr_free (&key); zstr_free (&value); zstr_sendx (client1, "$TERM", NULL); zstr_sendx (server, "$TERM", NULL); zclock_sleep(500); zcert_destroy (&client1_cert); zcert_destroy (&server_cert); zactor_destroy (&client1); zactor_destroy (&server); zactor_destroy (&auth); } #endif #if defined (__WINDOWS__) zsys_shutdown(); #endif .fi .if n \{\ .RE .\} .sp .SH "AUTHORS" .sp The czmq manual was written by the authors in the AUTHORS file\&. .SH "RESOURCES" .sp Main web site: \m[blue]\fB\%\fR\m[] .sp Report bugs to the email <\m[blue]\fBzeromq\-dev@lists\&.zeromq\&.org\fR\m[]\&\s-2\u[1]\d\s+2> .SH "COPYRIGHT" .sp Copyright (c) the Contributors as noted in the AUTHORS file\&. This file is part of CZMQ, the high\-level C binding for 0MQ: http://czmq\&.zeromq\&.org\&. This Source Code Form is subject to the terms of the Mozilla Public License, v\&. 2\&.0\&. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla\&.org/MPL/2\&.0/\&. LICENSE included with the czmq distribution\&. .SH "NOTES" .IP " 1." 4 zeromq-dev@lists.zeromq.org .RS 4 \%mailto:zeromq-dev@lists.zeromq.org .RE