Using the right Async tool, present day
Caceres, Anton
Using the right Async tool, present day 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
 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 async.io. 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 life 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're all like coming from different backgrounds. Some of us already have experience with async tools and generally network development, and 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 the 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 on Euro Python and also like 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 logo on 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. All right. All right. 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 a syn 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 I think I owe a bit more details. 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 a sinkish, so I hope that we have time for that too. All right. So what's a sink? A quick recap. What is it in general, this buzzword? So what we see on this picture is a task. Yeah. We have, you can see my laser pointer? Yes. 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 we will, our requests never wait for responses. They will just come and hope that we can have them 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 we just wait. 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 like 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, we 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. It's 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 web page. 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 a 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 like in progress, those who are almost done, if Apache crashes, that's it. Nobody gets anything. That's a problem of the threaded server. So there was a smart idea. Why don't we have a pool? Like we can say our threaded server is capable of, I don't know, 16 threads, let's say. 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 thread at 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 normal threaded server, but yeah, you can do more with your server. And now comes the synchronous web server. From the same theory that I already shown to you, we have just one thread, and we are using this thread to like switch between different tasks in different points of time. So whenever we have free slots, free resources, we are taking another task. When there is, when the response or when some, let's say the network socket has data of our previous request, we're 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 give. So this is the thread, this is a synchronous web server in my vision. Good. Of course, it was just blah, blah. So let's have real charts. Here you have Apache light, HEPD and NGINX. Apache being threaded server, NGINX being a synchronous web server. And this 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 picture 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 light HEPD are quite sparing with your operating system resources. Good. So now let's get more into practice. So async Python is not async at all. There have been tools around for years. Let's say that Tornado is eight years old and then there is Twisted, which is quite a while, let's 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 much easier to write. You don't want to have a callback hell like you have usually not 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, at least comparably easy. Yes. And the goal is to make it as easy as Django or Ruby on Rails. Then I would 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 message in SSH clients, DNS clients and servers, anything. So basically any network task you have, most likely you will have it already solved with Twisted. So 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, Twisted doesn't call, 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 always, I think, so 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 pulls for IO and 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. So 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 we have here. It's a really, really simplistic minimal case. How do you write into socket and Twisted? So first, you have two factories, of course. You have a protocol factory here first and a serial 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 it something accidentally. So of course, the main part here is the 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 write, chow.eu, 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 display. It will be faster in this case. Okay. 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 slide. And to check it, I cannot do it with a browser because it's a telnet socket. It's just a raw socket here. Okay. So we have here chow.eu. Python. 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? Yes. Thank you, guys. Thank you. Great. Okay. So then again the demo. You see chow.eu. 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. We'll do our best. Good. Okay. We're back. Then of course just the 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 hello. 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 the HTTP stuff. Then we define the render get method. You see it here. Render get. And it's as easy as return chow.eu. Python. And code 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 this example since 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 best 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 jungle multiple tasks in one application without using threads. That was a goal of asynchronous development. And the code tends to be a bit complicated, so I would 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 like if you would write an 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 of a framework that used it for asynchronous development of web applications. I will show you examples later. 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 a weight which, like 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 this little thing, they matter. So they help you to write asynchronous code in the similar ways that you write the synchronous 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 with 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 a JavaScript developer or just some JavaScript? Okay. So I don't have to tell you. It's difficult to write nice codes. 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 Mongo.js. 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 callbackhell.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. Async.io, the buzzword. I think I don't have to stop really long time over this one, because you have awesome talks as well on 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 callbackhell in Async.io 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 it 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, okay, this future, it doesn't have a result yet. It has a reference to the result. And it will be ready at some later points. So this future object can be returned to the color right away. And that color 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 will just show you how it works. So you need a library that works with futures, like returns futures. And for example, some async HTTP library, you do fetch, 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 IOLoop, 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 a future is something that can hold 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 are already familiar with this. 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 the 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 actually has several implementations available. 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 a sync event loop is doing. So here I already used the 3.5 syntax as sync 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. They sync def, fancy. So this is Anton's talk and what coroutine does is 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 how easy you can switch between two things to your IO loop and the sync IO. So first we run Anton's workshop or Anton's talk. We print welcome. We await. Await is the same as yield 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, yeah, because I planned it as a workshop at first, but then I packed it in the talk. So yeah, this will not work. But this is a pseudo code 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 pseudo code. This is a way that you would write usually a sequential code in any framework, any web frameworks 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, this is done, call this function. This is the callback way of doing the same thing. And this is a ternator way of doing the same task. You define in a coroutine a sink depth. 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 IE loop, say when this future is ready, please give us the 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. A UCTP server. Every UCTP is a synchronous UCTP framework or library available for async.io and its author, Andre, 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 depth, 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 root. You say that whenever it goes to this root, you execute the coroutine hello and then you run it. So this is a simplest example. You're not waiting for the database here, of course, that's why it looks so easy, but that's the minimal thing you need to run the IE HTTP server. Of course, we want to get more tricky. We want to have async in the wait 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 like 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're 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 coroutine. You are awaiting for it, meaning giving it back to the IE loop. Whenever the result is ready, we are saving it to response. Now the surprise, why we are waiting 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 IE 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 isinker.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 isink.io. Tornado was there since 2.5. So even when you didn't have the fancy syntax of isink.wait and isink.def, you already could use tornado and write asynchronous code with it in Python using its generators. And I will show you how right now. So tornado runs on the same idea of IOLOOP futures and coroutines. It just uses a 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 a good option for this. How does it play together with isink.io? Well, this is the idea of the stack that would work. On top, we have application level like tornado, twist it or whatever. Then we have the IOL framework, isink.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 E-POL or select on Windows. It doesn't work like that right now. So tornado and twist it, 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 isink.io. You don't have to remember the syntax. I'm just showing you that it's as easy as IOL loop.current. It's a single tone. You get the IOL loop and you do IOL loop that starts to start it. There is no big magic behind it. And then if you want to run the isink.io event loop, it's also possible. There are adapters available in tornado to use futures as well as IOL loop from isink.io. And this is how you do it. You just import isink.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 tornado from isink.io as well. And this is really handy because some libraries will give you futures of isink.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 isink.io back and forth easily. Finally, minimal tornado web app. It's very similar to the isink.io example. You have a handle 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 isink.io. Tornado has own isink.itb client. Here it is. That is doing the fetching for you. So first you create an instance of the isink.itb 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 IOLOOP. 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. So if you, I did it on purpose here, I didn't include multiple URLs to show you how easy 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 URL 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. Yes, you can. Good. Now I'm running out of time. So, I'm going to run 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 a syncio 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 on 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 a syncio. I think that's it. You're great. Thanks for not sleeping on this talk. And now let's go to questions. First one. Okay. 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 bounds? 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 the IO. That's why it's a syncio. 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. That's a typical way. So you can also do it with a syncio. There is a thread pool executor and process pool executor classes in a syncio 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 a syncio to wait for the IO events and for your fancy image recognition software. You could start threads but still use a syncio syntax. So 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, like, let's say some difficult machine learning computation, 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. 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 question. So yes, I did compare the performance. Of course, performance is such a term that there are very numerous ways to measure the performance and you could measure the memory, you could measure the speed, many things. So what I did measure is requests per second. I measured tornado, syncio, 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, yeah. I think we need to head on out. Yes. So, good. Thank you all for coming. Thank you very much. It was great.
 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 async.io. 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 life 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're all like coming from different  backgrounds. Some of us already have experience with async tools and generally network development,  and 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 the 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 on Euro Python and also like 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 logo on 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. All right. All right.  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 a syn 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 I think I owe a bit more details. 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 a sinkish, so I hope that we have time for that too. All right. So what's a  sink? A quick recap. What is it in general, this buzzword? So what we see on this picture  is a task. Yeah. We have, you can see my laser pointer? Yes. 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 we  will, our requests never wait for responses. They will just come and hope that we can have  them 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 we just wait. 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 like 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, we 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. It's 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 web page. 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 a 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 like  in progress, those who are almost done, if Apache crashes, that's it. Nobody gets anything.  That's a problem of the threaded server. So there was a smart idea. Why don't we have  a pool? Like we can say our threaded server is capable of, I don't know, 16 threads, let's  say. 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 thread at 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 normal threaded server, but yeah, you can do more  with your server. And now comes the synchronous web server. From the same theory that I already  shown to you, we have just one thread, and we are using this thread to like switch between  different tasks in different points of time. So whenever we have free slots, free resources,  we are taking another task. When there is, when the response or when some, let's say  the network socket has data of our previous request, we're 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 give. So this is the thread, this is a synchronous  web server in my vision. Good. Of course, it was just blah, blah. So let's have real  charts. Here you have Apache light, HEPD and NGINX. Apache being threaded server, NGINX  being a synchronous web server. And this 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 picture 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 light HEPD are quite sparing with your operating  system resources. Good. So now let's get more into practice. So async Python is not async  at all. There have been tools around for years. Let's say that Tornado is eight years old  and then there is Twisted, which is quite a while, let's 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 much easier to write. You don't want to have a callback hell like you  have usually not 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, at least comparably easy. Yes. And the  goal is to make it as easy as Django or Ruby on Rails. Then I would 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 message in SSH clients, DNS clients and servers, anything. So basically  any network task you have, most likely you will have it already solved with Twisted.  So 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, Twisted doesn't call, 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 always, I think, so 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 pulls for IO  and 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. So 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 we have here. It's a really, really simplistic minimal case. How do you  write into socket and Twisted? So first, you have two factories, of course. You have a  protocol factory here first and a serial 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 it something accidentally. So of course, the main part here is the 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 write, chow.eu, 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 display. It will be faster in this case. Okay. 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 slide. And to  check it, I cannot do it with a browser because it's a telnet socket. It's just a raw socket  here. Okay. So we have here chow.eu. Python. 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? Yes. Thank you, guys. Thank you. Great. Okay.  So then again the demo. You see chow.eu. 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. We'll do our best. Good. Okay. We're back.  Then of course just the 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 hello. 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 the HTTP  stuff. Then we define the render get method. You see it here. Render get. And it's as easy  as return chow.eu. Python. And code 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  this example since 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 best 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 jungle multiple tasks in one application without using threads.  That was a goal of asynchronous development. And the code tends to be a bit complicated,  so I would 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 like if you would write  an 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 of a framework that used it for asynchronous  development of web applications. I will show you examples later.  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 a weight which, like 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 this little thing, they matter. So they help you to write asynchronous code  in the similar ways that you write the synchronous 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  with 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 a JavaScript  developer or just some JavaScript? Okay. So I don't have to tell you. It's difficult to  write nice codes. 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 Mongo.js. 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 callbackhell.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. Async.io, the buzzword. I think I don't have to stop really  long time over this one, because you have awesome talks as well on 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 callbackhell in Async.io 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 it 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, okay, this future, it doesn't have a result yet. It has a reference to the result.  And it will be ready at some later points. So this future object can be returned to the  color right away. And that color 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 will just show you how it works. So you need a  library that works with futures, like returns futures. And for example, some async HTTP  library, you do fetch, 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 IOLoop, 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 a future is something that can hold 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 are already familiar with this. 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 the 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 actually has several implementations available.  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 a sync event loop is doing. So here I already used the 3.5 syntax  as sync 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. They sync def,  fancy. So this is Anton's talk and what coroutine does is 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 how easy you can switch  between two things to your IO loop and the sync IO. So first we run Anton's workshop or  Anton's talk. We print welcome. We await. Await is the same as yield 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, yeah, because I planned it as a workshop  at first, but then I packed it in the talk. So yeah, this will not work. But this is a  pseudo code 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 pseudo code. This is a way  that you would write usually a sequential code in any framework, any web frameworks 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,  this is done, call this function. This is the callback way of doing the same thing. And  this is a ternator way of doing the same task. You define in a coroutine a sink depth. 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 IE loop, say when this future is ready,  please give us the 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. A UCTP server. Every UCTP is a synchronous UCTP framework or library  available for async.io and its author, Andre, 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 depth, 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 root. You say that whenever it goes to this root, you execute the coroutine  hello and then you run it. So this is a simplest example. You're not waiting for the database  here, of course, that's why it looks so easy, but that's the minimal thing you need to run  the IE HTTP server. Of course, we want to get more tricky. We want to have async in  the wait 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 like 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're 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 coroutine. You are awaiting for it, meaning giving it back  to the IE loop. Whenever the result is ready, we are saving it to response. Now the surprise,  why we are waiting 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 IE 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 isinker.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 isink.io. Tornado was there since 2.5. So even when you didn't  have the fancy syntax of isink.wait and isink.def, you already could use tornado and write asynchronous  code with it in Python using its generators. And I will show you how right now. So tornado  runs on the same idea of IOLOOP futures and coroutines. It just uses a 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 a good option for this. How does it play together  with isink.io? Well, this is the idea of the stack that would work. On top, we have application  level like tornado, twist it or whatever. Then we have the IOL framework, isink.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 E-POL or select on Windows. It doesn't work like  that right now. So tornado and twist it, 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 isink.io. You don't have to remember the syntax. I'm just showing you that it's  as easy as IOL loop.current. It's a single tone. You get the IOL loop and you do IOL  loop that starts to start it. There is no big magic behind it. And then if you want to  run the isink.io event loop, it's also possible. There are adapters available in tornado to  use futures as well as IOL loop from isink.io. And this is how you do it. You just import  isink.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 tornado from isink.io as  well. And this is really handy because some libraries will give you futures of isink.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 isink.io back and forth easily. Finally, minimal tornado web app.  It's very similar to the isink.io example. You have a handle 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 isink.io.  Tornado has own isink.itb client. Here it is. That is doing the fetching for you. So  first you create an instance of the isink.itb 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 IOLOOP. 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. So  if you, I did it on purpose here, I didn't include multiple URLs to show you how easy  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 URL 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. Yes, you can. Good. Now I'm running out of time. So, I'm going to  run 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 a syncio 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 on 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 a syncio. I think that's it. You're great. Thanks for not sleeping on  this talk. And now let's go to questions. First one.  Okay. 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 bounds? 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 the IO. That's why it's a  syncio. 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. That's a typical way. So  you can also do it with a syncio. There is a thread pool executor and process pool executor  classes in a syncio 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 a syncio to wait for the IO events and for your fancy image recognition  software. You could start threads but still use a syncio syntax. So 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, like, let's say some difficult machine learning computation,  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.  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 question. So yes, I did compare the performance.  Of course, performance is such a term that there are very numerous ways to measure the  performance and you could measure the memory, you could measure the speed, many things.  So what I did measure is requests per second. I measured tornado, syncio, 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, yeah. I think we need to head on  out. Yes. So, good. Thank you all for coming. Thank you very much. It was great.
Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_0_347.png Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_348_779.png Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_780_2267.png Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_2268_2495.png Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_2496_3611.png Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_3612_4619.png Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_4620_11183.png Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_11184_12263.png Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_12264_14171.png Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_14172_15203.png Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_15204_16019.png Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_16020_16211.png Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_16212_16307.png Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_16308_17651.png Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_17652_18119.png Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_18120_20459.png Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_20460_23303.png Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_23304_23723.png Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_23724_25751.png Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_25752_26627.png Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_26628_26855.png Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_26856_27011.png Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_27012_27119.png Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_27120_28667.png Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_28668_33683.png Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_33684_40115.png Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_40116_41891.png Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_41892_44831.png Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_44832_45491.png Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_45492_48539.png Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_48540_52643.png Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_52644_53423.png Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_53424_56975.png Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_56976_58295.png Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_58296_59531.png Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_59532_60011.png Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_60012_60071.png Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_60072_60527.png Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_60528_61547.png Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_61548_62747.png Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_62748_62963.png Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_62964_63011.png Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_63012_63071.png Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_63072_63119.png Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_63120_63455.png Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_63456_63503.png Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_63504_64703.png Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_64704_65819.png Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_65820_65867.png Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_65868_65927.png Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_65928_65999.png Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_66000_66071.png Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_66072_66155.png Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_66156_66551.png Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_66552_66623.png Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_66624_66671.png Anton_Caceres_-_Using_the_right_Async_tool__present_day_ZXrdqq7nc1I_66672_67664.png
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 async.io. 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 life 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're all like coming from different backgrounds. Some of us already have experience with async tools and generally network development, and 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 the 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 on Euro Python and also like 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 logo on 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. All right. All right. 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 a syn 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 I think I owe a bit more details. 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 a sinkish, so I hope that we have time for that too. All right. So what's a sink? A quick recap. What is it in general, this buzzword? So what we see on this picture is a task. Yeah. We have, you can see my laser pointer? Yes. 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 we will, our requests never wait for responses. They will just come and hope that we can have them 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 we just wait. 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 like 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, we 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. It's 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 web page. 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 a 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 like in progress, those who are almost done, if Apache crashes, that's it. Nobody gets anything. That's a problem of the threaded server. So there was a smart idea. Why don't we have a pool? Like we can say our threaded server is capable of, I don't know, 16 threads, let's say. 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 thread at 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 normal threaded server, but yeah, you can do more with your server. And now comes the synchronous web server. From the same theory that I already shown to you, we have just one thread, and we are using this thread to like switch between different tasks in different points of time. So whenever we have free slots, free resources, we are taking another task. When there is, when the response or when some, let's say the network socket has data of our previous request, we're 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 give. So this is the thread, this is a synchronous web server in my vision. Good. Of course, it was just blah, blah. So let's have real charts. Here you have Apache light, HEPD and NGINX. Apache being threaded server, NGINX being a synchronous web server. And this 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 picture 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 light HEPD are quite sparing with your operating system resources. Good. So now let's get more into practice. So async Python is not async at all. There have been tools around for years. Let's say that Tornado is eight years old and then there is Twisted, which is quite a while, let's 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 much easier to write. You don't want to have a callback hell like you have usually not 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, at least comparably easy. Yes. And the goal is to make it as easy as Django or Ruby on Rails. Then I would 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 message in SSH clients, DNS clients and servers, anything. So basically any network task you have, most likely you will have it already solved with Twisted. So 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, Twisted doesn't call, 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 always, I think, so 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 pulls for IO and 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. So 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 we have here. It's a really, really simplistic minimal case. How do you write into socket and Twisted? So first, you have two factories, of course. You have a protocol factory here first and a serial 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 it something accidentally. So of course, the main part here is the 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 write, chow.eu, 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 display. It will be faster in this case. Okay. 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 slide. And to check it, I cannot do it with a browser because it's a telnet socket. It's just a raw socket here. Okay. So we have here chow.eu. Python. 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? Yes. Thank you, guys. Thank you. Great. Okay. So then again the demo. You see chow.eu. 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. We'll do our best. Good. Okay. We're back. Then of course just the 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 hello. 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 the HTTP stuff. Then we define the render get method. You see it here. Render get. And it's as easy as return chow.eu. Python. And code 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 this example since 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 best 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 jungle multiple tasks in one application without using threads. That was a goal of asynchronous development. And the code tends to be a bit complicated, so I would 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 like if you would write an 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 of a framework that used it for asynchronous development of web applications. I will show you examples later. 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 a weight which, like 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 this little thing, they matter. So they help you to write asynchronous code in the similar ways that you write the synchronous 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 with 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 a JavaScript developer or just some JavaScript? Okay. So I don't have to tell you. It's difficult to write nice codes. 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 Mongo.js. 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 callbackhell.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. Async.io, the buzzword. I think I don't have to stop really long time over this one, because you have awesome talks as well on 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 callbackhell in Async.io 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 it 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, okay, this future, it doesn't have a result yet. It has a reference to the result. And it will be ready at some later points. So this future object can be returned to the color right away. And that color 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 will just show you how it works. So you need a library that works with futures, like returns futures. And for example, some async HTTP library, you do fetch, 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 IOLoop, 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 a future is something that can hold 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 are already familiar with this. 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 the 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 actually has several implementations available. 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 a sync event loop is doing. So here I already used the 3.5 syntax as sync 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. They sync def, fancy. So this is Anton's talk and what coroutine does is 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 how easy you can switch between two things to your IO loop and the sync IO. So first we run Anton's workshop or Anton's talk. We print welcome. We await. Await is the same as yield 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, yeah, because I planned it as a workshop at first, but then I packed it in the talk. So yeah, this will not work. But this is a pseudo code 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 pseudo code. This is a way that you would write usually a sequential code in any framework, any web frameworks 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, this is done, call this function. This is the callback way of doing the same thing. And this is a ternator way of doing the same task. You define in a coroutine a sink depth. 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 IE loop, say when this future is ready, please give us the 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. A UCTP server. Every UCTP is a synchronous UCTP framework or library available for async.io and its author, Andre, 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 depth, 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 root. You say that whenever it goes to this root, you execute the coroutine hello and then you run it. So this is a simplest example. You're not waiting for the database here, of course, that's why it looks so easy, but that's the minimal thing you need to run the IE HTTP server. Of course, we want to get more tricky. We want to have async in the wait 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 like 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're 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 coroutine. You are awaiting for it, meaning giving it back to the IE loop. Whenever the result is ready, we are saving it to response. Now the surprise, why we are waiting 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 IE 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 isinker.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 isink.io. Tornado was there since 2.5. So even when you didn't have the fancy syntax of isink.wait and isink.def, you already could use tornado and write asynchronous code with it in Python using its generators. And I will show you how right now. So tornado runs on the same idea of IOLOOP futures and coroutines. It just uses a 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 a good option for this. How does it play together with isink.io? Well, this is the idea of the stack that would work. On top, we have application level like tornado, twist it or whatever. Then we have the IOL framework, isink.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 E-POL or select on Windows. It doesn't work like that right now. So tornado and twist it, 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 isink.io. You don't have to remember the syntax. I'm just showing you that it's as easy as IOL loop.current. It's a single tone. You get the IOL loop and you do IOL loop that starts to start it. There is no big magic behind it. And then if you want to run the isink.io event loop, it's also possible. There are adapters available in tornado to use futures as well as IOL loop from isink.io. And this is how you do it. You just import isink.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 tornado from isink.io as well. And this is really handy because some libraries will give you futures of isink.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 isink.io back and forth easily. Finally, minimal tornado web app. It's very similar to the isink.io example. You have a handle 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 isink.io. Tornado has own isink.itb client. Here it is. That is doing the fetching for you. So first you create an instance of the isink.itb 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 IOLOOP. 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. So if you, I did it on purpose here, I didn't include multiple URLs to show you how easy 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 URL 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. Yes, you can. Good. Now I'm running out of time. So, I'm going to run 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 a syncio 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 on 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 a syncio. I think that's it. You're great. Thanks for not sleeping on this talk. And now let's go to questions. First one. Okay. 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 bounds? 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 the IO. That's why it's a syncio. 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. That's a typical way. So you can also do it with a syncio. There is a thread pool executor and process pool executor classes in a syncio 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 a syncio to wait for the IO events and for your fancy image recognition software. You could start threads but still use a syncio syntax. So 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, like, let's say some difficult machine learning computation, 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. 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 question. So yes, I did compare the performance. Of course, performance is such a term that there are very numerous ways to measure the performance and you could measure the memory, you could measure the speed, many things. So what I did measure is requests per second. I measured tornado, syncio, 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, yeah. I think we need to head on out. Yes. So, good. Thank you all for coming. Thank you very much. It was great.
different thing. Yeah, we will talk about async, on Euro Python and also like having this. I know We will go then into Twisted Tornado and I think overview with a threaded web server. And this is requests concurrently is that we just spin a new different tools in Python that allow you to write is a framework. It's huge. It includes powerful code in the similar ways that you write the asynchronous code in Python since 2.5, because For that, we need to learn a bit of a theory. Who where it stops. This is an example. A coroutine right away that, hey, this is not just a function will switch between tasks for you. Could you any framework, any web frameworks that you use. in Django. Then how would you do it in JavaScript not exactly the same as Django, but I would say it's still decent and easy and you're not using