Proxy Pattern¶
What this chapter answers — how to compose ROUTER, DEALER, PAIR, and the PUB-family sockets into a proxy that relays messages. Each individual socket's contract is owned by that socket's own spec.
1. Overview¶
A proxy relays messages between two sockets.
zlink_proxy() is a general-purpose utility that works with any socket
combination. Users can also build custom proxies by composing the public
per-socket APIs.
2. zlink_proxy() — Built-in Proxy¶
- Forwards messages from
frontendtobackendand vice versa - If
captureis non-NULL, copies all passing messages to the capture socket - Blocking function — run in a dedicated thread
- No socket type restriction — internally uses the same
socket_base_trecv/send paths as the raw socket APIs, so pairing sockets whose public send or receive surface returnsZLINK_SUBMIT_NOT_SUPPORTED/ZLINK_RECV_NOT_SUPPORTED(for example XSUB or XPUB) still works inside a proxy
Example Socket Combinations¶
| frontend | backend | Use case |
|---|---|---|
| XSUB | XPUB | PUB/SUB relay (most common) |
| ROUTER | DEALER | Request/reply broker |
| DEALER | DEALER | Load balancing relay |
| PAIR | PAIR | Inter-thread bridge |
3. PUB/SUB Proxy — XSUB/XPUB¶
The most common proxy pattern.
%%{init: {'flowchart': {'nodeSpacing': 32, 'rankSpacing': 40, 'padding': 8, 'wrappingWidth': 180}, 'themeVariables': {'fontSize': '18px'}}}%%
flowchart LR
PUB -->|data| XSUB
XSUB ==>|proxy| XPUB
XPUB -->|data| SUB
SUB -.->|subscribe| XPUB
XPUB -.->|proxy| XSUB
XSUB -.->|subscribe| PUB
3.1 Built-in Proxy¶
void *xsub = zlink_socket(ctx, ZLINK_SOCKET_XSUB);
zlink_bind(xsub, "tcp://*:5556"); /* PUBs connect here */
void *xpub = zlink_socket(ctx, ZLINK_SOCKET_XPUB);
zlink_bind(xpub, "tcp://*:5557"); /* SUBs connect here */
void *capture = zlink_socket(ctx, ZLINK_SOCKET_PUB);
zlink_bind(capture, "tcp://*:5558"); /* optional: message recording */
zlink_proxy(xsub, xpub, capture); /* blocking */
zlink_proxy() handles two things internally:
- Data relay: Pulls messages from XSUB and pushes to XPUB
- Subscription propagation: Pulls subscription events from XPUB and pushes to XSUB
3.2 Manual Proxy¶
When custom logic (logging, filtering, topic transformation) is needed,
build a manual proxy using the public per-socket APIs: zlink_subscribe()
/ zlink_publish() for data, zlink_xpub_recv() /
zlink_set_subscription() for subscriptions.
Data Flow¶
| Step | Socket | API | Description |
|---|---|---|---|
| 1 | XSUB | zlink_subscribe(xsub, ...) |
Receive the complete payload into an array (topic returned separately) |
| 2 | App | Custom logic | Filtering, transformation, logging |
| 3 | XPUB | zlink_publish(xpub, topic, ...) |
Publish the complete payload array |
Subscription Propagation¶
| Step | Socket | API | Description |
|---|---|---|---|
| 1 | XPUB | zlink_xpub_recv(xpub, ...) |
Receive SUB subscribe/unsubscribe events |
| 2 | App | Custom logic | Authorization, topic remapping |
| 3 | XSUB | zlink_set_subscription(xsub, topic) |
Propagate to upstream PUB |
Full Code¶
void *xsub = zlink_socket(ctx, ZLINK_SOCKET_XSUB);
void *xpub = zlink_socket(ctx, ZLINK_SOCKET_XPUB);
zlink_bind(xsub, "tcp://*:5556");
zlink_bind(xpub, "tcp://*:5557");
while (running) {
/* Data relay: XSUB -> app -> XPUB, one complete record at a time */
char topic[256];
size_t topic_len = 0;
zlink_msg_t parts[16];
size_t part_count = 0;
zlink_recv_result_t rc = zlink_subscribe(
xsub, NULL, topic, sizeof(topic), &topic_len,
parts, 16, &part_count,
ZLINK_RECV_FLAGS_DONTWAIT);
if (rc == ZLINK_RECV_OK) {
/* Insert custom logic here (filtering, logging, etc.) */
/* Forward the complete array in one call to preserve the record boundary. */
zlink_publish(xpub, topic, parts, part_count, ZLINK_SEND_FLAGS_NONE);
}
/* Subscription propagation: XPUB -> app -> XSUB */
const zlink_routing_id_t *sub_rid = NULL;
int subscribed = 0;
char sub_topic[256];
size_t sub_len = 0;
zlink_recv_result_t sub_rc = zlink_xpub_recv(
xpub, &sub_rid, &subscribed, sub_topic, sizeof(sub_topic), &sub_len,
ZLINK_RECV_FLAGS_DONTWAIT);
if (sub_rc == ZLINK_RECV_OK) {
/* Insert custom logic here (authorization, remapping, etc.) */
if (subscribed)
zlink_set_subscription(xsub, sub_topic);
else
zlink_unset_subscription(xsub, sub_topic);
}
}
3.3 Why XSUB/XPUB?¶
| Question | With SUB/PUB | With XSUB/XPUB |
|---|---|---|
| Data pass-through | SUB local filter on — must subscribe | XSUB local filter off — passes all |
| Subscription events | PUB doesn't expose | XPUB exposes them via zlink_xpub_recv() |
| Proxy suitability | Proxy must manage topics itself | Relay only — ideal for proxy |
Key point:
zlink_proxy()uses the same internal recv/send paths as the raw socket APIs, not the publiczlink_send()/zlink_recv()surface. Through that public surface,zlink_send()on XSUB still returnsZLINK_SUBMIT_NOT_SUPPORTEDandzlink_recv()on XPUB still returnsZLINK_RECV_NOT_SUPPORTED. Proxy operation is only possible viazlink_proxy()or the manual approach above (using the dedicatedzlink_subscribe(),zlink_publish(), etc. APIs).
4. Request/Reply Proxy — ROUTER/DEALER¶
void *frontend = zlink_socket(ctx, ZLINK_SOCKET_ROUTER);
zlink_bind(frontend, "tcp://*:5559");
void *backend = zlink_socket(ctx, ZLINK_SOCKET_DEALER);
zlink_bind(backend, "tcp://*:5560");
zlink_proxy(frontend, backend, NULL); /* blocking */
ROUTER/DEALER proxy has no subscription propagation, so zlink_proxy()
alone is sufficient. For manual construction of the ROUTER-facing side, use
zlink_router_recv() → zlink_send_rid() (see the
ROUTER guide for the full signature and a
worked example). To relay a multipart record, use the whole-message
zlink_router_recv()
to receive it into an array and send that array unchanged.
5. Why Use a Proxy?¶
Direct (no proxy) -- N x M connections:
%%{init: {'flowchart': {'nodeSpacing': 32, 'rankSpacing': 40, 'padding': 8, 'wrappingWidth': 180}, 'themeVariables': {'fontSize': '18px'}}}%%
flowchart LR
P1[PUB 1] --> S1[SUB 1]
P1 --> S2[SUB 2]
P2[PUB 2] --> S1
P2 --> S2
PUB/SUB must know each other's addresses. Connection count = N x M.
With proxy -- N + M connections:
%%{init: {'flowchart': {'nodeSpacing': 32, 'rankSpacing': 40, 'padding': 8, 'wrappingWidth': 180}, 'themeVariables': {'fontSize': '18px'}}}%%
flowchart LR
P1[PUB 1] --> XSUB
P2[PUB 2] --> XSUB
subgraph Proxy
XSUB --> XPUB
end
XPUB --> S1[SUB 1]
XPUB --> S2[SUB 2]
Only the proxy address is needed. Connection count = N + M.
| Use Case | Description |
|---|---|
| Reduce connections | N×M → N+M |
| Address decoupling | PUB/SUB don't need each other's endpoints |
| Dynamic scaling | PUB/SUB add/remove independently |
| Subscription transformation | XPUB MANUAL mode for topic remapping/filtering |
| Network bridging | Connect different network segments (e.g., inproc ↔ tcp) |
| Monitoring | Capture socket records all passing messages |