2017-02-10 14 views
0

요청을 다른 마이크로 서비스로 리디렉션하는 게이트웨이 역할을하는 서비스를 개발했습니다. 이렇게하려면 리디렉션 요청을 처리하기 위해 aiohttp를 사용했으며, 호스트 역할을하는 gunicorn (w/aiohttp.worker.GunicornWebWorker)과 Heoku를 사용했습니다.heroku + gunicorn + aiohttp를 사용한 임의 시간 초과 오류

로컬에서 모든 작업이 완벽하게 작동합니다. 100 % 요청은 응답을 반환하고 클라이언트는 항상 원하는 정보를받습니다. 그러나 Heroku에 배포하고 일부 요청 (분당 5k)을 리디렉션 할 때 HTTP 상태 503 시간 초과 오류입니다. 좋은 해결 요청의 비율이 매우 높기 때문에 걱정하지 않아도되지만 (99.9994) 무슨 일이 일어나고 있는지 알고 싶습니다. 다만 제한 시간 전에 예외 모금은 다음과 같이이다 :

gunicorn --pythonpath app app.app:aio_app --worker-class aiohttp.worker.GunicornWebWorker --workers 3 

:

at=error code=H12 desc="Request timeout" method=GET path="https://stackoverflow.com/users/21324/posts/" host=superapp.herokuapp.com request_id=209bd839-baac-4e72-a04e-657d85348f45 fwd="84.78.56.97" dyno=web.2 connect=0ms service=30000ms status=503 bytes=0 

내가 함께 응용 프로그램을 실행 해요 :

[2017-02-10 17:03:48 +0000] [683] [INFO] Worker exiting (pid: 683) 
ERROR:asyncio:Task was destroyed but it is pending! 
[2017-02-10 17:03:48 +0000] [683] [INFO] Stopping server: 683, connections: 1 
Exception ignored in: <generator object GunicornWebWorker._run at 0x7f18b1d2f518> 
Traceback (most recent call last): 
    yield from self.close() 
    yield from self.wsgi.shutdown() 
File "/app/.heroku/python/lib/python3.5/site-packages/aiohttp/web.py", line 199, in shutdown 
    yield from self.on_shutdown.send(self) 
File "/app/.heroku/python/lib/python3.5/site-packages/aiohttp/signals.py", line 48, in send 
    yield from self._send(*args, **kwargs) 
File "/app/.heroku/python/lib/python3.5/site-packages/aiohttp/signals.py", line 16, in _send 
    yield from res 
File "/app/app/app.py", line 14, in close_redis 
    app.redis_pool.close() 
File "/app/.heroku/python/lib/python3.5/site-packages/aioredis/pool.py", line 135, in close 
    self._close_state.set() 
File "/app/.heroku/python/lib/python3.5/asyncio/locks.py", line 242, in set 
    fut.set_result(True) 
File "/app/.heroku/python/lib/python3.5/asyncio/futures.py", line 332, in set_result 
    self._schedule_callbacks() 
File "/app/.heroku/python/lib/python3.5/asyncio/futures.py", line 242, in _schedule_callbacks 
    self._loop.call_soon(callback, self) 
File "/app/.heroku/python/lib/python3.5/asyncio/base_events.py", line 497, in call_soon 
    handle = self._call_soon(callback, args) 
File "/app/.heroku/python/lib/python3.5/asyncio/base_events.py", line 506, in _call_soon 
    self._check_closed() 
File "/app/.heroku/python/lib/python3.5/asyncio/base_events.py", line 334, in _check_closed 
    raise RuntimeError('Event loop is closed') 
RuntimeError: Event loop is closed 
ERROR:asyncio:Task was destroyed but it is pending! 
task: <Task pending coro=<ServerHttpProtocol.start() running at /app/.heroku/python/lib/python3.5/site-packages/aiohttp/server.py:261>> 
[2017-02-10 17:03:48 +0000] [4] [CRITICAL] WORKER TIMEOUT (pid:683) 

그런 다음 Heroku가가/라우터가이 같은 오류가 표시됩니다 주요 코드 :

def init(asyncio_loop): 
    app = web.Application(loop=asyncio_loop, middlewares=[middlewares.auth_middleware, 
                  middlewares.logging_middleware]) 

    # INIT DBs 
    app.redis_pool = asyncio_loop.run_until_complete(
     create_pool((settings.REDIS['host'], settings.REDIS['port']), 
        password=settings.REDIS['password'])) 

    # Clean connections on stop 
    app.on_shutdown.append(close_redis) 

    # Add rollbar 
    rollbar.init(settings.ROLLBAR_TOKEN, 'production') # access_token, environment 

    # Bind routes 
    for r in routes: 
     app.router.add_route(r[0], r[1], r[2]) 

    return app 


# Create app 
loop = asyncio.get_event_loop() 
aio_app = init(loop) 

그리고 재 예 :

async with aiohttp.ClientSession() as s: 
    try: 
     async with s.request(method=method, 
          url=new_url, 
          headers=new_headers, 
          data=body, 
          allow_redirects=False, 
          timeout=25) as response: 
      # Clean response 
      resp_headers = MSRepository.filter_response_headers(response.headers) 
      resp_body = (await response.read()) 

      return ResponseDataEntity(resp_headers, response.status, resp_body) 
    except asyncio.TimeoutError: 
     raise MSConnectionError("Request timeout") 
    except Exception as e: 
     rollbar.report_message(str(e), extra_data={ 
      "url": new_url, 
      "data": body, 
      "method": method, 
      "headers": new_headers 
     }) 
     raise MSConnectionError(str(e)) 

는 요청을하고 예외가 30 시간 초과로 상승 할 때 25 초 시간 제한이 볼 수 있듯이.

누구나 무슨 일이 일어나는지 알 수 있습니까?

(참고 : I 리디렉션 쓸 때 나는, 인증을 확인, HTTP 302 내가 요청, 편집 헤더를 처리하는 의미 말하는 appropiate MS에 비동기 요청을 응답을 처리하고이 응답을 반환하는 의미하지 않는다)

답변

0

결국 문제는 핸들러 중 하나에 발생했습니다. 타임 아웃이 모든 엔드 포인트에서 완전히 무작위 였기 때문에 실제로 어떤 일이 발생했는지는 알 수 없지만 분당 10k 요청 이상으로 6 시간이 지나면 완벽하게 문제를 해결할 수 있습니다. 당신이 유일한 차이점은 하나의 경우에 우리는 항상 몸을 기다리는 다른 경우에는하지 않는 것입니다 볼 수 있듯이

async def bad_handler(request): 
    # Read body in ALL cases to not to block requests 
    if '/event-log/action/' == request.path: 
     if request.query_string != '': 
      action_type = request.query_string 
     else: 
      try: 
       request_body = await request.json() 
       action_type = request_body.get('type', '') 
      except: 
       action_type = '' 

     print("Action_type={}".format(action_type)) 

    # Response to client 
    return aiohttp.web.json_response(data={}, status=200) 

async def good_handler(request): 
    # Read body in ALL cases to not to block requests 
    try: 
     request_body = await request.json() 
    except: 
     request_body = None 

    if '/event-log/action/' == request.path: 
     if request.query_string != '': 
      action_type = request.query_string 
     else: 
      if request_body is not None: 
       action_type = request_body.get('type', '') 
      else: 
       action_type = '' 

     print("Action_type={}".format(action_type)) 

    # Response to client 
    return aiohttp.web.json_response(data={}, status=200) 

: 여기 전과 수정 후 코드를입니다.

질문을 공개하고 누군가를 뛰쳐 나오면 지금 왜 작동하는지 나와 있습니다. :)