Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Gevent be or not to be

449 views

Published on

From celery with gevent to asyncio

Published in: Software
  • Be the first to comment

Gevent be or not to be

  1. 1. Gevent: be or not to be? Александр Мокров
  2. 2. About ● asynchrony ● gevent ● asyncio ● пример построения асинхронного приложения
  3. 3. Web APP Task queue Task queue Task queue WorkerWorkerWorker DB Push tasks into queue Workers pull task from queue Report progress Pull DP for report status
  4. 4. Tasks workflow get flour bake pie get meat seal pie create dough order pie get milk get aggs
  5. 5. Applications as services Service 1 Worker Worker Service 3 Worker Worker Service 2 Worker Service 2 Worker Worker Worker Worker
  6. 6. Long polling client request response request response request response request response server event event event event
  7. 7. Many same services entry point service n app service 2 service 1 ...
  8. 8. entry task service task 1 DB callback task 1 service task n callback task n Callbacks
  9. 9. entry task service task 1 DB callback task 1 service task n callback task n Callbacks
  10. 10. entry task service task 1 DB callback task 1 service task n callback task n Callbacks
  11. 11. What we want entry task service task 1 service task 2 service task n
  12. 12. What we want entry task service task 1 service task 2 service task n
  13. 13. long task services time Timeline
  14. 14. long task services time Timeline
  15. 15. long task services time Timeline
  16. 16. long task services time Timeline timeout
  17. 17. Gevent gevent is a concurrency library based around libev. It provides a clean API for a variety of concurrency and network related tasks.
  18. 18. Greenlet The primary pattern used in gevent is the Greenlet, a lightweight coroutine provided to Python as a C extension module. Greenlets all run inside of the OS process for the main program but are scheduled cooperatively. Only one greenlet is ever running at any given time. Spin-off of Stackless, a version of CPython that supports micro-threads called “tasklets”. Tasklets run pseudo-concurrently (typically in a single or a few OS-level threads) and are synchronized with data exchanges on “channels”. Its coroutine
  19. 19. The way to gevent ● Stackless ● Greenlet ● Eventlet ● Gevent
  20. 20. Event loop
  21. 21. def foo(): print('Running in foo') gevent.sleep(0) print('Explicit context switch to foo') def bar(): print('Explicit context to bar') gevent.sleep() print('Implicit context switch to bar') gevent.joinall([ gevent.spawn(foo), gevent.spawn(bar), ]) Synchronous & Asynchronous Execution. Context Switching Running in foo Explicit context to bar Explicit context switch to foo Implicit context switch to bar
  22. 22. def task(pid): gevent.sleep(random.randint(0,2)*0.001) print('Task %s done' % pid) def synchronous(): for i in range(1, 8): task(i) Synchronous & Asynchronous Execution Task 1 done Task 2 done Task 3 done Task 4 done Task 5 done Task 6 done Task 7 done
  23. 23. def task(pid): gevent.sleep(random.randint(0,2)*0.001) print('Task %s done' % pid) def asynchronous(): threads = [gevent.spawn(task, i) for i in range(1, 8)] gevent.joinall(threads) Task 1 done Task 5 done Task 6 done Task 2 done Task 4 done Task 7 done Task 3 done Synchronous & Asynchronous Execution
  24. 24. Blocking calls Blocking operation Non-blocking asynchronous call Callbacks?
  25. 25. def callback_hell(param): def _cb1(result): def _cb2(result): def _cb3(result): def _cb4(result): def _cb5(result): def _cb6(result): def _cb7(result): def _cb8(result): return func9(param).add_callback(_cb2) return func8(param).add_callback(_cb2) return func7(param).add_callback(_cb2) return func6(param).add_callback(_cb2) return func5(param).add_callback(_cb2) return func4(param).add_callback(_cb2) return func3(param).add_callback(_cb2) return func2(param).add_callback(_cb2) return func1(param).add_callback(_cb1) Callback hell
  26. 26. def echo(i): time.sleep(0.001) return i # Non Deterministic Process Pool from multiprocessing.pool import Pool p = Pool(10) run1 = [a for a in p.imap_unordered(echo, range(10))] run2 = [a for a in p.imap_unordered(echo, range(10))] run3 = [a for a in p.imap_unordered(echo, range(10))] run4 = [a for a in p.imap_unordered(echo, range(10))] print(run1 == run2 == run3 == run4) False Determinism
  27. 27. # Deterministic Gevent Pool from gevent.pool import Pool p = Pool(10) run1 = [a for a in p.imap_unordered(echo, range(10))] run2 = [a for a in p.imap_unordered(echo, range(10))] run3 = [a for a in p.imap_unordered(echo, range(10))] run4 = [a for a in p.imap_unordered(echo, range(10))] print(run1 == run2 == run3 == run4) True Determinism
  28. 28. Spawning Greenlets from gevent import Greenlet thread1 = Greenlet.spawn(foo, "message", 1) thread2 = gevent.spawn(foo, "message", 2) thread3 = gevent.spawn(lambda x: (x+1), 2) threads = [thread1, thread2, thread3] # Block until all threads complete. gevent.joinall(threads)
  29. 29. class MyGreenlet(Greenlet): def __init__(self, message, n): Greenlet.__init__(self) self.message = message self.n = n def _run(self): print(self.message) gevent.sleep(self.n) g = MyGreenlet("Hi there!", 3) g.start() g.join() Spawning Greenlets
  30. 30. Greenlet State started -- Boolean, indicates whether the Greenlet has been started ready() -- Boolean, indicates whether the Greenlet has halted successful() -- Boolean, indicates whether the Greenlet has halted and not thrown an exception value -- arbitrary, the value returned by the Greenlet exception -- exception, uncaught exception instance thrown inside the greenlet
  31. 31. greenlet greenlet long task service response listener subscribe(task_id) Greenlets subscribing
  32. 32. from gevent import Timeout timeout = Timeout(seconds=10) timeout.start() def wait(): gevent.sleep(10) print('Completed') try: gevent.spawn(wait).join() except Timeout: print('Could not complete') Timeouts
  33. 33. time_to_wait = 5 # seconds class TooLong(Exception): pass with Timeout(time_to_wait, TooLong): gevent.sleep(10) Timeouts
  34. 34. class Queue(maxsize=None, items=None) empty() full() get(block=True, timeout=None) get_nowait() next() peek(block=True, timeout=None) peek_nowait() put(item, block=True, timeout=None) put_nowait(item) qsize() Queues
  35. 35. greenlet greenlet greenlet service results dispatcher gevent.queues task_id reply_to, results_queue Getting results
  36. 36. greenlet greenlet greenlet service results dispatcher service service service reply_to services queues subscribe gevent.queues Asynchronous RPC
  37. 37. Events Groups and Pools Locks and Semaphores Subprocess Thread Locals Actors
  38. 38. Monkey patching guerrilla patch gorilla patch monkey patch
  39. 39. import socket print(socket.socket) from gevent import monkey monkey.patch_socket() print("After monkey patch") print(socket.socket) import select print(select.select) monkey.patch_select() print("After monkey patch") print(select.select) <class 'socket.socket'> After monkey patch <class 'gevent._socket3.socket'> <built-in function select> After monkey patch <function select at 0x7ff7e111c378> Monkey patching
  40. 40. Stack layout for a greenlet | ^^^ | | older data | | | stack_stop . |_______________| . | | . | greenlet data | . | in stack | . * |_______________| . . _____________ stack_copy + stack_saved . | | | | . | data | |greenlet data| . | unrelated | | saved | . | to | | in heap | stack_start . | this | . . |_____________| stack_copy | greenlet | | | | newer data | | vvv |
  41. 41. What instead?
  42. 42. PEP 255 -- Simple Generators Authors: Neil Schemenauer <nas at arctrix.com>, Tim Peters <tim.peters at gmail.com>, Magnus Lie Hetland <magnus at hetland.org> Created: 18-May-2001 Python 2.2
  43. 43. PEP 342 Coroutine via Enhanced Generators Authors: Guido van Rossum, Phillip J. Eby Created: 10-May-2005 Python 2.5 PEP 288, Generators Attributes and Exceptions (Raymond Hettinger) PEP 325, Resource-Release Support for Generators (Samuele Pedroni)
  44. 44. What’s new? 1. (yield) statement: value = (yield) 2. send() 3. throw() 4. close() 5. Ensure that close() is called when a generator iterator is garbage-collected. 6. Allow yield to be used in try/finally blocks
  45. 45. Cooperative Coroutine Coroutine Coroutine Coroutine Coroutine
  46. 46. Produce, Filter, and Consume producer consumerfilterfilter ... send sendsend (yield) (yield)(yield)
  47. 47. RESULT = yield from EXPR PEP 380 Syntax for Delegating to aSubgenerator Authors: Gregory Ewing Created: 13-Feb-2009 Python 3.3 - September 29, 2012
  48. 48. PEP 0492 -- Coroutines with async and await syntax Authors: Yury Selivanov Created: 09-Apr-2015 Python 3.5
  49. 49. asynciotulip
  50. 50. Event loop async def coroutine(): print('Running in coro') event_loop = asyncio.get_event_loop() try: print('init coroutine') coro = coroutine() print('waiting for ...') event_loop.run_until_complete(coro) finally: print('closing event loop') event_loop.close() init coroutine waiting for ... Running in coro closing event loop
  51. 51. Coroutines, futures and tasks ● Coroutines ○ async def ○ @asyncio.coroutine ● Futures ○ promise of a result or an error ● Tasks ○ future which runs a coroutine
  52. 52. Chaining coroutines async def chain(): result1 = await step1() result2 = await step2(result1) result3 = await step3(result2) return result2, result3
  53. 53. async def task(pid): asyncio.sleep(random.randint(0, 2)*0.001) print(f'Task {pid} done') loop = asyncio.get_event_loop() futures = asyncio.gather( *[task(i) for i in range(7)]) loop.run_until_complete(futures) loop.close() Task 3 done Task 2 done Task 4 done Task 5 done Task 1 done Task 6 done Task 0 done Asynchronous Execution
  54. 54. Queues ● Queue ● PriorityQueue ● LifoQueue
  55. 55. Transport and protocols (callback based API) Streams (coroutine based API)
  56. 56. greenlet greenlet coro service results dispatcher service service service reply_to services queues subscribe asyncio.queues Asynchronous RPC
  57. 57. server dispatcher Application structure tasks tasksservice tasks client client
  58. 58. Client Client/ Server Server Client/ Server Client/ Server Server Server Constructor
  59. 59. Combining Coroutines with Threads and Processes executor = concurrent.futures.ProcessPoolExecutor( max_workers=3, ) event_loop = asyncio.get_event_loop() try: event_loop.run_until_complete( run_blocking_tasks(executor) ) finally: event_loop.close()
  60. 60. Summary ● В мире python много библиотек хороших и разных ● Некоторые библиотеки, когда-то популярные, уступают свое место новым, более продуманным ● Чтобы крепче спать, не ковыряйтесь в потрохах ● Не всё то, чем кажется
  61. 61. References http://www.gevent.org/ http://sdiehl.github.io/gevent-tutorial/ https://greenlet.readthedocs.io/en/latest/ https://docs.python.org/3/library/asyncio.html https://github.com/aio-libs
  62. 62. Thank you!
  63. 63. Questions? alexander.mokrov@gmail.com

×