서버 사이드에서 어떤 작업을 하고 클라이언트 사이드로 응답을 하는 로직을 만드는 중 이었습니다.
사용하고 있는 서버 인프라가 특정 시간만큼만 작동하고 그 이상으로 작동할 시 timeout error를 내었습니다.
하지만 서버에서 해야하는 작업은 어떤 입력을 받았냐에 따라 얼마나 작동할 지 알 수 없는 상황이었습니다.
내린 결론은 특정 시간이 지나면 하던 작업을 마치고 클라이언트 사이드로 응답을 보내는 것이었습니다.
하지만 Python프로그램이 돌아가는 도중에 시간을 재고있던 다른 녀석이 돌아가는 녀석을 멈춰야 했습니다.
이 상황을 해결할 수 있는 것이 signal 모듈 입니다.
예제를 봅시다.
import signal
import time
class TimeOutException(Exception):
pass
def alarm_handler(signum, frame):
print("Time is up!")
raise TimeOutException()
def loop_for_n_seconds(n):
for sec in range(n):
print("{} second!".format(sec))
time.sleep(1)
signal.signal(signal.SIGALRM, alarm_handler)
signal.alarm(4)
try:
loop_for_n_seconds(6)
except TimeOutException as e:
print(e)
위의 코드를 간단히 설명하자면 우선, 6초동안 돌아가는 loop가 있습니다.
그리고 4초 동안 작업을 마치지 않으면 TimeOutException을 일으키는 signal.alarm이 있습니다.
loop이 try except문 안에서 돌고 있고 4초안에 마칠 수 없기 때문에 TimeOutException이 일어나게 됩니다.
그 에러를 except문이 잡게 되고 에러를 출력합니다.
잘 이해가 안되는 분들을 위해 하나씩 뜯어 봅시다.
import signal
import time
우선 우리가 필요한 signal 모듈과 time 모듈을 import하여 옵니다.
이 부분부터 이해가 되시지 않는다면 힘차게 화이팅을 외쳐봅시다.
class TimeOutException(Exception):
pass
간단하게 TimeOutException을 정의해 봅시다.
Exception에러를 상속하기만 하면 됩니다.
def alarm_handler(signum, frame):
print("Time is up!")
raise TimeOutException()
signal.alarm이 발생하게 되면 할 일들을 alaram_handler 내부에 정의해 줍니다.
파라미터는 이 예제에서는 수정할 필요가 없습니다.
signal.alarm이 발생하면 "Time is up!"이 출력되고 위에서 정의한 TimeOutException을 raise(발생) 시킵니다.
def loop_for_n_seconds(n):
for sec in range(n):
print("{} second!".format(sec))
time.sleep(1)
n의 숫자를 받으면 그 수초만큼 프로세스를 점유하여 1초마다 내부에 정의된 로직을 실행하게 할 수 있습니다.
물론 내부로직이 시간이 많이 걸린다면 이 예제에서 필요한 "1초마다"를 보장 할 수 없습니다.
이 예제에서는 단순히 몇초가 지났는지만 출력하기 때문에 약 1초마다 실행되는 것을 보장 할 수 있습니다.
time.sleep(1)이 부분이 1초동안 프로세스를 sleep시킵니다.
signal.signal(signal.SIGALRM, alarm_handler)
signal.alarm(4)
signal.signal에 첫 번째 인자로 signal.SIGALRM을 넘겨 alarm을 사용하는 것을 알립니다.
두 번째 인자로 위에서 정의한 alarm_handler를 넘겨 마무리합니다.
signal.alarm(4)는 4초 뒤에 alarm_handler 내부의 로직을 실행하겠다 라는 의미입니다.
try:
loop_for_n_seconds(6)
except TimeOutException as e:
print(e)
try 안에서 위에서 정의한 loop_for_n_seconds에 6을 넘겨 6초동안 프로세스를 점유하여 실행합니다.
만약 loop_n_seconds가 실행하는 동안 위에서 실행한 signal.alarm(4)이 먼저 발생하게 된다면
내부에 정의한 raise TimeOutException이 발생하게 될 것입니다.
TimeOutException이 발생하면 except문이 해당 exception을 잡아 실행하는 로직입니다.
여기서
timeout을 이용하여 서문에서 이야기한 문제를 해결 할 수 있습니다.
서버 인프라가 timeout을 내게되면 인프라가 문제를 발생시킨 것 또한 클라이언트로 전달할 수 없습니다.
그 전에 내부로직에서 스스로 timeout을 내고 그 exception을 잡아서
직접 그 시점까지 처리한 정보와 함께 시간이 넘어섰다라는 정보를 넘깁니다.
그렇다면 클라이언트는 그것을 알고 이후의 행동을 할 수 있습니다.
두 가지 더!
마지막에서 signal.alarm(0)을 실행해 주지 않으면 alarm_handler내부의 로직은 loop이 끝났어도 alarm에 설정한 시간이 끝나면 실행됩니다.
signal모듈은 UNIX 운영체제에서만 실행됩니다.
MacOS에서 실행되는 것을 확인하였고 Ubuntu Linux에서 실행되는 것은 확인하였습니다.
읽어주셔서 감사합니다. :)
다른 글 보기
'개발과 기술' 카테고리의 다른 글
Next JS 프로젝트에 Google Analytics 적용하는 방법 (5) | 2020.04.28 |
---|---|
[Next JS] Next JS + Material UI + Styled Component 설정방법 (7) | 2020.04.22 |
[Prisma] Prisma(Graphql) 로컬서버 설정 방법(+ docker 설정 방법) - PART 2/2 (0) | 2020.03.31 |
[Prisma] Prisma(Graphql) 로컬서버 설정 방법(+ docker 설정 방법) - PART 1/2 (0) | 2020.03.31 |
[Ubuntu] umask 명령어 (0) | 2018.11.28 |
댓글