Distributed Systems &
Rust
Dan Burkert
irc: dcb
GitHub: danburkert
Distributed Systems
-
IO & Networking
- protocols
- abstractions & threading models
-
Operations
- monitoring
- tracing
- debugging
- Security & Encryption
- Performance
- ...many more...
std::net
mio
networking in Rust
// accept TCP connections and process them,
// spawning a new thread for each one
for stream in listener.incoming() {
match stream {
Ok(stream) => {
thread::spawn(move|| {
// connection succeeded
handle_client(stream)
});
},
Err(e) => handle_failure(e),
}
}
fn ready(&mut self,
event_loop: &mut EventLoop<Echo>,
token: Token,
events: EventSet) {
if events.is_readable() {
match token {
SERVER => self.server.accept(event_loop),
CLIENT => self.client.readable(event_loop),
i => self.server.conn_readable(event_loop, i),
}
}
if events.is_writable() {
match token {
SERVER => panic!("received writable for token 0"),
CLIENT => self.client.writable(event_loop),
_ => self.server.conn_writable(event_loop, token),
};
}
}
non-blocking sockets
invert
program flow
let's build something
a simple key value store
a simple key value store
$ telnet 127.0.0.1 5556
...
> PUT my-key some-value
OK
> GET my-key
some-value
> GET other-key
NONE
> bogus command
ERR
github.com/danburkert/simple-kv
/// A simple key value database server.
///
/// The server listens for text-based TCP
/// messages in the following formats:
///
/// * 'PUT <key> <value>'
/// * 'GET <key>'
pub struct Server {
listener: TcpListener,
connections: Slab<Connection>,
db: HashMap<String, String>,
}
a simple key value store
impl Handler for Server {
fn ready(&mut self, event_loop: &mut EventLoop<Server>, token: Token, events: EventSet) {
if token == LISTENER {
self.accept_connections(event_loop);
} else {
if events.is_readable() {
if let Err(error) = self.connection_readable(token) {
self.connections.remove(token);
return;
}
}
if events.is_writable() {
if let Err(error) = self.connections[token].writable() {
self.connections.remove(token);
return
}
}
let events = self.connections[token].events;
if let Err(error) = event_loop.reregister(&mut self.connections[token].socket,
token,
events, PollOpt::edge() | PollOpt::oneshot()) {
self.connections.remove(token);
}
}
}
}
Handling the Event Loop
struct Connection {
socket: TcpStream,
/// Holds bytes read from the socket,
/// but not yet deserialized.
read_buf: Vec<u8>,
/// Holds a stream of bytes to be
/// sent to the socket on the next
/// writable event.
write_buf: Vec<u8>,
/// The set of events which the
/// connection is interested in handling.
events: EventSet,
}
Buffer Management
impl Connection {
/// Called when there are bytes available
/// to read on the connection's socket.
///
/// Reads all available bytes, and then scans
/// the bytes for newline characters. For each
/// newline character, the corresponding line
/// is deserialized into a message.
///
/// Returns all of the messages read.
fn readable(&mut self) -> Result<Vec<Message>> {
let mut messages = Vec::new();
let read_buf = &mut self.read_buf;
let read_from = read_buf.len();
match self.socket.read_to_end(read_buf) {
Ok(0) => return Ok(messages),
Ok(_) => (),
Err(ref error) if error.kind() == ErrorKind::WouldBlock => (),
Err(error) => return Err(error),
}
let mut lo = 0;
// Check the newly read bytes for line seperators. For each line, decode it
// into a message and add it to the messages list.
for (hi, &c) in read_buf[read_from..].iter().enumerate() {
if c == '\n' as u8 {
let line = &read_buf[lo..hi];
messages.push(Message::from_bytes(line));
lo = hi + 1;
}
}
// Remove bytes that have been decoded into lines.
read_buf.drain(..lo).count();
Ok(messages)
}
}
Protocols & Serialization
#[derive(Debug)]
enum Message {
/// A get message, including the key to look up.
Get(String),
/// A put message, including the key and value.
Put(String, String),
/// Unable to decode the message
Error,
}
impl Message {
fn from_bytes(bytes: &[u8]) -> Message {
let line = match str::from_utf8(bytes) {
Ok(chars) => chars,
Err(..) => return Message::Error,
};
let words: Vec<&str> = line.split_whitespace().collect();
let len = words.len();
if len < 1 { return Message::Error }
match words[0] {
"GET" => {
if len != 2 { Message::Error }
else { Message::Get(words[1].to_owned()) }
},
"PUT" => {
if len != 3 { Message::Error }
else { Message::Put(words[1].to_owned(), words[2].to_owned()) }
},
_ => { words[0]); Message::Error},
}
}
}
Messages
why non-blocking
- scalability
- (lack of) synchronization
-
abstractions
- futures & streams
- coroutines
Distributed Systems
-
IO & Networking
- protocols
- abstractions & threading models
-
Operations
- monitoring
- tracing
- debugging
- Security & Encryption
- Performance
- ...many more...
Operations
- metrics
- reporting
- tracing
- debugging
Special Thanks To
rust-lang/rust
carllerche/mio
hoverbear/raft
simple-kv
By danburkert
simple-kv
- 656