
재직중인 회사에서 사용중인 AsyncElasticsearch 에서 Unclosed Client Session 경고 문구가 발견되었다.

aiohttp는 비동기 HTTP 클라이언트/서버 프레임워크이며, 이 경고는 aiohttp.ClientSession 객체나 연결(connectors)이 적절히 닫히지 않았을 때 나타나는 경고였다.
운영 서버에서도 같은 경고가 발생하고 있었다.
해당 자원 관리를 통해 누수가 발생하지 않도록 막기 위해, 사용중인 FastAPI 프레임워크의 공식 문서와 ES의 공식문서를 읽어보며 AsyncElasticsearch Client 관리 모듈을 생성하게 되었다.
Fast API는 @app.on_event("startup"), @app.on_event("shutdown") 이벤트를 제공하는데, 이는 각각 앱이 실행될 때, 종료될 때 실행되는 함수를 지정할 수 있다.
ES의 공식 문서에서는 AsyncElasticsearch를 사용할 때 위 이벤트 레이어에서 client의 생성 및 해제를 권장하고있다.
이에 따라 AsyncElasticsearch 클라이언트를 싱글톤 패턴으로 생성하고, 해제하는 모듈을 생성하였고, 위 이벤트에 등록해주었다.
###################### 모듈 구현부 ########################
from typing import Optional
from elasticsearch import AsyncElasticsearch
class AsyncElasticSearchClientManager:
_instance: Optional[AsyncElasticsearch] = None
@classmethod
def get_client(
cls,
es_url: Optional[str] = None,
user_id: Optional[str] = None,
password: Optional[str] = None
) -> AsyncElasticsearch:
if cls._instance is None:
cls._instance = AsyncElasticsearch(
es_url,
http_auth=(user_id, password),
request_timeout=1,
max_retries=5,
retry_on_timeout=True,
# verify_certs=False,
)
return cls._instance
@classmethod
async def close_client(cls):
if cls._instance is not None:
await cls._instance.close()
cls._instance = None
###################### FastAPI 메인 구현부 ########################
@app.on_event("startup")
async def startup_event():
AsyncElasticSearchClientManager.get_client(
es_url=f"{config.ES_URL}:{config.ES_PORT}",
user_id=config.ES_LOGIN_ID,
password=config.ES_LOGIN_PWD,
)
@app.on_event("shutdown")
async def shutdown_event():
await AsyncElasticSearchClientManager.close_client()
위와 같은 방식으로 AsyncElasticsearch를 DAO 레이어에서 직접 생성하여 사용하던 기존의 코드를 걷어내고,
위 싱글톤 패턴으로 생성한 인스턴스를 재사용하도록 모두 적용했고, 발생하던 경고가 더이상 나타나지 않음을 확인했다.
즉, 자원의 누수는 더이상 발생하지 않았고, 서비스 주요 기능들의 laytency를 감소시킬 수 있었다.
