Slide 1

Slide 1 text

Une petite page de pub

Slide 2

Slide 2 text

async def django(): Joachim Jablon - DjangoCong 2019 Marseille

Slide 3

Slide 3 text

Qui suis-je ? ! Joachim Jablon Dev @ PeopleDoc @ewjoachim / https://ewjoach.im

Slide 4

Slide 4 text

Qui a déjà..? "#$%& Entendu parler d'Andew Godwin ? Lu du code Python asynchrone ? Ecrit du code Python Asynchrone ? Lu la DEP 0009 ?

Slide 5

Slide 5 text

Andrew Godwin South Django Channels Core dev Django ...DEP 0009

Slide 6

Slide 6 text

Partie 1 Le pourquoi

Slide 7

Slide 7 text

Que fait votre serveur web ? Des I/Os (réseau / disque) Des cycles CPU (calculs) Python délègue
 à l'OS (appel système) Python parle au CPU directement Python sait
 le paralléliser dans des threads Python ne sait pas le paralléliser ( GIL ) >90%* <10%* *au doigt mouillé

Slide 8

Slide 8 text

Les threads & Python Serveur qui envoie du pâté (végan ) 100+ requêtes simultanées 100 threads ☠ Context Switching & GIL

Slide 9

Slide 9 text

Et si les threads coopéraient ? La partie CPU s'exécute sans interruption Quand elle lance un IO, elle s'interrompt,
 et laisse la place a une autre partie CPU L'IO s'exécute en tâche de fond,
 quand il est fini, il indique
 que le CPU peut reprendre Boucle d'évènements CPU I/O A A A A A B B B C C C

Slide 10

Slide 10 text

async / await (3.5/6+) async def coroutine(length: int): await another_coroutine(length) return 2 * length

Slide 11

Slide 11 text

async / await async def coroutine(length: int): await asyncio.sleep(length) return 2 * length

Slide 12

Slide 12 text

async / await async def view(request: Request) -> Response: user = await orm.get_user(id=request.id) return JsonResponse({"hello": user.full_name})

Slide 13

Slide 13 text

Appels def func(): a = sync_network() def func(): a = asyncio.run( async_network() ) async def coro(): a = await loop.run_in_executor( ThreadPoolExecutor(), sync_network ) async def coro(): a = await async_network() Appeler du code: Synchrone Asynchrone Depuis
 du code: Synchrone Asynchrone

Slide 14

Slide 14 text

Contre-exemples ❌ async def coro(): sync_network() async def coro(): k = 1 for i in range(1, 1e32): k *= i ** i def func(): await async_network() def func(): async_network()

Slide 15

Slide 15 text

A A A A A A B B B C C C A a oublié d'appeler ses I/O en asynchrones A A A B C C A C L'exemple de tout a l'heure CPU I/O A A A A A B B B C C C B lance une tâche gourmande en CPU

Slide 16

Slide 16 text

Disclaimer C'est vachement compliqué, ce truc quand même

Slide 17

Slide 17 text

Partie II Le quoi

Slide 18

Slide 18 text

WSGI Une application est un callable Reçoit une requête HTTP, renvoie la réponse HTTP Plutôt adapté pour HTTP (mais un peu vieillot)

Slide 19

Slide 19 text

Les WebSockets Canal bidirectionnel Plusieurs échanges ♾ Imcompatible avec WSGI ❌

Slide 20

Slide 20 text

ASGI Recevoir et envoyer plusieurs fois par requête Asynchrone

Slide 21

Slide 21 text

ASGI async def app(scope, receive, send): assert scope['type'] == 'http' # or 'websocket' ...

Slide 22

Slide 22 text

