4 ms·
The one problem I have with Python and would like to solve, is to be able, from within a request rendering function/method of my web application (think Flask) t
by Loic 10y ago
The one problem I have with Python and would like to solve, is to be able, from within a request rendering function/method of my web application (think Flask) to run something like:
handle1 = call_webservice_1(args)
handle2 = call_webservice_2(args)
handle3 = call_webservice_3(args)
(realres1, realres2, realres3) = wait_until_timeout(500, handle1, handle2, handle3)
# here, I have my results in realresX or None if timeout
# the call_webservice_X would be non blocking
This way I can dispatch my requests to my backends and degrade gracefully if one request fails within the time. I was doing that in PHP using zeromq to send the requests and listening to the answers with a unique id on each request, but now I would prefer to stay with an HTTP based protocol to communicate with my backends.
- darkf 10y agoYou could probably do that fine with coroutines and/or asyncio.
- ojii 10y agoQuick two minute implementation: import multiprocessing import time NULL = object() def call_webservice(fail=False): if fail: time.sleep(10) return False else: return True def wait_until_timeout(timeout, *async_results): results = [NULL] * len(async_results) end = time.time() + timeout while time.time() < end: for index, result in enumerate(async_results): if results[index] is NULL and result.ready(): results[index] = result.get() if not results.count(NULL): break return tuple(None if result is NULL else result for result in results) def handler(): with multiprocessing.Pool() as pool: handle1 = pool.apply_async(call_webservice, (True,)) handle2 = pool.apply_async(call_webservice, (False,)) start = time.time() realres1, realres2 = wait_until_timeout(0.5, handle1, handle2) duration = time.time() - start print(f'Took {duration:.3f} seconds') print(realres1, realres2) if __name__ == '__main__': handler()
- orf 10y agofrom asyncio import wait, gather, get_event_loop from aiohttp import web async my_handler(request): handle1 = call_webservice_1(args) handle2 = call_webservice_2(args) handle3 = call_webservice_3(args) await wait(gather(handle1, handle2, handle3), 500) return web.Response({'finished': True}) app = web.Application() app.router.add_route('GET', '/test/', my_handler) loop = asyncio.get_event_loop() server = loop.create_server(app.make_handler()) loop.run_until_complete(server) Done. Not flask, but if you need to make lots of parallel network calls during a web request why the hell are you using flask?
- Loic 10y agoI am using flask because you know, long string of development with flask over the years, your application is growing and then you hit some walls. I do not want to rewrite everything just for a couple of views within my application. Flask with gunicorn has its own even loop and asyncio io another one and at the end it is hard to be sure if something is working because I am lucky or because it is the way to do things. Anyway, thank you for your example, it is really clear and easy to understand! Maybe we need a flask like framework which is asyncio based. Which mean I will need to upgrade the code to Python 3 :)
- orf 10y agohttp://flask-aiohttp.readthedocs.io/en/latest/ http://flask-aiohttp.readthedocs.io/en/latest/ ;)
- loganekz 10y agoIf you haven't already, check out sanic [1] which is powered by uvloop [2] which itself is pretty amazing. [1] https://github.com/channelcat/sanic https://github.com/channelcat/sanic [2] https://github.com/MagicStack/uvloop https://github.com/MagicStack/uvloop
- spookylukey 10y agoThat's really helpful. Can you make an example which doesn't require making the my_handler function 'async'? For cases when you don't want to make an entire async stack, you just want to slot some async code into your existing code. If the answer is "to get some async goodness, just use this easy code, plus rewrite your entire project to use a different framework and set of libraries", then we are only fooling ourselves.