Skip to content
게으른 엔지니어의 기술 블로그
Go back

고쳤는데도 안 됐다, 진짜 이유는 따로 있었다

막힌 이유를, 몇 번이고 다시 찾았다 · 6/9편

5편에서 네이버 크롤링을 실전 스케일(타깃 21개)로 옮기자 동시 요청이 이론상 최대 20개까지 몰릴 수 있다는 걸 계산으로 확인했다. 이번엔 그 계산이 진짜인지 직접 세어보기로 했다.

실측 — 순간 최대 14개가 동시에 몰렸다

21개 타깃 전체를 다시 짧게 돌리면서, 이 프로세스가 맺은 소켓 연결을 1초 간격으로 20번 샘플링했다. 직접 IP로 가는 연결과 프록시(127.0.0.1)로 가는 연결을 구분해서.

순간 최대 동시 연결 14개(직접 3 + 프록시 11), 개별로는 직접 최대 7개, 프록시 최대 11개. 진단 규모(타깃 2개)에서는 최대 4개였던 것과 비교하면 3~5배였다. 이론상 최대치(20개)에는 못 미쳤지만 — 타깃들이 항상 정확히 같은 순간에 네트워크 요청 중은 아니라서 — 실질적으로는 그 이론치에 가깝게 몰리고 있었다.

한 가지 더 눈에 띈 게 있었다. 순간적으로는 프록시 쪽이 직접보다 더 많이 몰리는 경우도 있었다(11 vs 3). 이 프록시는 다른 프로젝트의 크롤러도 같이 쓰는 회선이었다 — 너무 몰아치면 그쪽 작업에도 영향이 갈 수 있다는 뜻이라, 더 조심해야 할 이유가 하나 늘었다.

IP당 동시 요청을 2개로 강제하다

타깃이 몇 개 동시에 처리되든, 실제 네트워크 요청만큼은 IP당 정확히 2개로 묶어두기로 했다. 각 IP로 요청을 보내는 지점에 “동시에 2개까지만” 이라는 잠금 장치를 걸었다 — 이 잠금은 실제 요청-응답 구간에만 걸려 있고, 딜레이나 403 백오프로 대기하는 동안에는 풀려 있어서, 기다리는 다른 요청이 그 자리를 바로 이어받을 수 있게 했다.

같은 방식(21타깃 실행 + 소켓 25회 샘플링)으로 다시 확인했다. 이번엔 직접/프록시 둘 다 한 번도 2를 넘지 않았다. 동시성은 확실히 잡혔다.

그런데 오히려 더 나빴다

동시성을 잡은 채로 21타깃 전체를 다시 돌렸다. 8분 만에 “직접 +프록시 둘 다 실패” 397건, 폴백 성공은 0건이었다. 분당 실패율로 계산하면, 동시성을 안 잡았던 이전 시도(16분에 764건, 분당 약 47.75건)와 사실상 같거나 오히려 살짝 더 나빴다(분당 약 49.6건).

동시 연결 수를 확실히 2개로 낮췄는데도(방금 실측으로 확인했으니) 실패율이 전혀 줄지 않았다.

열두 번째로 다시 찾은 이유 — 네이버 크롤링 IP 차단 원인

이 결과는 “동시성이 너무 높아서”라는 설명만으로는 부족하다는 뜻이었다. 대신 훨씬 유력한 설명이 하나 남았다 — 오늘 하루, 두 IP(직접, 프록시) 모두에 이미 상당한 양의 요청을 쐈다는 것. 이번 시리즈의 진단 실험들(5편부터 여기까지)과 실전 스케일 시도 두 번을 다 합치면, 오늘 하루 동안 이 두 IP로 보낸 요청은 결코 적은 양이 아니었다.

이건 2편에서 처음 세웠던 “누적 요청량 기반 시간창 rate-limit” 가설이 오늘 다시 소진 상태로 돌아온 것이었다. 특히 폴백 성공이 0건으로 떨어졌다는 게 결정적인 신호였다 — 지금까지는 직접이 막혀도 프록시는 거의 항상 뚫렸는데, 이번엔 그 프록시조차 오늘의 반복된 테스트로 지쳐 있었다는 뜻이다.

그래서 어디까지 왔나

코드 쪽 개선(요청 딜레이, 403 백오프 재시도, 프록시 폴백, field=1로 물량 축소, 두 IP 병렬화, IP당 동시성 제한)은 전부 유효하고 필요한 변경이었다. 다만 오늘처럼 두 IP가 이미 상당히 지친 상태에서는, 그 개선들만으로 완전한 안정성을 보장할 수 없었다. 오늘은 여기서 멈추고, 두 IP를 충분히 식힌 뒤 다시 시도하기로 했다. 다음에 재개할 때 가장 먼저 확인할 것은 하나다 — 완전히 식은 상태에서 실전 스케일 전체가 한 번에 끝까지 돌아가는지.

남는 생각

열두 번의 실험 동안, “이게 원인이다”라고 생각한 게 매번 조금씩 틀렸다. 처음엔 동시성이라고 생각했고, 그다음엔 시간창이라고 생각했고, 그다음엔 순수한 물량이라고 생각했고, 다시 동시성으로 돌아갔다가, 결국 그 어느 쪽도 아니었다. 진짜 원인은 코드 안에 없었다 — 원인을 찾으려고 그날 보낸 요청들 자체가, 다음 진단을 더 어렵게 만드는 조건이 되고 있었다.

이게 이 시리즈에서 가장 크게 남는 지점이다. 문제를 진단하는 행위 자체가 그 문제의 조건을 바꿔놓을 수 있다는 것, 그리고 그 바뀐 조건이 다음 결론을 왜곡할 수 있다는 것. “고쳤다”와 “끝났다” 는 다른 말이었다. 열두 번의 실험 중 열한 번은 “무엇을 고쳐야 하는가”를 좁혀가는 과정이었지만, 마지막 한 번은 “지금 이 실험 자체가 답을 볼 수 있는 조건인가”를 먼저 물어야 했다는 걸 알려준 셈이다.


Share this post:

막힌 이유를, 몇 번이고 다시 찾았다

  1. 1. 네이버 뉴스 검색 403, 3개월치를 순차로 돌렸는데 한 번도 안 막혔다
  2. 2. 차단됐다는 증거부터 없었다
  3. 3. 식은 IP도 순식간에 막혔다, 고쳤더니 이번엔 너무 느렸다
  4. 4. 프록시는 한 번도 쓸 일이 없었다
  5. 5. 두 배 빨라졌다, 그리고 다시 막혔다
  6. 6. 고쳤는데도 안 됐다, 진짜 이유는 따로 있었다
  7. 7. 순서를 뒤집자, 패턴도 정확히 뒤집혔다
  8. 8. 가장 많이 죽던 자리를 그대로 두고 다시 돌렸는데, 이번엔 다 살았다
  9. 9. 30분에서 1분까지, 얼마나 줄일 수 있는지 끝까지 밀어봤다