We're sorry but this page doesn't work properly without JavaScript enabled. Please enable it to continue.
Feedback

Using the right Async tool, present day

00:00

Formal Metadata

Title
Using the right Async tool, present day
Title of Series
Number of Parts
160
Author
License
CC Attribution - NonCommercial - ShareAlike 3.0 Unported:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this
Identifiers
Publisher
Release Date
Language

Content Metadata

Subject Area
Genre
Abstract
Using the right Async tool, present day [EuroPython 2017 - Talk - 2017-07-11 - Arengo] [Rimini, Italy] Recent releases like AsyncIO and Django Channels gave a new push towards building real-time web-apps fast and easy. However, as similar tools exist in Python since 2000th, how should we balance between modern and time-proven? This talk includes but is not focused just on AsyncIO. It gives an overview of Async libraries in Python, and helps with choosing a right tool for various web tasks. It describes caveats of using Twisted, Tornado and AsyncIO including theory and live code, and concludes with a basic overview of Django Channels. Talk plan Why do we need Async Web (5 min) Existing libraries and frameworks: Twisted, Tornado, AsyncIO (15 min) Sample task, sample code, conclusions (10 min) (optional) what's about Django Channels? Q & A (5 min)
95
Thumbnail
1:04:08
102
119
Thumbnail
1:00:51
Axiom of choiceCodeServer (computing)World Wide Web ConsortiumThread (computing)NumberResultantFormal languageSoftwareDependent and independent variablesAxiom of choiceProcess (computing)Software frameworkPlastikkarteTheoryDatabaseCodeEndliche ModelltheorieMultiplication signGoodness of fitWorld Wide Web ConsortiumBitPoint (geometry)PlanningNeuroinformatikWeb-Designer3 (number)Task (computing)Software developerThread (computing)TrailArithmetic meanClient (computing)Server (computing)Descriptive statisticsScaling (geometry)Shared memoryComputer fileInheritance (object-oriented programming)Computer programmingWorkstation <Musikinstrument>Decision theoryGenderDemo (music)CountingPhysical lawVideo gameHypermediaWeb serviceWordWeightPartial pressureWeb pageResidual (numerical analysis)Web browserDirected graphPatch (Unix)Arithmetic progressionClassical physicsComputer animation
Server (computing)World Wide Web ConsortiumEvent horizonThread (computing)Loop (music)Concurrency (computer science)Java appletScripting languageSequenceCodeProof theorySoftware frameworkCore dumpProgrammschleifeNetwork socketComputer networkWorld Wide Web ConsortiumThread (computing)Server (computing)Connectivity (graph theory)Point (geometry)Task (computing)SynchronizationCodeAuthenticationSoftwareSoftware frameworkEmailElectronic visual displayMultiplication signDifferent (Kate Ryan album)Basis <Mathematik>NumberWritingPower (physics)MereologySocket-SchnittstelleFactory (trading post)Axiom of choiceArithmetic meanConnected spaceOperating systemCommunications protocolRadical (chemistry)Semiconductor memoryDependent and independent variablesPattern languageSystem callImplementationRuby on RailsCASE <Informatik>Physical systemNetwork socketGoodness of fitRight angleFormal languageEvent horizonLoop (music)Client (computing)Machine visionProgrammschleifeComputer hardware2 (number)TheorySet (mathematics)Social classDivisorExtension (kinesiology)GenderOperator (mathematics)Natural languagePairwise comparisonLevel (video gaming)State of matterElement (mathematics)FreewareDirect numerical simulationDisplacement MappingRule of inferenceSeries (mathematics)Local ringHeat transferComputer animation
Electronic data interchangeContent (media)TelnetCodeNetwork socketSlide ruleWeb browserComputer animation
Arithmetic meanDigital electronicsImage resolutionKeyboard shortcutDemo (music)Game theoryTelnetSocket-SchnittstelleVirtual machineNetwork socketGoodness of fitSource codeComputer animation
Computer networkNetwork socketWindowServer (computing)CodeFormal languageTime evolutionSequenceEvent horizonCoroutineLoop (music)Process (computing)Function (mathematics)Presentation of a groupForcing (mathematics)Goodness of fitWordBitSinc functionRight angleCore dumpDirected graphWeightConnected spaceTheory of relativityCASE <Informatik>Social classSuspension (chemistry)ResultantSoftware developerMultiplication signElectric generatorSynchronizationExpressionGraph coloringPoint (geometry)Library (computing)LogicConnectivity (graph theory)State of matterWeb-DesignerWell-formed formulaCommunications protocolIdentical particlesAuthorizationSummenregelFree variables and bound variablesBlock (periodic table)Web applicationGenerating functionDatabaseOperator (mathematics)Link (knot theory)Set (mathematics)Object (grammar)Physical lawSoftware frameworkTask (computing)CodeTheoryWorld Wide Web ConsortiumEvoluteMultiplicationCoroutineFunctional (mathematics)Factory (trading post)NumberStudent's t-testDivisorDemosceneBasis <Mathematik>Cartesian coordinate systemConcurrency (computer science)Data conversionNetwork socketProzesssimulationAxiom of choiceCodeGastropod shellSoftware testingGodRobotSoftwareWritingAnalytic continuationVolumenvisualisierungMobile appReal numberComputer animation
Event horizonSystem callCoroutineLoop (music)SequenceWeb applicationServer (computing)Client (computing)Uniform resource locatorCASE <Informatik>Wrapper (data mining)Operator (mathematics)MappingWordProteinDependent and independent variablesEvent horizonFunctional (mathematics)Loop (music)Task (computing)Pattern languageEndliche ModelltheorieMultiplication signPhysical systemWindowSelectivity (electronic)Default (computer science)ResultantDatabaseDirected graphRule of inferencePhysical lawSoftwareWeightArithmetic meanElectronic visual displaySingle-precision floating-point formatLibrary (computing)Point (geometry)AuthorizationIsing-ModellCartesian coordinate systemStatement (computer science)NumberStaff (military)World Wide Web ConsortiumTerm (mathematics)Vector spaceDifferent (Kate Ryan album)Right angleMultiplicationPlastikkarteOperating systemInequality (mathematics)Computer scienceBitInformation securityStreaming mediaVideo game consoleSoftware frameworkPRINCE2CodeSystem callMaxima and minimaServer (computing)Uniform resource locator2 (number)Flow separationWeb applicationModule (mathematics)ImplementationGoodness of fitVariable (mathematics)Wave packetCoroutineSynchronizationRoutingExterior algebraComputer animation
Client (computing)Uniform resource locatorCoroutineAuto mechanicSimilarity (geometry)Software frameworkStack (abstract data type)Loop (music)Event horizonWorld Wide Web ConsortiumMobile appServer (computing)Concurrency (computer science)ImplementationPhysical systemRight angleChaos (cosmogony)Table (information)Normal (geometry)Functional (mathematics)Dependent and independent variablesData structureEvent horizonInternational Date LineMultiplication signCodeElectric generatorCommunications protocolBitCartesian coordinate systemProcess (computing)Computer configurationWeightDemosceneGroup actionVirtual machineLevel (video gaming)Adaptive behaviorPattern recognitionWorld Wide Web ConsortiumScaling (geometry)Operating systemLoop (music)SoftwareLibrary (computing)Type theorySingle-precision floating-point formatCASE <Informatik>ProgrammschleifeDirected graphClient (computing)Exterior algebraIsing-ModellWeb applicationMaxima and minimaSocial classMultiplicationDifferent (Kate Ryan album)Electronic mailing listSet (mathematics)Data dictionaryGoodness of fitWindowKey (cryptography)Basis <Mathematik>Parallel portMereologyTask (computing)Commitment schemePoint (geometry)Mechanism designSoftware frameworkSynchronizationNeuroinformatikResultantPolarization (waves)Form (programming)HistogramSelectivity (electronic)Perspective (visual)Data storage deviceProduct (business)CoroutineBefehlsprozessorThread (computing)Letterpress printingInheritance (object-oriented programming)Uniform resource locatorOperator (mathematics)System callComputer clusterInstance (computer science)RoutingTupleWeb browserWritingQueue (abstract data type)Medical imagingPercolation theoryMilitary baseSoftware testing
Goodness of fit2 (number)MeasurementTerm (mathematics)Library (computing)Frame problemSemiconductor memoryMeeting/InterviewLecture/Conference
Transcript: English(auto-generated)
Hello, everyone. Super cool to see you today. Yeah, I'm not the Postgres guy, sorry. I used it, I love it, but today I'll be talking about the different thing. Yeah, we will talk about async, and async does not mean asyncio.
Python is a rich language with a lot of nice frameworks and tools. We'll be looking shortly in many of those with a little live code, with a little demo, with a little bit of theory as well, just to have an overview, what is there, what's available for us right now, what tool can we use under what circumstances, and so on.
So, the way we're going to do it today, we all come from different backgrounds. Some of us already have experience with async tools and general network development in Python, some don't. So you can interrupt me and ask questions right during the talk, because I really want that everyone
keeps track of this talk well. So please do not wait until the end, raise your hand or just ask the question, I'll repeat it. I'll do my best to keep everyone in a good mood and understanding what's happening here. Let's go, all right. So, first thing I have to say is,
I know it was a difficult choice to come to this room, having seven tracks in EuroPython and also having this. I know what we've been thinking about, it's this versus this, and burning your eyes with code, especially with my code is of course a tough decision, so thank you for taking that.
We'll do our best, so as I said, I have a live examples in my talk included, and we can also have discussions right during the talk, and we are in Italy, so I hope coffee helps as well. Who am I? Very shortly, who knows this logon, my T-shirt. Yes, it's PyCon Web, so I didn't do any awesome framework
in Python yet, unfortunately, I hope so. I will do it in the future, but for now, the only thing I can say about myself, no frameworks, okay. No more frameworks. All right, all right, yeah, thanks. Yeah, so for now I can say that I'm the one who is organizing PyCon Web, so that's probably the best description of myself,
and I'm a freelancer. Yes, and the plan about today's talk is first, we'll just have a bit of the theory recap, like what is async computing in general, what we have in Python available, what tools we have that we can use already now. We will go then into Twisted Tornado
and asyncio a bit more detailed. We'll look, including the examples, how it works. Then we'll have Q&A, and I hope we'll have some time to just briefly discuss Django channels, because it's a trendy topic, and it's also kind of async-ish, so I hope that we have time for that too.
All right, so what's async? A quick recap. What is it in general, this buzzword? So what we see on this picture is a task. We have, you can see my laser pointer? Yes, great, all right. So we have a task that is processed synchronously at first.
We have a request coming in here, we have processing until here, then the result coming out. Then the request number two comes in between, then we're processing it, and response. And then request number three coming in, process response. That's like a classic model that they show us in school,
but the thing is that it actually never works that way, obviously, because one request will never wait for another request to be completed. In the reality, we will have one request, and then right away another, and then right away the third one, and so on and so forth. So our requests never wait for responses. They will just come and hope that we can handle them somehow.
So you see that the request number one comes, our main thread of the program is processing it, and then request number two comes, but we do not have resources for that, so it just waits. Then request number three comes, and it also has to wait because we don't have resources for that. And then when the request one is done,
only then we can go on the request number two, and only then we can go to the request number three. That's the reality. Now what we can also notice in this picture is that the request number one, it also has some spots here which I marked as waiting. This means that when we are processing some request,
it does not really mean that we are heavily computing something all of the time. Most of the times, especially in web development, we are waiting for something else, like for the database or for file IO, maybe some slow NFS share or whatever. So we are not really computing something all the time. We are often waiting for another resources,
and the problem in this approach is that even though we are just waiting, since we have just one main thread and we are dealing with this one thread with one request, other requests have to wait, and that could be optimized. And this is where asynchronous computing comes in. So the idea is that instead of waiting,
we can just do other work. So we can work on other requests too. And this is what's shown on this chart. You see the request one comes in, it's processed. Then let's say that at this point where request number two comes in, request number one is actually waiting for the database. So a smart software says, okay, we can wait here for some response.
Meanwhile, we will do another thing, and another thing is request number two. And we start processing it. Easy, good. Next. So if we map this very, very simple charts that I showed you on the web servers and the web development in general,
this is how it will look. So obviously, one request is one client, or one client request in a browser, let's say, or some API. So let's say we start our overview with a threaded web server. And this is a very simple idea, how can we handle many requests concurrently,
is that we just spin a new thread for every new request. This totally makes sense. It is still a blocking approach, but we can scale, because the more requests we have, the more threads we start. And we handle each new request in a new thread. Task one, meaning user one is requesting some webpage,
we're handling it here, then user number two joins, we handle it here, and so every next user starts another thread on our backend. The problem is obviously it doesn't scale very well. Who used Apache? Maybe old school. Okay, good. So I used Apache too.
This is Apache, in my opinion, under many threads. When you have too much. So it doesn't scale, that's the problem. And the biggest problem of that is that when Apache does this, not only your new client will just get nothing, but also the client that are already in progress, those who are almost done.
If Apache crashes, that's it. Nobody gets anything. That's the problem of the threaded server. So there was a smart idea, why don't we have a pool? We can say our threaded server is capable of, I don't know, 16 threads, let's say, and it does it efficiently. So we have a fixed pool of 16 threads.
Whenever there is more, you just have to wait. But we at least know that these 16 are covered without any problem. Yeah, that's a good idea. So we have, in this example, just two threads in the pool. We have thread one that is doing something, thread two, then the request number one, and the request number two, which is just waiting. That works, yeah.
So threads, a threaded pool works until, obviously, we just reach a point where there are not enough threads in the pool to handle all of the load. The good thing is that they just have to wait. The clients, I mean, it will not crash as hard as a normal threaded server, but yeah, you can do more with your server.
And now comes the synchronous web server. From the same theories that I already shown to you, we have just one thread, and we're using this thread to switch between different tasks in different points of time. So whenever we have free slots, free resources, we are taking another task.
When the response, or when some, let's say, the network socket has data of our previous request, we are switching back to it, and we're finishing it. Don't worry, I mean, it's a very, very brief overview. I will get into details, it will get more clear later on. So the main thing that I have to point out right now is that in this scenario, we have just one thread,
and we hope that this one thread will, like, jungle with as many tasks as it can and switch on some smart scenario that whenever there is some blocking operation, the thread is doing another task. That's the whole idea. I didn't find a better GIF, so this is the thread,
this is the asynchronous server in my vision. Good. Of course, it was just blah, blah, so let's have real charts. Here you have Apache Lite, HTTP and NGINX, Apache being threaded server, NGINX being asynchronous web server, and these are requests per second charts.
So you see here that NGINX gets as high as possible on this comparison. It can handle around 12,000 requests per second. Obviously, it depends a lot on your concrete server and hardware and so on,
but that's approximate pictures that you can see in practice. Then there is another good chart, is the memory usage. As I already said, threads are expensive, so whenever Apache starts a new thread, it costs you. So the more clients you have, the more threads you run, the more memory is being used.
In contrast, NGINX and LiteHCPD are quite sparing with your operating system resources. Good. So now let's get more into practice. So Async Python is not AsyncIO. There have been tools around for years. Let's say that Tornado is eight years old,
and then there is Twisted, which is, oh, it's quite a while, I'd say. And then there are also other languages there, and you have Async. It's not something new. So the question is, why are blocking servers still popular? Like, you can say, why is Django popular?
It's not Async. It's so cool. I'm using Django because it's easy. I think everyone would agree that sequential code is just much easier to write. You don't want to have a callback hell like you have in usually Node.js. You write your nice, fancy code in Django, and if you don't have very specific tasks that Django cannot handle, you're happy with it.
So why I'm doing this talk is, I'm going to show you that there are different tools in Python that allow you to write asynchronous code as easy as synchronous code, or at least comparably easy. Yes. And the goal is to make it as easy as Django or Ruby on Rails.
Then I'd say that we can seriously consider using asynchronous tools of Python on a daily basis. So, yeah. As I said, we prefer anything that covers our needs in the simplest way possible. So good news is that we have many choices in Python already at this point, and we can try different things
which we are going to do today, and we will see what is best suitable for us. Let's start with Twisted. Twisted is like a Python dinosaur. Who used Twisted in this room? Oh, cool, many people. That's great. I love Twisted. So Twisted is a framework. It's huge. It includes powerful high-level components like web server, user authentication systems,
as well as mail server, I don't know, instant messaging, SSH clients, DNS clients and servers, anything. So basically, any network task you have, most likely you will have it already solved with Twisted. Also XMPP server, anything, really. So if you are looking for, let's say,
some implementation of some network protocol with Python, then Twisted is your best bet. Most likely it's already done in Twisted. So as they say, you don't call Twisted, Twisted calls you. That's like a motto of Twisted. That actually applies to all async frameworks in Python,
but they've been first, so let's give this credit to them. The base of Twisted is the event loop, as all Async tools, obviously. So Twisted is built around the reactor pattern. It's an event loop responsible for handling network and system events. It basically says when the event A happens, react with a function or with a callback B.
And once started, the reactor loops over and over doing this kind of task. So it polls for IO and just triggers the appropriate callbacks depending on what events we have loops over and over. Then, let's have an example, finally.
So as I said, Twisted is a really low-level framework. As our first example, I would just try to print something on the network socket, so as low as possible. And here we go. I have the solution right here.
I will run it later on. First, let's go through the code just to show you what it's doing, so you feel comfortable with this. So, what do we have here? It's a really, really simplistic, minimal case. How do you write into socket in Twisted? So first, you have two factories, of course.
You have a protocol factory here, first, and the server factory here. You define these two classes, and that's actually what you have always in Twisted. You have a factory for everything, and you have a callback for everything. It makes it kind of complicated, but if you are really willing to implement
some difficult protocol, nice and clean, it's a good way to do it, because you cannot miss something accidentally. So, of course, the main part here is a greeting protocol. We inherit from protocol protocol, and we define our first and only callback, connection made, meaning that whenever there is a network connection on the socket,
we do self-transport right, and that's it, and we close the connection. That's the only thing we do here, and let's see if I lie to you or not by running this in terminal. Okay, you don't see my terminal.
I will bring it to you then. No?
Okay, I will just mirror the displays. It will be faster in this case. Can you see this? Not really good? Okay, let's start like this, then we see.
Okay, so I have all of the examples here. I started the example twisted1.py.
It's the same code that you've just seen on the slides, and to check it, I cannot do it with a browser, because it's a telnet socket. It's just a raw socket of this machine, so I'll use telnet.
Bless you. Okay, so we have here chow euro Python.
Ah, you don't see it at all. I got it. Okay, I need to zoom in somehow. Can I change the resolution, or will it crash? Okay. Or anyone knows the shortcut for iTerm to zoom in?
Comment plus, okay. Yes, thank you guys. Thank you, great. Okay, so then again the demo. You see, chow euro Python printed here. It worked, good. Then, jumping back to the presentation.
No? Yes, I'll take mirrored off. Yeah, this jumping back and forth is not perfectly optimized. I'm sorry, but we'll do our best, good.
Okay, we're back. Then, of course, just a socket connection is kind of boring. We are web developers, most of us, I think, in this room. So let's see some web related case.
Let's have a twisted app that returns chow euro Python as a get request handler. So what we have to do here is we inherit from the resource. This is a class in twisted that implements get the, well, like, HTTP stuff.
Then we define the render get methods. You see it here, render get. And it's as easy as return chow euro Python and Python 3, so encode UTF-8. I think I will not do this mirroring again, so you can just trust me that it works. If we have time at the end,
I will show you these examples in the shell, otherwise it gets really boring. Next, twisted is cool, so I'm not going to show you much more twisted because the goal is just to show you an idea. If you implemented some protocol, you are inheriting from the protocol class, you need to define factories of factories of factories,
and eventually it will work. By that, I want to say that twisted is an excellent choice for the vast number of cases, and especially if you need to integrate some multiple aspects of functionality for some network protocol, especially a complicated one. So first of all, twisted is a concurrency framework. It allows you to juggle multiple tasks in one application
without using threads. That was the goal of asynchronous development. And the code tends to be a bit complicated, I'd say. It's actually not complicated, it's just complex, so you will have a lot of code. But the good thing is that you can reuse components of twisted, and they have components
for pretty much anything you like. So it's easy to hack into existing protocol and customize it to your needs. It's also perfectly tested. So if you would write XMPP bot or something, I'd say go with twisted. But then, there are other tools
that make async development in Python even easier. Just a bit of words about async evolution in Python. Since Python 2.5, we have the yield keyword. And it was used just for generators, and then after some time, there was a tornado web framework
that used it for asynchronous development of web applications. I will show you examples later. Then in Python 3.3, we got the generator delegation expression yield from, which is a very similar concept, but it's just a bit better. I'll show you later. And then finally, at 3.5, we have await,
which in 99 cases for you will be totally the same as yield from, but it just makes more sense as a word. So it's easier to understand what is it actually doing. And these little things, they matter. So they help you to write asynchronous code in the similar way that you write asynchronous code.
Now let's get to more examples. You do not have any excuse not to use asynchronous code in Python since 2.5, because that's the point when we got yield and generators. But I would recommend to use Python 3, especially the 3.5, because it makes things even better.
And again, you do not need to write callbacks every time like you do it in Twisted. And why callbacks are bad? Who is also JavaScript developer? Or just some JavaScript? Okay, so I don't have to tell you. It's difficult to write nice code. And I have a little favorite example
that travels with me on different conferences. Here it is. So yeah, this is the real code from MongoJS. It's testing, as I can see here. But still, I know it's not something that you need to write every day. But if you even had to write this for your tests, there is something wrong.
Oh, and I recommend you a callback, hell.com, if you're not convinced they have more cool examples. So let's just agree that sequential code looks nicer. And we will try to avoid callbacks from this point. Asyncio, the buzzword. I think I don't have to stop really long time
over this one because you have awesome talks as well in this conference about it. Still, obviously, I will show you the basis. So we have some new concepts that can help us to escape the callback hell in Asyncio and as well in Tornado. For that, we need to learn a bit of a theory. Who knows what is the future? So maybe I can skip it.
A future object, okay. Let's have a little conversation here then. So a future is just like a placeholder object. It is designed to receive and store a result of some operation that is not available right now at this point of time. So we use a future as a placeholder to pass around.
And this will have a reference to the result of our operation. In the example that I told you in the beginning, like if we are waiting for the database result, we don't want to just block and wait for this result to be available. We can store it in the future and say, this future, it doesn't have a result yet. It has a reference to the result
and it will be ready at some later point. So this future object can be returned to the caller right away and that caller can access the actual result later when it's available. And this is the simplest way to use a future to run it. So this is obviously pretty dumb because it's still synchronous,
but it just shows you how it works. So you need a library that works with futures, like return futures, and for example, some async HTTP library. You do fetch europython.eu. Let's say that it's slow, even though it's not, but let's pretend it's slow and we don't want to wait and block. So instead, this library will return you a future,
save it in the future variable, and then we say to the IO loop, please wait until this future is ready. And then when it's ready, we just call future.results to get the actual result of this operation. As easy as that. Now next, the coroutine. A coroutine is a function.
It's a spatial, like a generator function that can return some value and suspend. So when we call it again, as generator, it will continue from the place where it left off last time. So unlike a function, it can be called multiple times and continuing and pausing multiple times.
And this is a minimal example of a coroutine. I didn't use the 3.5 syntax on purpose because coroutines are something that you can get in Python since 2.6 or 2.5 even. So there is really no excuse not to use them. So you define it just as a generator. You say for chunk in data.read,
and let's say that data.read is some really heavy operation, and then as a generator, we're just yielding chunks back to the caller. That's a minimal example. Then what does it have to do with the synchronous development? Like coroutines play really well with futures because future is something that can hold a link,
a reference to the result, as I already said, and the coroutine can return it. So coroutine returns futures. That's the way it works. Coroutine returns a future. It pauses until the future is ready, and then it continues from this place where it stops. This is an example.
A coroutine, and then we're already familiar with this asynchttp.fetch, a library that does not block but instead returns us a future, so a reference to the result. Then we yield the future. So this is a new concept here. By yielding the future, we are giving it to the caller.
Caller in this case is the IO loop, and we say that okay, please do not wait. Do other stuff in the meanwhile, like handle other requests in the meanwhile, and whenever the result of this future will be ready, please just jump back in this place and continue as if yield was never here. That's kind of a magic how it works,
and then we just print it. I mentioned that the event loop is caller in this case. So what is event loop? It's kind of a reactor pattern that we know from the computer science course. It just waits for something to happen, and it acts upon the events. It's responsible for handling such things
as IO and system events, and I think IO, it actually has several loop implementations available, like the module that will default to the one most likely to be most efficient and used in operating systems, like for Windows, it's still using the select loop,
but for Linux and Mac OS, it's KQ. So I think I will guess which IO loop can be used in the best case for your particular operating system. It delegates it to the operating system loop and just acts as a wrapper. However, you can always explicitly choose
the underlying loop if you like. And again, in a few words, if you don't want details, the IO loop is just something that says when the event A happens, do the function B and save this mapping. That's, in a nutshell, what it does. Let's see what async event loop is doing.
So here, I already used the 3.5 syntax, async def. This is just a fancy way to define a coroutine. It shows you right away that, hey, this is not just a function and not just a generator. It's a coroutine, async def, fancy. So this is Anton's talk, and what coroutine does is it just says do the talk
and prints question after this is finished. We do not have any yields here. So this is just a minimal example. How do you run the IO loop? No yields, no futures, just a minimal example. Now, a bit more complicated. Let's have multiple coroutines.
So there is, here is Anton's talk, and here is a coroutine, grab the coffee. What I want to show you here is that how easy you can switch between two, thanks to your IO loop and the async IO. So first, we run Anton's workshop or Anton's talk. We print welcome. We await, await is the same as yields in this case.
So we do the blocking operation here, do talk, and at this point where you use yield or await, the library is smart enough to say, okay, we are waiting on some blocking resource. Let's do something else. And something else in this case is coroutine grab coffee. So then the next line that will be printed
will be sip, drinking coffee. And then we are waiting for something again. So IO loop will say, aha, we have to wait here as well so now we jump to some other thing, and so it jumps back to the Anton's talk. And then this statement will be printed, thanks for coming. And then after this coroutine is finished, it will continue number two again.
So this is just a minimal example how the IO loop will switch between tasks for you. Could you please repeat?
Yes, you also have Anton's workshop and here Anton's talk. Thank you for noticing it, it was my bad. Because I planned it as a workshop first, but then I packed it in the talk. So yeah, this will not work. But this is absolutely good anyway, so I will not run it. But thanks for pointing it out.
So let's compare the approaches. Like I'm still trying to explain the general thing about coroutines and futures to you. So this is also a very, very simplistic pseudocode. This is a way that you would write usually a sequential code in any framework, any web framework that you use.
Let's say this is a get request handler. We have a huge database query, a synchronous one. We save it in the result and then we print the result. Easy, everyone did that. We will be blocking here. So we will just wait at this point. That's how it works in most cases. That's how it works in Django. Then, how would you do it in JavaScript or in Twisted?
You would say, make the huge database query and then you will use a callback. You would say, when the huge database query is ready, call the on result function. So this is a bit messy because it pushes you towards the spaghetti code, but it's at least very clear
because you're not using any coroutine future magic. So it's very explicit. You say, when this is done, call this function. This is the callback way of doing the same thing. And this is the AsyncIO alternator way of doing the same task. You're defining a coroutine, async def.
Then, assuming that your huge database query returns a future, so it supports this kind of operation, you put a weight in front of it. And by that, you're telling basically the same thing as in the example above, but without a callback. So, huge database query returns is a future. You yield or await that future,
give it back to the IOU loop. Say, when this future is ready, please give us a result and just save the results to the result variable. So this and this should be the same for you. And they actually look the same. That's the whole point of this talk, to show you that async does not mean callbacks.
Good. So now, again, let's have a real example. IOU HTTP server. IOU HTTP is a synchronous HTTP framework or library available for AsyncIO, and its author, Andrei, is actually at this conference, so I really recommend you to go to his talk
or his training to go in details on this. I will be just showing you a minimal example. So a web app that returns hello-rimini is as short as that. You do, again, the async dev, so define your coroutine. Then you return web response hello-rimini.
No yielding, no awaiting. And then you run it. You create web application. You define your first and only route. You say that whenever it goes to this route, you execute the coroutine hello, and then you run it. So this is the simplest example. You're not waiting for the database here, of course. That's why it looks so easy, but that's a minimal thing you need
to run the IOU HTTP server. Of course, we want to get more tricky. We want to have async and await to be more realistic. So this is a very same example, but now you are going to some URL. So this URL is HTTP bin. It's a nice tool that gives you a fake delay.
It's very cool to test your software. So this will give us delay of one second. Just if we will eventually run this code at the end, you will see it. What we are doing here, we are defining again a handler. We are requesting with get method this URL on top,
and then the result will be a future or a coroutine. You are awaiting for it, meaning giving it back to the IOU loop. Whenever the result is ready, we are saving it to response. And now the surprise. Why are we awaiting for it the second time? Who knows? Any ideas? Okay, so actually there are two blocking operations
in this simple example of fetching a URL. First blocking operation is when you fetch the first byte, like when you want to get the first byte of response. This is the first operation when you need to wait for something. But then there is a second one. If the response is not going all at once, if it's a streaming response,
this is why you need to wait for the second time. So let's say that first byte comes in one second, but the last byte comes in one minute. So you would be blocking the difference, right? So that's why you do it twice in this example. And then you print the result again. So this looks not, of course, not exactly the same as Django,
but I would say it's still decent and easy, and you're not using callbacks. Then what if we need to request multiple URLs in parallel? I will jump over this really quickly, because as I said, you have a dedicated talk for iSync.io
and IE HTTP on this conference. So I will just say that if you want to request multiple URLs and do this in parallel, it's also very easy. You just need to define multiple tasks, make a tuple of those, and yield a tuple that will work.
This is the second way that you can do the same thing. If you are requesting multiple URLs, you can use iSync.io.wait, a function that would let you pack multiple futures in one. I'm not stopping over this on purpose, because I want to show you Tornado as well. And now, Tornado.
A big difference between Tornado and iSync.io. Tornado was there since 2.5. So even when you didn't have the fancy syntax of iSync.await and iSync.def, you already could use Tornado and write asynchronous code with it in Python, using the generators. And I will show you how right now.
So Tornado runs on the same idea of IO loop futures and coroutines. It just uses hackish way of achieving the same thing, because, again, in 2.5, there was no native way of having this functionality in Python. So it has different mechanics, but it is using the similar syntax. And the main thing, it is well tested.
It is stable. It's used really a lot, I can tell you. So it's totally production ready if you have some web application that needs to deal with web sockets, think about Tornado. It's really a good option for this. How does it play together with iSync.io? Well, this is the idea of the stack that would work.
On top, we have application level, like Tornado, Twisted, or whatever. Then we have the IO framework, iSync.io. And then on the operating system level, as I said, we have the queues that are most efficient for the operating system, like KQ or EPOL or Select on Windows. It doesn't work like that right now. So Tornado and Twisted, they both come with their own event loops.
But this is the idea that maybe at some point it will work like this. Event loop of Tornado. Very similar to iSync.io. You don't have to remember the syntax. I'm just showing you that it's as easy as IO loop dot current. It's a singleton. You get the IO loop, and you do IO loop dot start to start it.
There is no big magic behind it. And then if you want to run the iSync.io event loop, it's also possible. There are adapters available in Tornado to use futures as well as IO loop from iSync.io. And this is how you do it. You just import iSync.io main loop, and you start it. So it works too.
Then futures, again, are compatible. So Tornado prefers own futures, but you can use futures from iSync.io as well. And this is really handy, because some libraries will give you futures of iSync.io type, let's say. And you would say, hey, why did I do this whole application in Tornado, and now it's incompatible. So it is compatible.
That's a really good thing. You can convert between Tornado and the iSync.io back and forth easily. Finally, minimal Tornado web app. It's very similar to the iSync.io example. You have a handler class. You have a get function that handles it,
and you do self.write to return the response to the caller. And in the same way, you define the routing. You say, listen on this port. You start the loop. That's it. Now let's have an example of fetching the URL in Tornado. It's quite similar to iSync.io.
Tornado has own iSync HTTP client. Here it is. That is doing the fetching for you. So first you create an instance of the iSync HTTP client here. Then you do fetch URL. This returns you a future that you yield to the caller. Tornado supports the await syntax as well.
I'm just showing you here that you can use it with Python 2 as well, which is of course a bad thing, but you can still do it. So you yield the future to the IO loop. It does the processing, and whenever the result is ready, it saves it to the response variable. And then you can print it to the caller, print it to the client.
Good. Then to fetch multiple URLs, you can do it like this. I need my mouse here. So I did it on purpose here.
Yes. I did it on purpose here. I didn't include multiple URLs to show you how easy it is to fetch multiple URLs instead of one URL. So if you have multiple URLs, all you need to do is you wrap this as a list,
assuming that URLs is now a list and not a single URL. So this is everything you need to change to fetch multiple URLs instead of one URL. Tornado is smart enough to notice
that when you yield not one future, but the list of futures or dictionary of futures, it will automatically process all of them in parallel. So it's super handy. Yes, yes, you can. Good, now I'm running out of time.
So just a little wrap up. There is Twisted that is super well tested. There are implementations of all of the protocols that you can imagine. It's production ready and it's time proven, so use that if you want to do something really complicated, like a difficult network protocol.
There is AsyncIO, which is the future. So if you want to make something in the perspective of future support, take that. And it is using the most fancy syntax available in Python. And then there is a Tornado. It's kind of a compromise that stays in between. I personally use Tornado just for historical reasons, but I'm now switching slowly to AsyncIO.
I think that's it. You're great, thanks for not sleeping on this talk. And now let's go to questions. First one. Quick questions, because we only have two minutes.
Yeah, my question is this. I see most of the parts when you apply synchronous calls are IO. Yes. What are the benefits of asynchronous processing when most of your things are CPU bound? Or, yeah, what would be the benefit in that case? Great question. So yes, it is in the first place
designed to be used this way. So first advantage that you can take is AsyncIO. That's why it's AsyncIO. I would say that 90% of cases of waiting in the web application is IO. I cannot even think of another case. Well, maybe you have some difficult
face recognition system running and it takes time. Yeah, you will not have any benefit. One thing that you have, though, is how else would you do it? You would start a thread normally, yeah? That's a typical way. So you can also do it with AsyncIO. There is a thread pool executer and thread and process pool executer classes
in AsyncIO library. So then the advantage for you would be that you would not need to write two different code bases. You would not need to use two different approaches. You could use perks of AsyncIO to wait for the IO events and for your fancy image recognition software. You could start threads but still use AsyncIO syntax.
It would make testing probably easier a bit and it would just make your code look the same way so the structure is better. If you have only, let's say, some difficult machine learning computations, there is no advantage as I see it. I could be wrong.
One more question. Anyone? No, okay. Is it good or is it bad? It's good. Okay. You covered it really well. One, yes, great. Yes.
So I know that this is probably very similar to each other. I mean those frameworks. I'm wondering whether you compared like a performance of them or maybe there is something that actually makes one of them less performant.
Great, great question. So yes, I did compare the performance. Of course, performance is such term that there are very numerous ways to measure the performance and you could measure the memory, you can measure the speed, many things. So what I did measure is requests per second. I measured Tornado, Async AO, Node.js and Scala Finagle
like four libraries that do the same. Sorry guys, Scala did the best but at least we are better than Node.js. That's also good. Actually. And on that note. Yeah. I think we need to head on out. Yes. So. Good.
Thank you all for coming. Yeah. Thank you very much. Thank you. It was great. Thank you.