Tokio
This chapter gives an overview of the Tokio stack. I'll assume that you understand what futures are and how they work. If not, I recommend reading this excellent blog post.
Disclaimer: Async IO and event loops are complex topics that I don't pretend to understand. I just know some basic concepts that I'll try to explain in my own words. Here is the thing: you don't need to understand these topics to use Tokio, and that what makes it awesome.
tokio-core
Let's start with the main piece of Tokio:
tokio-core. tokio-core
provides two things:
-
a
tokio_core::net
module that provides TCP/UDP utilities. These utilities are intended to be similar to thestd::net
ones, but they are designed to be asynchronous: they are not blocking. For instance,std::net::TcpStream::connect
blocks until the connection is established (or fails to be established) and the outcome is returned, whereastokio_core::net::TcpStream::connect
immediately returns a future that can be polled until it finishes. -
a
Core
(aka "reactor" or "event loop") which runs futures. We can already run futures with threads (viastd::thread::spawn
or pools of threads (viafutures_cpupool
), so why use an event loop instead?- Threads are expensive when there are many of them, due to context switches. You don't want to spawn thousands of threads, especially for IO extensive work, since most of them are going to spend most of their time waiting anyway.
- Efficiently managing multiple threads is hard. Tokio's event loop handles this for us. I don't need to know how many threads there are, or which ones should be parked or unparked.
tokio-io, tokio-proto, and tokio-service
tokio-core
is quite minimalistic, but tokio also provides a few crates that
make it easy to implement some common services, such as client and servers for
request/response based protocols.
tokio-io
contains traits and types to work asynchronously with streams of bytes.tokio-proto
implements some logic that is common to many protocols request/response based protocols.tokio-service
provides aService
trait implements how a request is handled.
Here is an illustration of how these crates are used together to implement a server:
The server receives a stream of bytes from a socket. A Codec
reads this
stream and decode meaningful messages. These messages are then passed to the
Protocol
, which forwards the requests to the Service
. The Protocol
is
kind of a black box, but we can imagine that for multiplexed protocols (ie
protocol for which requests have an ID), it keeps track of the IDs and makes
sure responses are sent with the ID of the request they correspond to. The
Service
handles the request and returns a response, which is in turn handled
by the Protocol
, which passes it to the Codec
that sends it.
The stack is quite similar for a client: