如何在发送响应之前等待 5 秒(不阻塞)?
How to wait 5 seconds (without blocking) before sending Response?
这似乎是可能的,因为在 app.Sanic.handle_request()
中有这个 snippet:
if isawaitable(response):
response = await response
这就是 Python 检查 awaitable
的方式:
def isawaitable(object):
"""Return true if object can be passed to an ``await`` expression."""
return (isinstance(object, types.CoroutineType) or
isinstance(object, types.GeneratorType) and
bool(object.gi_code.co_flags & CO_ITERABLE_COROUTINE) or
isinstance(object, collections.abc.Awaitable))
我知道使用 async def
创建一个可等待的函数,但我不知道如何创建一个可等待的 HTTPResponse
实例。如果可能的话,看到一个简单的 await asyncio.sleep(5)
的等待响应示例真的很有帮助。
尝试了 Mikhail 的解决方案,这是我观察到的:
raise500
进入asyncio.sleep()
ret500
不进入asyncio.sleep()
(bug)
raise500
阻止其他 raise500
(错误)
raise500
不阻塞ret500
- 无法判断
ret500
是否会阻塞其他 ret500
因为它太快了(没有休眠)
完整代码(运行 保存为 test.py
,然后在 shell python test.py
中并转到 http://127.0.0.1:8000/api/test
):
import asyncio
from sanic import Sanic
from sanic.response import HTTPResponse
from sanic.handlers import ErrorHandler
class AsyncHTTPResponse(HTTPResponse): # make it awaitable
def __await__(self):
return self._coro().__await__() # see
async def _coro(self):
print('Sleeping')
await asyncio.sleep(5)
print('Slept 5 seconds')
return self
class CustomErrorHandler(ErrorHandler):
def response(self, request, exception):
return AsyncHTTPResponse(status=500)
app = Sanic(__name__, error_handler=CustomErrorHandler())
@app.get("/api/test")
async def test(request):
return HTTPResponse(status=204)
@app.get("/api/raise500")
async def raise500(request):
raise Exception
@app.get("/api/ret500")
async def ret500(request):
return AsyncHTTPResponse(status=500)
if __name__ == "__main__":
app.run()
Class 实现 __await__
魔术方法变得可等待。
我没有检查它是否适用于您的情况,但这里是创建可等待自定义 class 实例的示例:
import asyncio
from inspect import isawaitable
class HTTPResponse: # class we have
pass
class AsyncHTTPResponse(HTTPResponse): # make it awaitable
def __await__(self):
return self._coro().__await__() # see
async def _coro(self):
await asyncio.sleep(2)
return self
async def main():
resp = AsyncHTTPResponse()
if isinstance(resp, HTTPResponse):
print('It is HTTPResponse class ...')
if isawaitable(resp):
print('... which is also awaitable.')
print('Let us see how it works.')
await resp
loop = asyncio.get_event_loop()
try:
loop.run_until_complete(main())
finally:
loop.run_until_complete(loop.shutdown_asyncgens())
loop.close()
由于 Mikhail 的回答是正确的,我将只讨论进一步的编辑
- raise500 blocks other raise500 (bug)
好像不堵。一个简单的测试(添加一些查询字符串来区分请求):
for i in `seq 2`;do curl http://127.0.0.1:8000/api/raise500&req=$i & done
从日志的日期时间来看,请求之间没有延迟(阻塞)
Sleeping
Sleeping
Slept 5 seconds
2017-11-26 01:01:49 - (network)[INFO][127.0.0.1:37310]: GET http://127.0.0.1:8000/api/raise500?req=1 500 0
Slept 5 seconds
2017-11-26 01:01:49 - (network)[INFO][127.0.0.1:37308]: GET http://127.0.0.1:8000/api/raise500?req=2 500 0
- ret500 does not enter the asyncio.sleep() (bug)
那是因为你return在awaitable函数中awaitable,而sanic await只等待第一个:
@app.get("/api/ret500")
async def ret500(request):
return AsyncHTTPResponse(status=500)
handle_request
可以:
response = ret500(request) # call `async def ret500` and returns awaitable
if isawaitable(response):
response = await response # resolve and returns another awaitable - AsyncHTTPResponse object
# note to wait 5 seconds sanic would need again await for it
# response = await response
解决方案:
不要return等待,换句话说await AsyncHTTPResponse
自己
@app.get("/api/ret500")
async def ret500(request):
res = await AsyncHTTPResponse(status=500)
return res
放弃 ret500 的 async
@app.get("/api/ret500")
def ret500(request):
return AsyncHTTPResponse(status=500)
注意:此技术仅在不打算在其中调用异步函数时才有效。
这似乎是可能的,因为在 app.Sanic.handle_request()
中有这个 snippet:
if isawaitable(response):
response = await response
这就是 Python 检查 awaitable
的方式:
def isawaitable(object):
"""Return true if object can be passed to an ``await`` expression."""
return (isinstance(object, types.CoroutineType) or
isinstance(object, types.GeneratorType) and
bool(object.gi_code.co_flags & CO_ITERABLE_COROUTINE) or
isinstance(object, collections.abc.Awaitable))
我知道使用 async def
创建一个可等待的函数,但我不知道如何创建一个可等待的 HTTPResponse
实例。如果可能的话,看到一个简单的 await asyncio.sleep(5)
的等待响应示例真的很有帮助。
尝试了 Mikhail 的解决方案,这是我观察到的:
raise500
进入asyncio.sleep()
ret500
不进入asyncio.sleep()
(bug)raise500
阻止其他raise500
(错误)raise500
不阻塞ret500
- 无法判断
ret500
是否会阻塞其他ret500
因为它太快了(没有休眠)
完整代码(运行 保存为 test.py
,然后在 shell python test.py
中并转到 http://127.0.0.1:8000/api/test
):
import asyncio
from sanic import Sanic
from sanic.response import HTTPResponse
from sanic.handlers import ErrorHandler
class AsyncHTTPResponse(HTTPResponse): # make it awaitable
def __await__(self):
return self._coro().__await__() # see
async def _coro(self):
print('Sleeping')
await asyncio.sleep(5)
print('Slept 5 seconds')
return self
class CustomErrorHandler(ErrorHandler):
def response(self, request, exception):
return AsyncHTTPResponse(status=500)
app = Sanic(__name__, error_handler=CustomErrorHandler())
@app.get("/api/test")
async def test(request):
return HTTPResponse(status=204)
@app.get("/api/raise500")
async def raise500(request):
raise Exception
@app.get("/api/ret500")
async def ret500(request):
return AsyncHTTPResponse(status=500)
if __name__ == "__main__":
app.run()
Class 实现 __await__
魔术方法变得可等待。
我没有检查它是否适用于您的情况,但这里是创建可等待自定义 class 实例的示例:
import asyncio
from inspect import isawaitable
class HTTPResponse: # class we have
pass
class AsyncHTTPResponse(HTTPResponse): # make it awaitable
def __await__(self):
return self._coro().__await__() # see
async def _coro(self):
await asyncio.sleep(2)
return self
async def main():
resp = AsyncHTTPResponse()
if isinstance(resp, HTTPResponse):
print('It is HTTPResponse class ...')
if isawaitable(resp):
print('... which is also awaitable.')
print('Let us see how it works.')
await resp
loop = asyncio.get_event_loop()
try:
loop.run_until_complete(main())
finally:
loop.run_until_complete(loop.shutdown_asyncgens())
loop.close()
由于 Mikhail 的回答是正确的,我将只讨论进一步的编辑
- raise500 blocks other raise500 (bug)
好像不堵。一个简单的测试(添加一些查询字符串来区分请求):
for i in `seq 2`;do curl http://127.0.0.1:8000/api/raise500&req=$i & done
从日志的日期时间来看,请求之间没有延迟(阻塞)
Sleeping
Sleeping
Slept 5 seconds
2017-11-26 01:01:49 - (network)[INFO][127.0.0.1:37310]: GET http://127.0.0.1:8000/api/raise500?req=1 500 0
Slept 5 seconds
2017-11-26 01:01:49 - (network)[INFO][127.0.0.1:37308]: GET http://127.0.0.1:8000/api/raise500?req=2 500 0
- ret500 does not enter the asyncio.sleep() (bug)
那是因为你return在awaitable函数中awaitable,而sanic await只等待第一个:
@app.get("/api/ret500")
async def ret500(request):
return AsyncHTTPResponse(status=500)
handle_request
可以:
response = ret500(request) # call `async def ret500` and returns awaitable
if isawaitable(response):
response = await response # resolve and returns another awaitable - AsyncHTTPResponse object
# note to wait 5 seconds sanic would need again await for it
# response = await response
解决方案:
不要return等待,换句话说
await AsyncHTTPResponse
自己@app.get("/api/ret500") async def ret500(request): res = await AsyncHTTPResponse(status=500) return res
放弃 ret500 的
async
@app.get("/api/ret500") def ret500(request): return AsyncHTTPResponse(status=500)
注意:此技术仅在不打算在其中调用异步函数时才有效。