본문 바로가기
Software Architect/SWA 이야기

네트워크 프로그램 오류의 이해

by romainefabula 2022. 4. 9.

네트워크 프로그램은 자주 만들 일도 없고, 문제도 아주 가끔 발생하기 때문에 매번 새로운 느낌이다. 그래서, 네트워크 프로그램이 작동하는 방식과 오류가 발생하는 원인에 대해서 설명하려고 한다.

일반적으로 대부분의 네트워크 프로그램에서 사용하는 TCP는 연결지향(Connection-Oriented) 프로토콜로 커넥션을 수립(establish)한 후에 그 커넥션을 통해 클라이언트와 서버가 데이터를 송수신한다. 그 과정을 아래에 간단하게 그려 보았다.

연결지향 네크워크 프로그램 작동방식

 

1. 클라이언트가 서버의 IP와 포트를 이용해서 서버에 커넥션을 요청한다.
2. 서버가 클라이언트의 커넥션 요청에 수락 응답을 전송한다.
3. 커넥션이 수립되었고, 클라이언트가 서버로 요청데이터를 전송한다.
4. 서버는 클라이언트로부터 받은 요청데이터에 대해 처리한 후, 응답을 클라이언트로 전송한다.

일반적인 웹을 예로 들면, 웹브라우저는 클라이언트가 되고 웹서버는 서버가 된다. DB 접속 프로그램을 예로 들면, 애플리케이션은 클라이언트가 되고 클라이언트로부터 SQL을 받아서 처리하는 DB는 서버가 된다. 텔넷을 예로 들면, 텔넷 프로그램이 클라이언트가 되고 서버에 있는 텔넷 서버 프로그램이 서버가 된다.

커넥션 풀

여기서 클라이언트가 서버에 요청을 보낼 때마다 커넥션을 맺느라 소요되는 시간과 리소스를 절약하기 위해서 커넥션풀을 사용한다. 1,2단계를 통해서 만들어진 커넥션 객체를 풀에 넣어두고, 요청을 전송할 필요가 있을 때 바로 꺼내서 전송하고 응답을 받은 후엔 커넥션을 풀에 다시 반납한다.

 

네트워크 프로그램의 오류

보통은 네트워크 프로그램이 문제가 없지만, 아주 가끔씩 문제가 발생한다. 네트워크 프로그램에서 발생하는 오류의 종류와 원인을 알아 두면 어떤 부분을 점검해야 할지 알 수 있을 것이다.

 

Connection Refused

클라이언트가 IP와 포트를 이용해서 커넥션을 요청했는데 이 요청이 들어간 서버에 해당 포트번호를 Listen하고 있는 서버가 없다는 뜻이다.

원인 1 : 클라이언트에서 IP나 포트를 잘못 알았다
클라이언트가 IP와 포트 중 하나라도 잘못 알았다면 당연히 해당 서버에 그 포트를 Listen하는 서버는 떠 있지 않을 것이다.
클라이언트 프로그램에서 IP와 포트를 먼저 확인해 보자.

원인 2 : 서버프로그램이 내려갔거나 재기동중이다
클라이언트에서 접속하려는 서버프로그램이 내려간 상태이거나 어떤 이유로 재기동중이라서 아직 해당 포트를 Listen하고 있지 않은 경우이다.
서버 프로그램이 기동되어 있는지 확인해 보자.

 

Connection Timeout

클라이언트가 1번으로 서버에 커넥션을 요청할 때에는 커넥션 타임아웃을 설정해 둔다. 커넥션 요청을 보낸 후에 커넥션 수락을 얼마동안 기다릴지 정해 두는 것이다. 어딘가에 문제가 생겨 커넥션 수락이 오지 않을 경우에 프로그램이 대기상태에서 영원히 멈춰 있을 수는 없으니까. Connection Timeout은 정해진 시간까지 커넥션 수락이 오지 않아서 발생한다.

원인 1 : 방화벽에서 커넥션 요청을 먹어 버렸다
연결하려는 서버의 포트에 방화벽이 설정되어 있고, 클라이언트의 IP가 허용되지 않은 경우에 커넥션 요청이 방화벽에 막혀서 서버로 전달되지 않는다. 이런 경우, 아무리 기다려도 커넥션 수락이 오지 않아 커넥션 타임아웃이 발생한다.
경험상 커넥션 타임아웃이 발생하는 거의 대부분의 경우는 이것이 원인이었다. 그러니까, 방화벽부터 확인해 보자.

원인 2 : 서버가 너무 바빠서 커넥션 요청을 받기 힘들거나 요청을 받았어도 수락 응답을 줄 수가 없다
아주 가끔 서버에 요청이 몰리거나 문제가 생겨서 서버의 리소스가 거의 남지 않은 경우에 발생한다.
서버의 리소스를 확인해서 커넥션 요청을 처리할 수 있는 상태인지 확인해 보아야 한다.