ASGI ... await send({ 'type': 'http.response.start', 'status': 200, 'headers': [ [b'content-type', b'text/plain'], ] ...

Slide 23

Slide 23 text

ASGI ... await send({ 'type': 'http.response.body', 'body': b'Hello, world!', })

Slide 24

Slide 24 text

Rendre Django
 compatible ASGI et async Et résoudre la faim et la paix dans le monde tant qu'à y être...

Slide 25

Slide 25 text

Les défis Async complètement optionnel Ne pas casser la rétro-compatibilité API familière "Thread sensitivity"

Slide 26

Slide 26 text

Partie III Le comment

Slide 27

Slide 27 text

La structure de Django WSGI Server View Middleware Handler URL router Form Template ORM From "Just Add Await: Retrofitting Async Into Django" - Andrew Godwin (PyCon AU 2019)

Slide 28

Slide 28 text

Phase 3 ORM async Phase 2: vues async Phase 1 Serveur async Le plan ASGI/WSGI Server View Middleware Handler URL router Form Template ORM From "Just Add Await: Retrofitting Async Into Django" - Andrew Godwin (PyCon AU 2019) Phase 4+
 le reste

Slide 29

Slide 29 text

Phase 1: ASGI Bien aidé par le le couplage faible entre WSGI et Django:
 WSGIHandler / BaseHandler
 
 ASGIHandler Point de passage async vers sync

Slide 30

Slide 30 text

Phase 1: ASGI WSGIHandler BaseHandler

Slide 31

Slide 31 text

Phase 1: ASGI BaseHandler WSGIHandler ASGIHandler ⚠

Slide 32

Slide 32 text

(r)Appels a = sync_func() a = asyncio.run( async_func() ) a = await loop.run_in_executor( ThreadPoolExecutor(), sync_func ) a = await async_func() Appeler du code: Synchrone Asynchrone Depuis
 du code: Synchrone Asynchrone

Slide 33

Slide 33 text

sync_to_async() asgiref.sync.sync_to_async Exceptions & tracebacks, thread sensitivity, thread locals

Slide 34

Slide 34 text

Résultat Ca marche C'est plus rapide si vous avez des uploads lents C'est déjà mergé dans Django 3

Slide 35

Slide 35 text

async_to_sync() asgiref.sync.async_to_sync Compatible

Slide 36

Slide 36 text

Phase 3 ORM async Phase 2: vues async Phase 1 Serveur async Le plan ASGI/WSGI Server View Middleware Handler URL router Form Template ORM From "Just Add Await: Retrofitting Async Into Django" - Andrew Godwin (PyCon AU 2019) Phase 4+
 le reste

Slide 37

Slide 37 text

Phase 2: Vues asynchrones Handler asynchrone capable de lancer: 
 - des vues synchrones (sync_to_async)
 - ou asynchrones Travail en cours, peut-etre en Django 3.1 async def my_view(request):

Slide 38

Slide 38 text

Le problème des Middlewares Cas 1 Middleware1> Middleware2> Middleware3> Middleware4>
 Cas 2 Middleware1> Middleware2> Middleware3> Middleware4>
 Cas 3 Middleware1> Middleware2> Middleware3> Middleware4>

Slide 39

Slide 39 text

Le problème des Middlewares Cas 1 Middleware1> Middleware2> Middleware3> Middleware4>
 Cas 2 Middleware1> Middleware2> a2s>Middleware3>s2a> Middleware4>
 Cas 3 Middleware1> a2s>Middleware2>s2a> a2s>Middleware3>s2a> Middleware4>

Slide 40

Slide 40 text

Le problème des Middlewares Cas 1 Middleware1> Middleware2> Middleware3> Middleware4>
 Cas 2 Middleware1> Middleware2> a2s>Middleware3>s2a> Middleware4>
 Cas 3 Middleware1> a2s>Middleware2>s2a> a2s>Middleware3>s2a> Middleware4>

Slide 41

Slide 41 text

Le problème des Templates Un template peut appeler de l'IO (lecture du disque) Une exception peut provoquer un template (500, 404...)

Slide 42

Slide 42 text

Évitons de se tirer
 une balle dans le pied Décorateur @async_unsafe

Slide 43

Slide 43 text

Phase 3 ORM async Phase 2: vues async Phase 1 Serveur async Le plan ASGI/WSGI Server View Middleware Handler URL router Form Template ORM From "Just Add Await: Retrofitting Async Into Django" - Andrew Godwin (PyCon AU 2019) Phase 4+
 le reste

Slide 44

Slide 44 text

Phase 3: L'ORM for result in Model.objects.all(): async for result in Model.objects.all(): instance.foreign_key

Slide 45

Slide 45 text

Phase 4: La stratégie 1. coeur sync, utiliser sync_to_async partout 2. coeur async, async_to_sync partout 3. Ne pas tout casser

Slide 46

Slide 46 text

Phase 3 ORM async Phase 2: vues async Phase 1 Serveur async Le plan ASGI/WSGI Server View Middleware Handler URL router Form Template ORM From "Just Add Await: Retrofitting Async Into Django" - Andrew Godwin (PyCon AU 2019) Phase 4+
 le reste

Slide 47

Slide 47 text

Merci Des questions ?