发布-订阅方案中的异常处理 (ZeroMQ)

Exception handling in a pub-sub scheme (ZeroMQ)

我使用 ZeroMQ 创建了一个发布者-订阅者通信方案,我注意到我的服务器和客户端程序存在一个小问题。 我知道 C 中没有 try catch(根据我的简短研究)但是接下来的两个 while(1) 无一例外地捕获对我来说似乎很危险。

考虑到以下代码片段,处理异常(同时)的最正确方法是什么?使用我现在拥有的结构(如下所示),zmq_closezmq_ctx_destroy 将永远不会执行,但我希望它们在程序 error/exception 的情况下(无论哪个起源)。

注意: 在这个架构中,我有一个客户端监听多个发布者,因此 Client 中的 for 周期代码。

服务器

(...inside main)

while (1) {
    char update[20];
    sprintf(update, "%s", "new_update");
    s_send(publisher, update);
    sleep(1);
}

zmq_close(publisher);
zmq_ctx_destroy(context);
return 0;

客户端

(...inside main)

while(1){
    for (c = 1; c < server_num; c = c + 1){
        char *msg = s_recv(subscribers[c]);

        if (msg) {
            printf("%s\n",msg);
            free(msg);
        }
        sleep(1);
    }
}

for (c = 0; c < server_num; c = c + 1)
    zmq_close(subscribers[c]);

zmq_ctx_destroy(context);
return 0;

C 中检查错误的惯用方法是查看 return 值,然后检查 errno 是否为负值。

// ... Your previous code
int ret = zmq_ctx_destroy(context);
if(ret < 0) {
    // Process your error here
    printf("My error message is : %s\n", strerror(errno));
}

如果您的程序中没有 #include <errno.h><string.h>,您可能需要添加它们。 您还可以阅读 strerror 文档。

现在解决这部分问题:

Taking into account the following code snippets, what would be the most correct way to handle an exception (inside the while)? With the structure I have right now (as you can see below), the zmq_close and zmq_ctx_destroy will never execute, but I want them to, in case of a program error/exception (whichever the origin).

所有zmq_*函数都会return一个错误并设置errno。出现错误时检查每个功能和break。在这种情况下,当发生错误时,对非阻塞函数的轮询最好 break 跳出 while 循环。

在 Linux 上,您还可以设置信号处理程序并在发出信号时执行清理例程(例如,在 UNIX 上通过 ctrl 捕获 SIGINT 以正确退出程序是很常见的+C 在控制台中)。看到这个

你是对的,C 没有 try/catch 的概念,但这应该不是问题。这只是意味着您需要处理 s_send() 和 s_recv() 例程中的异常(因此,例如,如果发生意外情况(例如 malloc() returning NULL),你必须处理它并继续处理或 return)。

我还建议您查看客户端的 poll() 或 select() 系统调用,而不是进行循环轮询。只为有数据等待读取的文件描述符提供服务要优雅得多。

如果你从未使用过 ZeroMQ,
或者从未接触过零之禅,
的概念 在深入了解更多细节之前,您可能会喜欢先看看

作为标签 出席...:[=​​55=]

Q : what would be the most correct way to handle an exception (inside the while)?

最好的策略错误预防而不是 任何种类 of "reactive"(ex-post异常)处理。

永远假设事情可能而且将会造成破坏,让它们廉价地失败。失败的成本越低,系统就会越好、越快地恢复到它自己的预期行为。

这就是说,在现代 , the more in 中,异常是设计代码执行流程中极其昂贵的破坏性元素。


出于这些原因,也为了保持最高水平的性能,

ZeroMQ 从此有了一种非常不同的方法:

0)
更好地使用 zmq_poll() 作为 有史以来最便宜的检测 存在(或不存在)任何可读消息(已经发送并且准备好接收 ), 之前,如果有的话,调用 zmq_recv() 的 API 函数,从 Context() 内部将这些数据获取到应用程序级代码的手中=]-实例内部存储。

1)
根据您的语言绑定(包装器),最好享受 .poll().send().recv() 方法的 非阻塞形式 。本机 API 是最直接的,总是以 retCode = zmq_recv( ..., ZMQ_NOBLOCK );

进入此模式

2)
始终分析 retCode - 无论是无声的还是解释性的 assert( retCode == 0 && zmq_errno() ) 或其他。

3)
最好的审查和微调 ZeroMQ 框架中可用的实例化工具的所有配置属性,并利用它们所有隐藏的优势来最好地满足您的应用程序域的需求。许多本机 API-settings 可能有助于减轻(如果不能主要避免)Context()-engine 实例内部的许多冲突要求,因此请毫不犹豫地了解可能设置的所有细节并将它们用于他们对您的代码提供最好的帮助。

如果不执行上述所有操作,您的代码将不是充分利用Zen-of-Zero


Q : With the structure I have right now (...), the zmq_close and zmq_ctx_destroy will never execute, but I want them to, in case of a program error/exception (whichever the origin).

设置一个明确的标志是公平的:

bool    DoNotExitSoFar = True;

while ( DoNotExitSoFar ){
    // Do whatever you need

    // Record return-codes, always
       retCode = zmq_...(...);

    // Test/Set the explicit flag upon a context of retCode and zmq_errno()
       if ( retCode == EPROTONOTSUPPORTED ){
         // take all due measures needed
            ...
         // FINALLY: Set
            DoNotExitSoFar = False;
       }
}

// --------------------------------------------- GRACEFUL TERMINATION .close()
if ( ENOTSOCK == zmq_close(...) ) { ...; }
...
// --------------------------------------------- GRACEFUL TERMINATION .term()
retCode = zmq_ctx_term(...);

if ( EINTR  == retCode ){ ...; }
if ( EFAULT == retCode ){ ...; }
...

使用其他工具,例如 int atexit(void (*func)(void)); 可能是 ALAP 调用 zmq_close()zmq_ctx_term()

的最后手段