ZeroMQ:20---模式之(ØMQ內置代理功能:zmq_proxy()、zmq_proxy_steerable())

一、前言

二、zmq_proxy()

int zmq_proxy (const void *frontend, const void *backend, const void *capture);
  • 功能:在應用程序線程中啓動內置的ØMQ代理
  • 參數:
    • frontend:代理前端套接字
    • backend:代理後端套接字
    • capture:捕獲套接字
  • 描述與說明:
    • 代理將前端套接字連接到後端套接字。從概念上講,數據是從前端流向後端。根據套接字類型的不同,回覆可能以相反的方向流動。方向只是概念上的;代理是完全對稱的,在前端和後端之間沒有技術上的區別
    • 在調用zmq_proxy()之前,您必須設置任何套接字選項,並連接或綁定前端和後端套接字兩種傳統的代理模型是:
      • zmq_proxy()在當前線程中運行,只在當前上下文關閉時返回
      • 如果捕獲套接字不爲NULL,代理將發送所有前端和後端接收到的消息到捕獲套接字。捕獲套接字應該爲ZMQ_PUB、ZMQ_DEALER、ZMQ_PUSH或ZMQ_PAIR類型的套接字
  • 返回值:總是返回-1,並將errno設置爲如下值之一:
    • ETERM
    • EINTR:與ØMQ上下文相關的指定套接字被終止

用法示例——共享隊列

  • 共享隊列在“ROUTER-DEALER”文章介紹過了,可參閱:https://blog.csdn.net/qq_41453285/article/details/106878960
  • 概念:前端是ZMQ_ROUTER套接字,後端是ZMQ_DEALER套接字時,代理應該充當一個共享隊列,從一組客戶端收集請求,並將這些請求公平地分配給一組服務。來自前端連接的請求應該公平排隊,並在後端連接之間均勻分佈。回覆將自動返回給發出原始請求的客戶端
  • “ROUTER-DEALER”文章中我們使用rrbroker.c編寫了代理的模型,現在我們使用zmq_proxy()函數改寫rrbroker.c,只要將rrbroker.c中的while(1)循環替換爲zmq_proxy()函數並去除輪詢即可。代碼如下:
// msgqueue.c
// 源碼鏈接: https://github.com/dongyusheng/csdn-code/blob/master/ZeroMQ/msgqueue.c
#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <zmq.h>
 
int main()
{
    int rc;
    // 1.初始化上下文
    void *context = zmq_ctx_new();
 
    // 2.創建、綁定套接字
    void *frontend = zmq_socket(context, ZMQ_ROUTER);
    void *backend = zmq_socket(context, ZMQ_DEALER);
    // ZMQ_ROUTER綁定到5559, 接收客戶端的請求
    rc = zmq_bind(frontend, "tcp://*:5559");
    if(rc == -1)
    {
        perror("zmq_bind");
        zmq_close(frontend);
        zmq_close(backend);
        zmq_ctx_destroy(context);
        return -1;
    }
    // ZMQ_DEALER綁定到5560, 接收服務端的回覆
    rc = zmq_bind(backend, "tcp://*:5560");
    if(rc == -1)
    {
        perror("zmq_bind");
        zmq_close(frontend);
        zmq_close(backend);
        zmq_ctx_destroy(context);
        return -1;
    }

    // 3.啓動代理
    zmq_proxy(frontend, backend, NULL);
 
    // 4.關閉套接字、銷燬上下文
    zmq_close(frontend);
    zmq_close(backend);
    zmq_ctx_destroy(context);
    
    return 0;
}
  • 編譯運行如下,左側爲客戶端,中間爲我們的代理程序,右側爲服務端
gcc -o mshqueue msgqueue.c -lzmq

用法示例——轉送代理

  • 這裏代理就是“XSUB-XPUB”形式,我們已經介紹過了,可參閱:https://blog.csdn.net/qq_41453285/article/details/106877202
  • 當前端是ZMQ_XSUB套接字,後端是ZMQ_XPUB套接字時,代理應該充當消息轉發器,從一組發佈者收集消息並將其轉發給一組訂閱者。這可以用來橋接網絡傳輸,例如在tcp://上讀取和在pgm://上轉發

