http: Accept error: ... : too many open files

byron1st·2020년 12월 27일
0

배경

얼마 전에 겪었던 일이다. 상황은 이렇다. 우리 회사의 백엔드 시스템은 게이트웨이 서버를 포함, 5개의 마이크로서비스 서버들로 구성되어 있다. 이들은 오고가는 HTTP Request/Response를 로그로 기록하는데, 이를 중앙화된 로그 서버로 전송한다. 이를 위해 각 서버들은 직접 중앙화된 로그 서버에서 제공하는 REST API를 이용하여 로그를 전성한다. (로그 서버에 직접 HTTP 호출로 로그를 전송하는게 bad practice 라는 것을 나도 안다. 개선해야 할 부분 중 하나인데, 일단은 넘어가자) 서버들은 모두 Go 언어로 작성되어 있다.

버그

http: Accept error: accept tcp [::]:9090: accept: too many open files;

로그 서버는 랜덤하게 위의 에러를 콘솔에 뱉어냈다. http 모듈에서 에러가 났는데, too many open files 라는 메세지가 이상했다. 난 파일을 사용하고 있지 않은데, 왜 너무 많은 파일들을 열었다고 에러가 발생하는 것일까.

이유

이유는 검색을 통해 발견한 Stackoverflow 질문글에서 찾을 수 있었다. 해당 질문글의 답변들 중 이런 내용이 있다.

When a response is received regardless of whether response-body is required or not, the connection is kept alive until the response-body stream is closed. So, as mentioned in this thread, always close the response-body. Even if you do not need to use/read the body content:
(Response 가 도착하면, response-body를 쓰던 말던, 해당 response-body stream이 닫히기 전까진 커넥션은 살아있다. 그러므로, 이 스레드에서 언급된 바와 같이, response-body는 항상 닫아줘야 한다. 비록 당신이 body content를 읽거나 사용하지 않는다 할지라도.)

나의 마이크로서비스 서버들은 로그 서버로 로그를 전송만 하고 답변을 딱히 기다리지 않고 다음 작업을 한다. 즉, 로그가 실패하더라도 메인 기능이 동작하는데 영향을 주지 않도록 짰기 때문에 로그 서버로부터의 리스폰스를 딱히 체크하지 않았던 것이다.

해결

아래 코드를 로그 서버로부터의 리스폰스를 받는 부분에 추가함으로써 해결할 수 있었다.

defer func() {
	err := response.Body.Close()
	if err != nil {
		log.Printf("Error occurs: %+v", err)
	}
}()

원래는 defer response.Body.Close() 한줄을 추가해도 되는 부분인데, 이렇게만 할 경우, Close() 함수가 반환하는 에러 객체를 처리하지 않았다는 이유로 GoLand linter가 warning 메세지를 출력한다. 이에, 명시적으로 에러 객체를 받아서, 에러가 발생할 경우 Go 언어의 기본 log 패키지를 이용하여 콘솔에 에러 메세지를 출력하도록 했다.

profile
Fullstack software engineer specialized for Blockchain

0개의 댓글