ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [python] CLOSE_WAIT 해결 방법 with TimeoutIterator
    Programming Language/Python 2021. 5. 7. 11:20
    반응형
    • tcp 연결된 소켓의 상태가 CLOSE_WAIT 상태에서 처리되지 못하고 계속 소켓을 잡고 있는 상황이 발생하여 해당 내용을 정리한다

    0. 용어

    • end, end-point : aplication, user 등 연결에서 한 쪽 끝 프로세스
    • tcp 상태

    1. 개념 정리

    • CLOSED : 연결이 없음
    • LISTEN : Passive open, SYN을 기다리는 상태
    • SYN-SENT : SYN을 보내고 ACK를 기다리는 상태
    • SYN-RECEIVED(SYN-RCVD) : SYN+ACK을 보내고 ACK를 기다리는 상태
    • ESTABLISHED : 커넥션이 생성된 상태, 데이터를 전송할 수 있다.
    • FIN-WAIT-1 : 첫 FIN이 보내진 상태, ACK를 기다리고 있다.
    • CLOSE-WAIT : 첫 FIN을 받고 ACK를 보낸 상태, OS 는 프로그램의 연결 종료를 기다리고 있다.
      • 문제는 프로그램이 실제로 socket 을 close 하지 않아서 발생
      • TCP 튜닝 이슈가 아님 - TCP 튜닝을 해도 소용없다는 것
      • 프로그램이 connection 을 계속 유지하면 CLOSE_WAIT 상태는 프로세스가 내려가기 까지 영원히 유지됨
      • 따라서, 해당 연결을 유지하는 application 을 확인 해야함.
      •  
    • FIN-WAIT-2 : 첫 FIN에 대한 ACK를 받은 상태, 2번째 FIN을 기다리고 있다.
    • LAST-ACK : 2번째 FIN을 보내고 ACK를 기다리는 상태
    • CLOSED : 양쪽이 동시에 닫기로 한 상태
    • TIME-WAIT : CLOSED 상태로 가기전에 MSL 의 2배가 지날 때 까지 기다린다 - 상대 가 ACK 를 받았는지 확신하기 위해서
      • MSL(the maximum segment lifetime) : 동일 연결이 생성 되지 않도록 대기하는 상태

    2. CLOSE_WAIT 문제

    • 각 어플리케이션마다 장시간 block 이 되는 코드가 있어서 발생하는 문제
      • 각자의 방법으로 해결이 필요
    • 아래는 iterator 를 사용하여 발생한 block 에 대한 처리 내용
      • flask 로 개발한 RESTful API 의 동작에서 결과를 장시간 내보내지 못하고 처리/대기 하는 iterator 가 있어, 요청 user 가 연결을 끊어도 서버에서는 CLOSE_WAIT 상태로 연결이 남아있는 이슈가 발생
      • user 는 여러번 해당 요청을 하여 결국 서버입장에서는 해결하지 못한 동작을 CLOSE_WAIT 상태로 계속 유지 중이고, 자원이 그만큼 낭비 됨에 따라 서버가 점차 과부화가 됨
      • 해결을 위해서 iterator 에 timeout 시간을 적용 했고, 해당 시간안에 처리되지 못하는 동작은 실패로 응답을 할 수 있도록 처리
      • 아래는 python 의 iterator 에 timeout 을 적용하여 간단하게 테스트한 코드
    import time
    from iterators import TimeoutIterator
    
    
    # 3초 마다 숫자를 리턴하는 iterator 함수
    def my_iter():
        for i in range(100):
            time.sleep(3)
            yield i
    
    # sentinel : timeout 이 발생 했을 때 리턴되는 값, default = object()
    a = TimeoutIterator(my_iter(), timeout=1, sentinel='TIMEOUT')
    
    for res in a:
        # default 값 사용시 리턴되는 값을 체크하는 방법
        # if res == a.get_sentinel():
    
        if res == 'TIMEOUT':  # timeout 이 발생하면 여기서 체크되어 break 됨
            print('time error')
            a.interrupt()  # 여기서 interrupt 를 하지 않으면 코드가 멈춘 상태로 유지됨 -> python exit 을 하지 못함
            break
        print(res)
    
    print('last')

    Reference

     

     

    반응형

    댓글

Designed by Tistory.