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')