Обработка исключений в схеме pub-sub (ZeroMQ) - PullRequest
0 голосов
/ 23 сентября 2019

Я создал схему связи издатель-подписчик с ZeroMQ и заметил одну маленькую проблему с моими программами сервера и клиента.Я знаю, что в C нет улова попытки (согласно моему краткому исследованию), однако наличие следующих двух while(1) без исключения ловли мне кажется опасным.

Учитывая следующие фрагменты кода, какой будет наиболее правильный способ обработки исключения (в то время как)?Со структурой, которая у меня есть сейчас (как вы можете видеть ниже), zmq_close и zmq_ctx_destroy никогда не будут выполняться, но я хочу, чтобы они выполнялись в случае программной ошибки / исключения (независимо от источника).

Примечание: В этой архитектуре один клиент прослушивает несколько издателей, поэтому циклы for в коде Client .

Server

(...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;

Ответы [ 3 ]

1 голос
/ 23 сентября 2019

Вы правы, C не имеет понятия try / catch, но это не должно быть проблемой.Это просто означает, что вам нужно обрабатывать исключения в подпрограммах s_send () и s_recv () (поэтому, например, если происходит что-то непредвиденное (например, malloc (), возвращающее NULL), вы должны разобраться с этим и продолжить обработку или возврат).

Я бы также посоветовал вам посмотреть системные вызовы poll () или select () для вашего клиента, вместо того, чтобы делать циклический опрос.Гораздо элегантнее обслуживать только те файловые дескрипторы, у которых есть данные, ожидающие чтения.

1 голос
/ 23 сентября 2019

В случае, если вы никогда не работали с ZeroMQ,
или никогда не встречали концепцию искусства Zen-of-Zero ,
, здесь можно насладиться первым взглядом.на "ZeroMQ Принципы менее чем за пять секунд " , прежде чем углубляться в дальнейшие подробности

Наличие тега присутствует...:

Q : Какой самый правильный способ обработки исключения (в то время как)?

Лучшая стратегия - это предотвращение ошибок , а не любой вид"реактивного" (например,-почта Исключения) обработка.

Всегда предполагайте, что вещи могут разрушить и разрушат, и пусть они потерпят неудачу дешево.Чем дешевле стоимость отказов, тем лучше и быстрее система может вернуться к своему собственному предполагаемому поведению.

В современных , тем более в исключение является чрезвычайно дорогим, разрушительным элементом спроектированного потока выполнения кода.


По этим причинам, а также для обеспечения стабильных уровней максимальной производительности,

ZeroMQ с тех пор использует совершенно другой подход:

0)
лучше использовать zmq_poll() как самое дешевое из когда-либо обнаруженных присутствия (или не присутствия) любого читаемого сообщения (уже доставленного и готового к получению)прежде чем, если вообще, вызывать API-функцию zmq_recv(), чтобы получить такие данные в руки кода уровня приложения изнутри Context() экземпляра внутреннего хранилища.

1)
в зависимости от языковой привязки (wrapper), лучше всего наслаждайтесь неблокирующими формами методов .poll(), .send() и .recv().Собственный API-интерфейс наиболее прост в использовании в этом режиме: retCode = zmq_recv( ..., ZMQ_NOBLOCK );

2)
Всегда анализировать the retCode - будь то в беззвучном режиме или с пояснениями assert( retCode == 0 && zmq_errno() ) или иным образом.

3)
Лучший обзор и точная настройка всей конфигурации* атрибуты созданных экземпляров инструментов, доступных в платформе ZeroMQ, и использование всех их скрытых возможностей для наилучшего соответствия потребностям домена вашего приложения.Многие собственные настройки API-интерфейса могут помочь смягчить, если не принципиально, избежать большого количества противоречивых требований прямо в экземпляре Context() -инжиниринга, поэтому не стесняйтесь изучать все подробности возможных настроек и использовать их, насколько это возможно, для вашей помощи.код.


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 голос
/ 23 сентября 2019

Идиоматический способ проверки на наличие ошибок в 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 документацию.

Теперь, чтобы обратиться к этой части вопроса:

Учитывая следующие фрагменты кода, что будет наиболееправильный способ обработки исключения (в то время как)?Со структурой, которая у меня есть сейчас (как вы можете видеть ниже), zmq_close и zmq_ctx_destroy никогда не будут выполняться, но я хочу, чтобы они выполнялись в случае программной ошибки / исключения (независимо от источника).

Все zmq_* функции вернут ошибку и установят errno.Проверьте каждую функцию и break при возникновении ошибки.В этом случае опрос по неблокирующей функции лучше всего break из вашего цикла while при возникновении ошибки.

В Linux вы также можете установить обработчик сигнала и выполнить процедуру очисткикогда сингл поднимается (например, очень часто перехватывает SIGINT для правильного выхода из программы в UNIX при помощи ctrl + C в консоли).Посмотрите этот ответ

...