2017-05-21 6 views
1

내가 좋아하는 핸들러 aiohttp 웹 서버 응용 프로그램이 있습니다 app["db"]는 (지금 문제, aiopg, aioredis하지 않습니다) 풀링 된 자원의 일종입니다CancelledError는 일반적인 aiohttp 웹 서버에서 어떻게 처리되어야합니까?

async def handler(request): 
    async with request.app["db"].acquire() as db: 
     row = await query(db) 

    return aiohttp.web.json_response(row) 

. 이것은 오늘날까지 훌륭했습니다. 아무 이유없이 모든 클라이언트가

처럼 흔적으로 가득 시간 제한 및 응용 프로그램 로그에 의해 분리 시작
[2017-05-21 17:58:24,254] ERROR [aiohttp.server] Error handling request 
Traceback (most recent call last): 
    File "/virtualenv/lib/python3.6/site-packages/aiohttp/web_server.py", line 61, in handle_request 
    resp = yield from self._handler(request) 
    File "/virtualenv/lib/python3.6/site-packages/aiohttp/web.py", line 249, in _handle 
    resp = yield from handler(request) 
    File "/visio-longer/visio_longer/views/communicate/__init__.py", line 81, in legacy_communicate 
    async with request.app["db"].acquire() as db: 
    File "/virtualenv/src/aiopg/aiopg/utils.py", line 140, in __aenter__ 
    self._conn = yield from self._coro 
    File "/virtualenv/src/aiopg/aiopg/sa/engine.py", line 162, in _acquire 
    raw = yield from self._pool.acquire() 
    File "/virtualenv/src/aiopg/aiopg/utils.py", line 67, in __iter__ 
    resp = yield from self._coro 
    File "/virtualenv/src/aiopg/aiopg/pool.py", line 168, in _acquire 
    with (yield from self._cond): 
    File "/usr/lib/python3.6/asyncio/locks.py", line 67, in __iter__ 
    yield from self.acquire() 
    File "/usr/lib/python3.6/asyncio/locks.py", line 176, in acquire 
    yield from fut 
concurrent.futures._base.CancelledError 
풀에서 데이터베이스 연결을 획득하면서 CancelledError (타임 아웃에 의해 클라이언트 연결 해제)를 수신하고 여기에

요점 :

File "/visio-longer/visio_longer/views/communicate/__init__.py", line 81, in legacy_communicate 
    async with request.app["db"].acquire() as db: 

나는 5 초마다 풀 상태 (sizefreesize)를 인쇄하는 코 루틴을 실행했으며 현재 풀에 충분한 연결이있었습니다!

시간의 조사 결과 풀의 컨텍스트 관리자 __atexit__을 수행하는 동안 CancelledError을 수신하면 풀에 연결을 반환하는 프로세스가 풀 오작동을 초래한다는 결론을 얻었습니다. 나는 asyncpg에있는 그 행동을 고치고있는 commit을 찾았고, 에 awkward-looking fix을 만들었습니다. 이것의 아무도 도왔다 - 나는 아직도 aioredisaiopg 둘 다에서 동일한 과실을 얻고 있었다. (인수, 리턴을 포함한

async def handler(request): 
    async def process(): 
     async with request.app["db"].acquire() as db: 
      row = await query(db) 

    return aiohttp.web.json_response(asyncio.shield(process(row))) 
그래서

이 중단 요청이 여전히 끝에 처리 된 :

상황

asyncio.shield으로 연결 풀을 사용하는 코드의 모든 조각을 포장하여
async def handler(request): 
    async with request.app["db"].acquire() as db: 
     row = await query(db) 

    return aiohttp.web.json_response(row) 

를 대체하여 해결 리소스를 풀에 추가).

이 방법이 필요합니까? 이제 내 코드가 무서워 보이고 다음에 풀을 asyncio.shield으로 감싸는 것을 잊지 않을 것이라는 보장은 없습니다. 이 문제를 해결하는 적절한 방법은 무엇입니까 (분명히 라이브러리는 스스로 해결할 수 없습니다)?

+0

https://vorpus.org/blog/control -c-handling-in-python-and-trio/interesting. –

답변

0

다른 예외와 다른 점은 전혀 없습니다. CanceledError은 또 다른 오류입니다. 모든 컨텍스트 관리자 종료 함수가 실행되므로 자원을 해제 할 수있는 기회가 있어야합니다. 연결 풀에서 잠금과 관련된 미래가 어떻게 든 사라지는 중입니다. 문제가있는 것 같아 취소 된 클라이언트 연결과 무관해야합니다. 진행중인 요청에 해당하는 coroutine이 있다고 가정하고 연결이 끊어지면 해당 coroutine을 취소해야합니다. Task.cancel을 호출하면 코 루틴 내부에 CanceledError이 생깁니다. 궁극적으로 CanceledError이 coroutine에서 발생한다고 가정하면 Task의 결과는 CanceledError이됩니다. 그러나 잠금 관련 미래가 취소되는 방식을 이해하지 못합니다.

컨텍스트 관리자 나 finally 블록 또는 다른 정리 코드의 종료 기능에서 기다리는 중일 때는 더 많은주의를 기울여야 할 수도 있습니다.예를 들어, 일부 리소스를 반환하기 위해 exit 함수에서 잠금이 필요한 경우 CanceledError을 포착하고 (결국에는 다시 올리거나) async.shield를 사용하는 while 루프가 필요할 수 있습니다. 나는 exit 함수/cleanup 핸들러의 특정 코드를 중심으로 전체 요청을 처리하기보다는 그렇게 할 것이다. 또한 콜백을 사용하여 리소스 (loop.call_soon 경유)를 반환하는 것이 사이드 클린업 코드에서 기다리는 것보다 더 좋은 선택인지를 고려하십시오. 클린업 코드를 기다리지 않으면 새 CanceledError을 제기 할 수 없습니다. Context manager exit 함수와 finally 블록은 기다릴 필요가 없더라도 다른 곳의 CanceledError에 의해 트리거 될 수 있지만 다른 예외와 다른 점이 없습니다.

+0

나는 CancelledError가 컨텍스트 매니저 종료 함수 안에서 발생하는 상황을 설명하고 있었다. 그러면 정상적으로 완료되지 않고 풀이 일관성있는 상태가됩니다. – themylogin

+0

db connection exit func에서는 어떻게 이런 일이 발생합니까? 나는 풀에 연결을 되 돌리는 것이 무엇이든 기다리지 않을 것이라고 생각합니다. –

+0

라이브러리의 모든 리턴 - 투 - 풀 (return-to-pool) 함수들 빈 풀에서 리소스가 사용되기를 기다리고있는 코 루틴에 알리는 조건 변수를 기다리고있다. 이 코드의 보호는 도움이되지 않았다. 어쩌면 당신은 https://github.com/themylogin/aiopg/commit/cf2580671eabd880e8826d42ac04f5a3380447a7을보고 무엇이 누락 된 생각이 있습니까?) – themylogin