Rust is a system programming language focused on safety, speed, and concurrency. It's standard library provides API for dealing with I/O, but for now in a synchronous way. In this talk we'll dive into the ecosystem of asynchronous libraries published so far on crates.io and how to use them in order to build robust, scalable, and production ready network clients and servers.
4. This Talk
• Async I/O for network
• Current status in standard library
• Crates available for doing async I/O
5. The std::{io,net} way
use std::io::prelude::*;
use std::net::TcpStream;
fn main() {
let mut stream = TcpStream::connect(“127.0.0.1:2424").unwrap();
let _ = stream.read_exact(&mut [0; 128]);
}
6. Simple Server
use std::io::Write;
use std::net::TcpListener;
use std::thread;
fn main() {
let listener = TcpListener::bind(“127.0.0.1:9123").unwrap();
println!("listening started, ready to accept”);
for stream in listener.incoming() {
thread::spawn(|| {
let mut stream = stream.unwrap();
stream.write(b"Hello Worldrn").unwrap();
});
}
}
7. Simple Web Server
(Hyper 0.10.x)
extern crate hyper;
use hyper::server::{Server, Request, Response};
fn main() {
let server = Server::http(“127.0.0.1:8080").unwrap();
let _guard = server.handle(|mut req: Request, mut res: Response| {
res.send(b"Hello Rust");
});
println!("Listening on http://127.0.0.1:8080");
}
18. Metal I/O (mio)
• I/O Abstraction over OS
• Zero Cost Abstraction
• Readiness Model
• Event Loop
• Handler with tokens
https://github.com/carllerche/mio
19. Metal I/O (mio)
• Very Low level
• Manually tracking I/O events
• Wire up a state machine
Solid foundation of async in Rust
32. Combining Example
fn get_person(id : i32) -> Box<Future<Item=Person,Error=()>>
fn hello_person(person : Person) -> Box<Future<Item=String,Error=()>>
get_person(1)
.and_then(hello_person)
.map(|s|{
let result: Result<String, ()> =
Ok(format!("{} from Codemotion",s));
result
});
33. Combining Example
let pool = CpuPool::new_num_cpus();
let combined =
get_person(1).and_then(hello_person).map(|s|{
let result: Result<String, ()> =
Ok(format!("{} from Codemotion",s));
result
});
let future = pool.spawn(combined);
println!("{:?}",future.wait().unwrap());
34. Streams
pub trait Stream {
type Item;
type Error;
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error>;
}
35. Stream Example
let (mut tx, rx) = mpsc::channel(10);
thread::spawn(move ||{
let mut sender = tx;
for i in 0..10 {
sender = sender.send(i).wait().unwrap();
}
});
rx.for_each(|val|{
println!("Got {:?}",val);
Ok(())
}).wait();
36. Zero Cost
• No Allocation in composition
• One dynamic dispatch
• No Synchronisation
https://aturon.github.io/blog/2016/09/07/futures-design
40. What is?
Tokio is a platform for writing fast networking code in Rust.
41. Tokio (Core)
• Built on top of mio and futures
• Lower Level
• Tcp Handlers
• Timeout
• Event Loop
https://medium.com/@carllerche/announcing-tokio-df6bb4ddb34#.g9ugbqg71
44. Echo Server (Core)
let mut core = Core::new().unwrap();
let handle = core.handle();
let address = "127.0.0.1:8080".parse().unwrap();
let acceptor = TcpListener::bind(&address,&handle).unwrap();
let server = acceptor.incoming().for_each(|(socket,address)|{
let (reader, writer) = socket.split();
let future = copy(reader,writer).then(|result|{
println!("{:?}", result);
Ok(())
});
handle.spawn(future);
Ok(())
});
core.run(server).unwrap();
51. Proto Impl
struct ExampleProto;
impl<T: Io + 'static> ServerProto<T> for ExampleProto {
type Request = String;
type Response = String;
type Transport = Framed<T, ExampleCodec>;
type BindTransport = Result<Self::Transport, io::Error>;
fn bind_transport(&self, io: T) -> Self::BindTransport {
Ok(io.framed(ExampleCodec))
}
}
52. Service Impl
pub struct ExampleService;
impl Service for ExampleService {
type Request = String;
type Response = String;
type Error = io::Error;
type Future = BoxFuture<String, io::Error>;
fn call(&self, req: String) -> Self::Future {
let res = format!("{} from Codemotion",req)
future::finished(res).boxed()
}
}
54. Hyper Service
#[derive(Clone, Copy)]
struct Hello;
impl Service for Hello {
type Request = Request;
type Response = Response;
type Error = hyper::Error;
type Future = FutureResult<Response, hyper::Error>;
fn call(&self, _req: Request) -> Self::Future {
let response = "Hello Rust!";
futures::future::ok(
Response::new()
.with_header(ContentLength(response.len() as u64))
.with_header(ContentType::plaintext())
.with_body(response)
)
}
}
55. Hyper Server
fn main() {
let addr = “127.0.0.1:8080".parse().unwrap();
let server = Http::new().bind(&addr,|| Ok(Hello)).unwrap();
println!("Listening on port 8080”);
server.run().unwrap();
}
56. Tokio Based Projects
• Tokio Postgres
• Tokio Redis
• Tokio Memcache
• Hyper
• Tokio Curl
• Telebot
• Tokio IRC
• ….more