用法示例——Streamer

  • 當前端是ZMQ_PULL套接字,後端是ZMQ_PUSH套接字時,代理應該從一組客戶端收集任務,並使用管道模式將這些任務轉發給一組worker

三、zmq_proxy_steerable

int zmq_proxy_steerable (const void *frontend, const void *backend, const void *capture, const void *control);
  • 功能:帶有控制流的內置ØMQ代理
  • 參數:
    • frontend:代理前端套接字
    • backend:代理後端套接字
    • capture:捕獲套接字
    • control:控制流套接字
  • 描述與說明:
    • 如果控制套接字不是NULL,代理就支持控制流:
      • 如果在此套接字上接收到PAUSE,代理將掛起其活動
      • 如果收到RESUME,就繼續
      • 如果接收到TERMINATE,則它將順利終止
      • 如果接收到STATISTICS,代理將回復控制套接字,發送一個8幀的多部分消息,每個消息具有64位的無符號整數,其順序如下:前端套接字接收的消息數===>前端套接字接收的字節數===>發送給前端套接字的消息數量===>前端套接字發送的字節數===>後端套接字接收的消息數===>後端套接字接收的字節數===>發送後端套接字的消息數===>發送後端套接字的字節數
  • 在啓動時,代理正常運行,就像使用了zmq_proxy一樣
    • 如果控制套接字爲NULL,則函數的行爲與調用zmq_proxy()完全相同
  • 返回值:
    • 如果將TERMINATE發送到其控制套接字,則zmq_proxy_steerable()函數返回0。否則,它將返回1和errno設置爲ETERM或捕獲(ØMQ上下文相關的指定套接字被終止)。
    • ETERM
    • EINTR:與ØMQ上下文相關的指定套接字被終止

演示案例

  • 創建共享隊列代理
/ Create frontend, backend and control sockets
void *frontend = zmq_socket (context, ZMQ_ROUTER);
assert (backend);
void *backend = zmq_socket (context, ZMQ_DEALER);
assert (frontend);
void *control = zmq_socket (context, ZMQ_SUB);
assert (control);

// Bind sockets to TCP ports
assert (zmq_bind (frontend, "tcp://*:5555") == 0);
assert (zmq_bind (backend, "tcp://*:5556") == 0);
assert (zmq_connect (control, "tcp://*:5557") == 0);

// Subscribe to the control socket since we have chosen SUB here
assert (zmq_setsockopt (control, ZMQ_SUBSCRIBE, "", 0));

// Start the queue proxy, which runs until ETERM or "TERMINATE"
// received on the control socket 
zmq_proxy_steerable (frontend, backend, NULL, control);

演示案例

  • 在另一個節點,進程或其他任何地方設置控制器
void *control = zmq_socket (context, ZMQ_PUB);
assert (control);
assert (zmq_bind (control, "tcp://*:5557") == 0);

// pause the proxy
assert (zmq_send (control, "PAUSE", 5, 0) == 0);

// resume the proxy
assert (zmq_send (control, "RESUME", 6, 0) == 0);

// terminate the proxy
assert (zmq_send (control, "TERMINATE", 9, 0) == 0);

// check statistics
assert (zmq_send (control, "STATISTICS", 10, 0) == 0);
zmq_msg_t stats_msg;

while (1) {
 assert (zmq_msg_init (&stats_msg) == 0);
 assert (zmq_recvmsg (control, &stats_msg, 0) == sizeof (uint64_t));
 assert (rc == sizeof (uint64_t));
 printf ("Stat: %lu\n", *(unsigned long int *)zmq_msg_data (&stats_msg));
 if (!zmq_msg_get (&stats_msg, ZMQ_MORE))
 break;
 assert (zmq_msg_close (&stats_msg) == 0);
}
assert (zmq_msg_close (&stats_msg) == 0);

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章