2016-01-22 22 views
15

Piszę serwer, który obsługuje zdarzenia, a nieprzechwycone wyjątki podczas obsługi zdarzenia nie mogą powodować zakończenia działania serwera.Solidna, nieskończona pętla dla serwera napisana w Pythonie

Serwer jest pojedynczym, nietraktowanym procesem python.

chcę zakończyć na tych rodzajów błędów:

  • KeyboardInterrupt
  • MemoryError
  • ...

Lista wbudowany wyjątków jest długa: https://docs.python.org/2/library/exceptions.html

Nie chcę ponownie wymyślać tej obsługi wyjątku, ponieważ domyślam się, że to było zrobione sever al razy wcześniej.

Jak postępować?

  1. prowadzenia White-list: lista wyjątków, które są ok, a przetwarzanie następną imprezę jest właściwym wyborem
  2. Mają czarną listę: Lista wyjątków, które wskazują, że kończące serwer jest prawo wybór.

Wskazówka: To pytanie nie dotyczy uruchamiania demona unixowego w tle. Nie chodzi o podwójnym widelcem, a nie o przekierowanie stdin/stdout :-)

Odpowiedz

2

najwyższym, wyjątkiem jest BaseException. Istnieją dwie grupy na podstawie, że:

  • Exception pochodzi
  • wszystko inne

miejsca jak Stopiteration, ValueError, TypeError, etc., wszystko to są przykłady Exception.

Rzeczy takie jak GeneratorExit, SystemExit i KeyboardInterrupt nie pochodzą z Execption.

Pierwszym krokiem jest złapanie Exception, a nie BaseException, co pozwoli w łatwy sposób zakończyć działanie programu. Polecam również złapanie GeneratorExit jako 1) nigdy nie powinno być widoczne, dopóki nie zostanie podniesione ręcznie; 2) możesz go zalogować i zrestartować pętlę; i 3) ma na celu zasygnalizowanie, że generator się zakończył i można go wyczyścić, a nie, że program powinien wyjść.

Następnym krokiem jest zarejestrowanie każdego wyjątku z wystarczającą ilością szczegółów, aby można było zorientować się, co poszło nie tak (kiedy później przejdziesz do debugowania).

Wreszcie, musisz sam zdecydować, które, jeśli w ogóle, z Exception wyjątków pochodzących chcesz wypowiedzieć się na: Proponuję RuntimeError i MemoryError, chociaż może być w stanie obejść te po prostu zatrzymanie i ponowne uruchomienie pętla serwera.

Tak naprawdę, to zależy od Ciebie.

Jeśli istnieje jakiś inny błąd (np IOError gdy próbuje załadować plik konfiguracyjny), który jest na tyle poważne, aby rzucić na to, że kod odpowiedzialny za ładowanie pliku konfiguracyjnego powinna być na tyle inteligentny, aby złapać że IOError i podnieść SystemExit zamiast.

Jeśli chodzi o białą listę/czarną listę, należy użyć czarnej listy, ponieważ powinno być tylko kilka, jeśli w ogóle, wyjątków, które należy faktycznie zakończyć na serwerze.

+1

Może to również pomóc w podjęciu decyzji o tym, które z wyjątków zarządzać: Wbudowana hierarchia wyjątków: https://docs.python.org/3.5/library/exceptions.html#exception-hierarchy – Timur

4

Chciałbym to zrobić w podobny sposób myślisz o, przy użyciu „nie przekaże” Gandalf obsługi wyjątku except Exception to catch all non-system-exiting exceptions podczas tworzenia wyszczególnionego na czarnej liście set wyjątków, które powinny zostać przekazane, a na końcu ponownie przebite.

Używanie Gandalf obsługi zadba GeneratorExit, SystemExit i KeyboardInterrupt (wszystkie wyjątki systemowe wyjściu) przechodzi i zakończyć program, jeśli nie ma innych teleskopowe są obecne wyższe w stosie wywołań. Tutaj można sprawdzić pod numerem type(e), czy __class__ przechwyconego wyjątku e rzeczywiście należy do zestawu wyjątków wymienionych na Czarnym i jest w tym przypadku ponownie.

Jako mały pokaz:

import exceptions # Py2.x only 

# dictionary holding {exception_name: exception_class} 
excptDict = vars(exceptions) 

exceptionNames = ['MemoryError', 'OSError', 'SystemError'] # and others 

# set containing black-listed exceptions 
blackSet = {excptDict[exception] for exception in exceptionNames} 

Teraz blackSet = {OSError, SystemError, MemoryError} trzymając klasy non-system-wychodzenia wyjątki chcemy nie uchwytem.

try-except blok może teraz wyglądać następująco:

try: 
    # calls that raise exceptions: 
except Exception as e: 
    if type(e) in blackSet: raise e # re-raise 
    # else just handle it 

przykład który łapie wszystkie wyjątki używając BaseException może pomóc zilustrować, co mam na myśli. (robi się to tylko dla celów demonstracyjnych , aby zobaczyć, jak to podniesienie ostatecznie zakończy twój program).Notatka: Jestem nie sugeruje użycie BaseException; Używam go w celu wykazać co wyjątek rzeczywiście „przejść” i spowodować Termination (czyli wszystko co BaseException połowy):

for i, j in excptDict.iteritems(): 
    if i.startswith('__'): continue # __doc__ and other dunders 
    try: 
     try: 
      raise j 
     except Exception as ex: 
      # print "Handler 'Exception' caught " + str(i) 
      if type(ex) in blackSet: 
       raise ex   
    except BaseException: 
     print "Handler 'BaseException' caught " + str(i) 

# prints exceptions that would cause the system to exit  
Handler 'BaseException' caught GeneratorExit 
Handler 'BaseException' caught OSError 
Handler 'BaseException' caught SystemExit 
Handler 'BaseException' caught SystemError 
Handler 'BaseException' caught KeyboardInterrupt 
Handler 'BaseException' caught MemoryError 
Handler 'BaseException' caught BaseException 

Wreszcie, w celu uczynienia tej Python 2/3 agnostyk , możesz try i import exceptions, a jeśli to się nie powiedzie (co robi w Pythonie 3), powróć do importowania builtins, która zawiera wszystkie Exceptions; szukamy słownika nazwy więc nie ma różnicy:

try: 
    import exceptions 
    excDict = vars(exceptions) 
except ImportError: 
    import builtins 
    excDict = vars(builtins) 

nie wiem, czy jest lepszy sposób, aby rzeczywiście to zrobić, innym rozwiązaniem może być zamiast mieć try-except z signle except, mając 2 koparki, jeden na czarnej liście wyjątków i druga dla przypadku ogólnym:

try: 
    # calls that raise exceptions: 
except tuple(blackSet) as be: # Must go first, of course. 
    raise be 
except Exception as e: 
    # handle the rest 
Powiązane problemy