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

问题描述 投票:0回答:3

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

while(1)
没有异常捕获对我来说似乎很危险。

考虑到以下代码片段,处理异常(在 while 内)的最正确方法是什么?根据我现在的结构(如下所示),

zmq_close
zmq_ctx_destroy
永远不会执行,但我希望它们能够执行,以防出现程序错误/异常(无论来源如何)。

注意: 在此架构中,我有一个客户端监听多个发布者,因此

for
Client 代码中循环。

服务器

(...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 error-handling zeromq
3个回答
2
投票

作为标签存在:

Q处理异常(在 while 内)的最正确方法是什么?

最好的策略错误预防,而不是任何类型的“反应式”(事后异常)处理。

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

这就是说,在现代低延迟分布式系统中,在实时系统中,异常是极其昂贵的,是设计的代码执行流的破坏性元素。


出于这些原因,也为了保持最佳性能的持续水平, 从那时起,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 设置可能有助于减轻(如果不是原则上避免的话)

Context()
引擎实例内部的大量冲突要求,因此请毫不犹豫地了解可能设置的所有详细信息,并充分利用它们来为您提供帮助。代码。

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


Q使用我现在拥有的结构(...),

zmq_close
zmq_ctx_destroy
永远不会执行,但我希望它们能够执行,以防出现程序错误/异常(无论来源如何) .

设置一个明确的标志就足够公平了:

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()

的最后手段

1
投票

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

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


1
投票

C 中检查错误的惯用方法是查看返回值,然后检查 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 文档。

现在解决这部分问题:

考虑到以下代码片段,处理异常(在 while 内)的最正确方法是什么?根据我现在的结构(如下所示),zmq_close 和 zmq_ctx_destroy 将永远不会执行,但我希望它们执行,以防出现程序错误/异常(无论来源如何)。

所有

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

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

© www.soinside.com 2019 - 2024. All rights reserved.