[aiohttp] 세마포 제한 시간이 만료되었습니다

2019. 12. 4. 23:49Trouble Shooting

aiohttp The semaphore timeout has expired

 

Python3에서 aiohttp를 사용하던중 2가지 문제를 겪게되었는데,

제목과 같이 semaphore의 시간이 만료되었습니다 라는 에러가 출력되는 현상이며,

 

* 세마포어(1개의 자원에 다중으로 접근제어를 방지하기 위한 기술)

 

aiohttp를 사용할때 대부분은 다음과 같이 작성을 합니다.

import aiohttp, asynico

async def fetch(url):
	async with aiohttp.ClientSession() as session:
		async with session.get(url) as resp:
			r = await resp.read()
			r.decode('utf-8')
			# Your Code!
            

if __name__ == "__main__":
	loop = asynico.get_event_loop()
    urls = []
    url_append = urls.append
    url = "https://gmyankee.tistory.com/{}"
    for x in range(1, 254):
    	url_append(fetch(url.format(x)))
        
    loop.run_until_complete(asynico.wait(urls))

위 와 같이 작성하는게 잘못된 것은 아닙니다!

하지만 Windows 운영체제에서 동시 오픈을 많이 시도하다보면...

Python asyncio/aiohttp: ValueError: too many file descriptors in select() on Windows

위 와 같은 오류를 만나 볼 수 있습니다.

 

 

aiohttp는 기본적으로 최대 512개의 소켓을 오픈하고 사용하는데,

Windows는 동시 오픈 가능한 최대 소켓의 수는 64개로 제한되도록 default(기본값) 설정 되어 있어서

64개이상의 페이지가 동시 GET 및 접근을하게 된다면 오류가 나는데 이럴때는 limit를 제공해주면 됩니다.

 

 

참고자료: https://stackoverflow.com/questions/47675410/python-asyncio-aiohttp-valueerror-too-many-file-descriptors-in-select-on-win

 

위 내용을 참고하여 수정하면

import aiohttp, asynico

async def fetch(url):
	connector = aiohttp.TCPConnector(limit=60)  # <--- Connector를 호출하여 동시접속을 제한
	async with aiohttp.ClientSession(connector=connector) as session:  # <--- connector 적용
		async with session.get(url) as resp:
			r = await resp.read()
			r.decode('utf-8')
			# Your Code!
            

if __name__ == "__main__":
	loop = asynico.get_event_loop()
    urls = []
    url_append = urls.append
    url = "https://gmyankee.tistory.com/{}"
    for x in range(1, 254):
    	url_append(fetch(url.format(x)))
        
    loop.run_until_complete(asynico.wait(urls))

 

네 안됩니다. ㅋ__ㅋ

 

limit가 되긴하는데 결정적으로는 Windows에서는 이를 처리해주기 위해

IOCP 포트를 사용하라고 Python 공식문서에서도... 언급되어 있습니다.

 

블록체인에서 채굴 프로그램 또는 마이닝풀을 돌려보신분들은 한번쯤 보셨을 IOCP가

바로 이러한 비동기 쓰레드 프로그래밍에 등장합니다.

 

IOCP에 대해서는 https://developstudy.tistory.com/43

 

C++ IOCP 서버 2. IOCP 이론

* 아직 배우고 있는 학생이라 틀린 내용이 있을 수도 있습니다. 틀린 내용이 있다면 알려주시면 감사하겠습니다. IOCP는 Window 환경에서 작동하는 제일 흔히 쓰이는 논블로킹 프로세스이다. 최소한의 쓰레드로 최..

developstudy.tistory.com

 

저보다 더 자세히 설명을 해주시는 것 같아서 참조하시면 좋을 것 같네요

 

 

결론으로 들어가자면 ProactorEventLoop를 사용하면 Windows에서 IOCP를 사용하여

aiohttp limit를 제한한 Non-blocking Process 통신이 가능해진다는건데

 

코드를 다시 수정해보자면

import aiohttp, asynico

async def fetch(url):
	connector = aiohttp.TCPConnector(limit=60)
	async with aiohttp.ClientSession(connector=connector) as session:
		async with session.get(url) as resp:
			r = await resp.read()
			r.decode('utf-8')
			# Your Code!
            

if __name__ == "__main__":
	loop = asyncio.ProactorEventLoop()  # <--- asynico.get_event_loop() 에서 변경
	asyncio.set_event_loop(loop)  # <--- ProactorEventLoop를 사용한다고 명시
    urls = []
    url_append = urls.append
    url = "https://gmyankee.tistory.com/{}"
    for x in range(1, 254):
    	url_append(fetch(url.format(x)))
        
    loop.run_until_complete(asynico.wait(urls))

 

완성!

도움이 되셨다면 위 코드로 제 블로그를 갈궈주세요!

 

아 많은 분들이 DDOS라고 하시는데

DDOS는 다중 유저(IP)가 한순간에 동시다발 접속을 요청하는행위고

위 와같이 혼자서 동시다발 접속 공격을 하는행위는 DoS라고 지칭합니다..

 

저정도로 DoS 공격으로 판단할 것 같진 않지만...

Daum Kakao에게 법적 책임을 받으신다면 여러분의 몫입니다!

제 뇌피셜(생각)이지만 Daum Kakao에서는 이미 서버단에서 분산 로드밸런싱 처리나

서버 백로그 큐를 증진 또는, 동시 요청수 제한등을 설정하지 않았을까 끄적여봅니다.

 

조만간 공감봇을 만들어볼까..

 

 

구독과 공감은 필수!