Tratamento de exceção em um programa que precisa ser executado 24/7

14

Eu li que devemos capturar apenas exceções que podem ser tratadas, o que torna a captura da classe de exceção base (C # neste caso) uma má idéia (por outras razões). Atualmente, faço parte de um projeto no qual, até agora, ainda não vi nada além da exceção básica sendo capturada. Mencionei que é considerado uma prática ruim fazê-lo, mas a resposta foi "Este serviço precisa ser executado 24 horas por dia, 7 dias por semana, e é assim que é".

Como não tive uma boa resposta sobre como lidar adequadamente com exceções em um programa que precisa ser executado 24/7, agora estou aqui. Não consegui encontrar nenhuma informação / sugestão sobre como lidar com o tratamento de exceções em programas / serviços "críticos" que precisam ser executados o tempo todo (e, neste caso, acredito que pode ser bom se o serviço estiver inativo por um minuto ou dois, portanto, nem mesmo críticos). Eu entendo que depende da natureza exata do programa. Os requisitos para um programa que pode causar problemas com risco de vida são bastante diferentes em comparação com um scanner de registro para um jogo online.

Dois exemplos:

1: Um serviço de digitação antecipada para clientes das ferrovias britânicas, usado quando eles pesquisam on-line estações ferroviárias.

2: Um programa que controla automaticamente os comutadores ferroviários das ferrovias acima, com base em informações em tempo real fornecidas por vários sensores nos trilhos, trens etc.

O primeiro programa provavelmente não causaria um grande problema se fosse interrompido por um minuto ou dois, sendo que o último poderia causar vítimas humanas. Sugestões sobre como lidar com cada um? Ponteiro para onde posso encontrar mais informações e pensamentos sobre esse assunto?

user1323245
fonte
2
Desenrolar a pilha durante o tratamento de exceções em um aplicativo em tempo real (sic!) Pode destruir um trem.
Deer Hunter
4
@DeerHunter Uma codificação incorreta, sem exceções, pode ter o mesmo resultado.
BЈовић
9
Ok, então você catch Exception. Isso não significa que o seu programa funcione , significa que as falhas permitem que o estado do aplicativo seja corrompido enquanto continua sendo executado, um lugar muito mais perigoso para se estar. Um caiu programa pode ser desastroso, mas um programa que está em um estado inválido, mas ainda realizar ações pode ser ativamente desastroso.
Phoshi
1
Se o aplicativo precisar ser executado 24 horas por dia, sete dias por semana, haverá um loop infinito em algum lugar, e é melhor envolver esse loop infinito em torno de uma construção que captura todas as exceções sem tratamento. Se não for esse o caso, uma exceção não tratada será aplicada ao manipulador já existente que está fora do main e ao kaboom! o aplicativo 24/7 termina.
David Hammen

Respostas:

7

Certos recursos de idioma, como

  • Coleta de lixo
  • Sistemas de exceção
  • Avaliação preguiçosa

geralmente não são úteis em um sistema em tempo real. Provavelmente, deve-se escolher um idioma sem esses recursos e tentar provar certas propriedades, como uso máximo de memória ou tempo máximo de resposta.


Quando um programa precisa ser executado continuamente, mas falhas curtas e não globais são aceitáveis, poderíamos usar uma estratégia do tipo Erlang. Erlang é uma linguagem de programação funcional e simultânea. Geralmente, um programa escrito em Erlang consiste em vários processos de trabalho que podem se comunicar (modelo de ator). Se um segmento de trabalho encontrar uma exceção, ele será reiniciado. Embora isso implique um curto tempo de inatividade, os outros atores podem continuar como de costume.

Para resumir isso: Em um programa robusto, várias partes são isoladas umas das outras e podem ser reiniciadas ou dimensionadas independentemente.

Então, basicamente, precisamos de um pedaço de código equivalente a isso:

while (true) {
  try {
    DoWork();
  }
  catch (Exception e) {
    log(e);
  }
}

além de uma maneira de terminar o loop. Esse loop conduziria cada segmento de trabalho.


Um problema ao ignorar erros por meio de um catch-all é que os invariantes do seu programa podem ter sido violados pela causa do erro e que as operações subsequentes podem ser inúteis. Uma boa solução para isso é não compartilhar dados entre trabalhadores independentes. Reiniciar um trabalhador reconstruirá todos os invariantes necessários. Isso significa que eles devem se comunicar de maneira diferente, por exemplo, através do envio de mensagens. O estado de um ator pode não fazer parte dos invariantes de outros atores.

Outro problema ao capturar muitas exceções é que nem todas as exceções são corrigíveis ao reiniciar, mesmo ao tomar essas precauções. Caso contrário, problemas difíceis, como ficar sem memória, podem ser resolvidos reiniciando. Mas uma reinicialização não ajudará você a recuperar a conectividade com a Internet quando um cabo físico foi retirado.

amon
fonte
1
Sim, mas a situação como um "cabo físico foi retirado" é exatamente quando você deseja que o log de exceção seja preenchido até que alguém volte a colocar o cabo, e tudo começa a funcionar novamente, sem a reinicialização manual do aplicativo.
Mark Hurd
2

Para responder sua pergunta, é preciso entender o que são exceções e como elas funcionam.

Exceções geralmente são lançadas quando esses erros ocorrem, onde a assistência do usuário é necessária. Nesses casos, não importa quanto tempo leva para relaxar a pilha e manipular a exceção.

Sem manipuladores de captura, o programa interrompe a execução. Dependendo da sua configuração e requisitos, pode ser aceitável.

Nos seus casos específicos:

  1. se a consulta não puder ser executada (por exemplo, nome da cidade errado), informe o usuário sobre o erro e peça para corrigi-lo.
  2. se você não está obtendo informações de um sensor crítico, não faz muito sentido continuar sem pedir ao operador que corrija o problema.

Isso significa que, em ambos os casos, pode fazer sentido usar exceções, com mais cuidado em um programa de RT para indicar apenas problemas sérios onde não é possível continuar a execução.

BЈовић
fonte
1

Até agora, ainda não vi nada além da exceção básica sendo capturada.

Parece que há um problema aqui, na medida em que as exceções não estão sendo tratadas adequadamente. Capturar exceções no ponto apropriado e tomar as medidas adequadas (dependendo do tipo de exceção) manterá o serviço em execução de uma maneira muito mais confiável.

Se o serviço precisar continuar, presumivelmente é importante que esteja funcionando conforme o esperado. Como exemplo, se um programa que controla interruptores ferroviários lança uma exceção, isso pode indicar que há um problema na comunicação com sensores relacionados à segurança. Se você capturar a exceção base e continuar, o serviço poderá ser executado, mas poderá não funcionar como planejado, levando ao desastre.

Como alternativa, se você capturar a exceção lançada quando houver uma falha de comunicação com o sensor e lidar com ela adequadamente (por exemplo, parar os trens na área afetada), seu serviço estará funcionando e você não matou ninguém.

Portanto, como eu entendo a pergunta, sugiro que, em primeira instância, você inclua um tratamento de exceção mais específico em vez de remover os manipuladores do tipo de exceção base.

Matt
fonte
0

Com relação ao ponto 2: não use C #. Não é uma linguagem em tempo real e você vai se machucar se você tentar usá-lo como tal.

Para o ponto 1: você pode seguir o caminho errado: deixe travar e reinicie

miniBill
fonte
Meu uso e experiência em C # não estão relacionados ao ponto 2 (troca de faixa em tempo real). Estou curioso por que o C # é tão inadequado para essa tarefa?
Michael O'Neill
1
Principalmente: o coletor de lixo torna o comportamento do programa imprevisível em relação ao tempo. Além disso, o tempo de execução é muito complexo e, nesses contextos, você precisa de coisas simples, elas são mais previsíveis.
miniBill
0

Declaimer: são apenas pensamentos, não tenho experiência.

Eu acho que um programa que atenda aos requisitos do segundo exemplo deve ser extremamente modular . Conseqüentemente, os módulos poderão ser reiniciados, sem desestabilizar o sistema.

Por exemplo, um objeto, na falta de uma afirmação de estado interno, deve poder ser destruído e recriado, notificando no processo todos os seus consumidores e fornecedores. Mais concretamente, se o programa estiver controlando os interruptores da ferrovia e falhar em uma afirmação no ciclo de decisão, ele ainda poderá executar um módulo de emergência, que interrompe todos os trens envolvidos e aguarda a reinicialização do módulo principal de decisão.

Mais realisticamente, introduziríamos redundância - duplicação de hardware e software. Uma instância é conectada ao sistema controlado e a outra é de execução livre. Se um erro for detectado, os sistemas serão trocados.

Um exemplo são dois processos na mesma máquina, que monitoram um ao outro e, se um é morto, o outro o gera novamente e desassocia seu PID pai de si mesmo.

Vorac
fonte