원인 3 : 커넥션풀의 커넥션이 모두 사용중일 경우
원래 커넥션풀의 커넥션이 부족할 때는 이런 오류가 나면 안 될 것 같은데, 가끔 커넥션풀에서 이런 오류를 리턴하는 경우가 있다.
커넥션풀의 사용가능한 커넥션 갯수를 확인해서 이 값이 0이면 커넥션풀이 원인일 수 있다. 이 커넥션풀의 커넥션을 사용하는 프로그램 중에 수행시간이 오래 걸리는 프로그램이 있다면 문제를 해결해야 한다. 모든 프로그램이 문제 없으면 커넥션풀의 크기를 늘려야 한다.

 

Read Timeout

커넥션이 수립된 후에 클라이언트가 서버로 요청데이터를 전송한다. 이때에도 서버로부터 응답을 받을 때까지 얼마나 오랫동안 기다릴지 설정하는 것이 Read Timeout이다. 클라이언트가 요청데이터를 보낸 후 이 시간동안 서버로부터 응답데이터를 못 받으면 이 오류가 발생한다.

원인 1 : 서버에서 요청데이터를 처리하는 데 시간이 오래 걸린다
서버에서 요청데이터를 처리하다가 시간이 너무 오래 걸린 경우이다. 처리 프로그램에 문제가 있어서 오래 걸릴 수도 있고, DB에 요청했는데 SQL이나 인덱스, 락 등의 문제로 응답이 오지 않거나 다른 서버와 인터페이스 중에 응답이 안 오는 경우가 있다.
서버프로그램의 처리상태를 확인해 보아야 한다.

원인 2 : 서버프로그램의 대량데이터 조회
조회조건 등의 문제로 서버프로그램이 조회한 데이터를 응답데이터로 만들기 위해 가공하는데 시간이 오래 걸릴 수 있다.
조회조건 등을 제한해서 지나치게 많은 데이터가 조회되지 않도록 해야 하고, 대량데이터를 조회할 수 밖에 없는 상황이라면 클라이언트의 Read Timeout을 늘려 주어야 한다. 대량데이터를 처리하기 위해서는 많은 메모리가 필요하므로 OutOfMemory가 발생하지 않도록 메모리도 늘려 주어야 한다.

원인 3 : 네트워크 대역폭
아주 드물게 서버에서 클라이언트 방향으로 네트워크 대역폭이 좁은 경우, 네트워크를 통해서 응답데이터가 오는 데 시간이 오래 걸릴 수 있다.
아주 드문 경우이지만, 이런 경우도 고려해 볼 필요가 있다.

 

Too Many Open Files

파일을 열 때 뿐만 아니라, 네트워크 클라이언트 프로그램이 소켓을 열 때도 File Descriptor를 사용한다. 즉, 네트워크 소켓이 허용된 것보다 많이 열려고 시도하면 위와 같은 오류가 발생한다.
일단, 현재 열려 있는 파일과 소켓을 보고 싶으면 lsof나 pfiles 명령을 이용해서 확인할 수 있다.

원인 1 : ulimit의 open files 값이 너무 작다
open files 값이 1024와 같이 너무 작으면 기본적으로 열 수 있어야 하는 파일과 소켓을 열 수 없어서 오류가 발생한다. ulimit 명령을 이용해 충분히 수용 가능한 값으로 설정해 준다.

원인 2 : 파일이나 소켓을 연 후에 닫아 주지 않았을 때
파일이나 소켓을 연 후에 닫지 않으면 open files의 값을 계속 잡아 먹게 된다. 이런 것들이 쌓이게 되면 어느 순간 Too Many Open Files 오류가 발생하면서 더 이상 파일이나 소켓을 열지 못 하고 서버가 멈춘다. 열면 꼭 닫는 버릇을 들여야 한다.

원인 3 : 짧은 시간 안에 지나치게 많은 소켓을 열었다가 닫았을 때
아무리 소켓을 열었다가 사용 후에 잘 닫아도 프로토콜(특히 HTTP)에 따라서 실제로 커넥션이 OS단에서 끊기는 데 시간이 걸린다. 그러니까, 프로그램에서 소켓을 닫아도 어느 정도 후에 실제 OS에서 커넥션이 끊기므로, OS에서 실제로 끊기지 않은 소켓이 많이 쌓이면 이런 오류가 발생할 수 있다.
이런 일을 방지하기 위해서 커넥션풀을 사용한다. 커넥션 생성에 필요한 시간과 리소스사용을 줄일 수 있고, 끊길 때까지 대기하는 커넥션이 쌓이지 않기 때문에 이런 오류를 방지할 수 있다.

 

 

모쪼록 네트워크 프로그램 오류로 고생하는 분들에게 도움이 되길...