원격 장비에서 자료를 받아오는데 큰 응답이 자꾸 끊겼다. 작은 요청은 되고 큰 것만 실패한다.
응답 시간이 오래 걸려서 그런 것으로 보고 소켓 타임아웃을 늘렸다.
struct timeval tv = { .tv_sec = 60 };
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
여전히 30초쯤에 끊겼다.
Table of contents
Open Table of contents
내가 안 만진 타임아웃이 있었다
SO_RCVTIMEO 를 늘렸는데 동작이 안 바뀌면 그 값이 실제로 안 쓰이고 있다는 뜻이다. 요청이 지나가는 경로를 처음부터 그려 봤다.
웹 화면 → 웹서버 → 게이트웨이 프로그램 → 시리얼/소켓 → 장비
각 구간에 타임아웃이 있었다.
| 계층 | 설정 위치 | 값 |
|---|---|---|
| 브라우저 | — | 브라우저 기본 |
| 웹서버 프록시 | proxy_read_timeout | 30초 |
| 스크립트 실행 | max_execution_time | 30초 |
| 소켓 | SO_RCVTIMEO | 내가 60초로 바꾼 것 |
| 장비 자체 | 펌웨어 상수 | 확인 필요 |
proxy_read_timeout 과 max_execution_time 이 30초였다. 한 요청에 여러 계층이 각자 시간을 재면 가장 짧은 것이 이긴다.
안쪽을 아무리 늘려도 바깥이 먼저 끊는다. 한 곳만 보고 고친 것이 이번 착오의 전부였다.
어디서 끊겼는지 구분했다
로그만 보면 전부 타임아웃으로 보인다. 어느 계층인지를 가를 근거가 둘 있었다.
첫째는 끊기는 시각이다. 30초에서 끊기면 그 값을 가진 계층이고 60초까지 가면 SO_RCVTIMEO 쪽이다.
둘째는 끊긴 뒤 오는 응답이다. 웹서버가 끊으면 502 쪽 화면이 오고 max_execution_time 이면 실행 시간 초과가 온다.
소켓이 끊기면 우리 코드가 오류를 만드는데 시각과 응답을 같이 보니 proxy_read_timeout 이었다.
설정 위치를 지도로 만들었다
한 곳을 고쳤다고 끝이 아니었다. 다시 겪지 않으려고 어디에 무엇이 있는지를 적어 뒀다.
[웹서버]
/etc/httpd/conf.d/proxy.conf ProxyTimeout
→ 전역 설정이다. 이 서버의 모든 vhost에 걸린다
[스크립트]
php.ini max_execution_time
→ CLI와 웹이 다른 ini를 쓴다. 웹 쪽을 봐야 한다
[게이트웨이 프로그램]
config.ini socket_timeout_sec
→ 우리 코드. 소스에도 기본값이 있어 둘 다 확인
[장비]
펌웨어 상수 확인 불가, 문의 필요
파일 경로와 항목 이름을 함께 적으니 다음에 찾는 시간이 없어졌다. php.ini 처럼 웹과 명령줄이 다른 파일을 쓰는 것도 적어 뒀다.
여기서 전역인지 개별인지를 함께 적은 것이 중요했다. ProxyTimeout 은 전역이라 늘리면 다른 화면도 전부 늘어난다.
조치 — 전역 대신 경로별로
특정 경로만 길게 하려면 그 경로에만 걸면 된다.
<Location /device/bulk>
ProxyTimeout 120
</Location>
/device/bulk 만 120초로 덮어쓰고 나머지는 30초로 둔다.
이렇게 한 이유가 있다. 전역을 120초로 늘리면 원래 빨리 끊겨야 할 요청도 2분을 기다린다.
장애가 났을 때 응답이 안 오면 사용자가 2분간 흰 화면을 본다. 타임아웃은 짧을수록 좋고 길어야 하는 경로만 골라 늘리는 것이 맞았다.
늘리는 게 답이 아닌 경우
이번에는 늘려서 풀렸지만 그러면 안 되는 경우도 있었다. 응답이 오래 걸리는 이유가 양이 아니라 처리 속도면 ProxyTimeout 을 늘려도 증상만 미루는 것이다.
사용자는 여전히 오래 기다리고 응답 시간이 계속 늘어나는 구조면 언젠가 늘린 값도 넘는다.
그때는 타임아웃이 다시 문제가 되는데 이미 한 번 늘려 뒀으니 또 늘리게 된다. 그렇게 값만 커지다 보면 어느 순간 아무도 그 값이 왜 그 숫자인지 모른다.
판단 기준 — 늘리기 전에 묻는 것
그래서 늘리기 전에 셋을 묻기로 했다.
이 시간이 데이터 양에 비례하나, 아니면 고정으로 느린가
앞으로 데이터가 더 늘어나나
나눠서 받을 수 있나 (페이지네이션, 청크)
첫째가 비례가 아니면 ProxyTimeout 을 늘려도 안 풀리고 둘째가 그렇다면 지금 늘린 값도 나중에 넘는다.
이번 건은 대량 조회라 양에 비례했고 나눠 받는 것이 가능했다. 그래서 타임아웃은 안전 여유로 조금만 늘리고 요청을 쪼개는 쪽으로 바꿨다.
정리
- 한 요청에 타임아웃이 여러 계층에 있고 가장 짧은 것이 이긴다
- 값을 늘렸는데 안 바뀌면 그 값이 안 쓰이는 것이다
- 안쪽만 늘리면 바깥이 먼저 끊으므로 경로 전체를 그려 본다
- 끊기는 시각과 끊긴 뒤 오는 응답으로 어느 계층인지 가른다
- 설정 위치를 파일 경로와 항목 이름까지 적어 지도로 만든다
- 전역인지 개별인지를 함께 적어야 무엇이 같이 바뀌는지 안다
<Location>으로 그 경로만 덮어쓰고 전역은 그대로 둔다- 전역을 늘리면 빨리 끊겨야 할 요청도 오래 기다린다
- 늘리기 전에 비례하는지와 더 늘어나는지와 나눌 수 있는지를 묻는다