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

一、前言

  • 在前面的两篇文章中我们分别介绍了“发布-订阅”代理和“请求-响应”代理:
    • “发布-订阅”代理使用ZMQ_XPUB、ZMQ_XSUB实现,详情可参阅:https://blog.csdn.net/qq_41453285/article/details/106877202
    • “请求-响应”代理使用ZMQ_ROUTER、ZMQ_DEALER实现,详情可参阅:https://blog.csdn.net/qq_41453285/article/details/106878960
  • 对于Ø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 
#include 
#include 
#include 
#include 
 
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

ZeroMQ:20---模式之(ØMQ内置代理功能:zmq_proxy()、zmq_proxy_steerable())_第1张图片

用法示例——转送代理

  • 这里代理就是“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);

 

你可能感兴趣的:(ZeroMQ,ØMQ内置代理功能,zmq_proxy)