Coding a simple microservices with Rust

Introduction

Initial setup with hyper crate

[dependencies]
hyper = "0.12"
futures = "0.1"
let addr = ([0, 0, 0, 0], 8080).into();
let builder = Server::bind(&addr);

Guard the data among multiple threads

type UserId = u64;struct UserData;impl fmt::Display for UserData {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("{}")
}
}
type UserDb = Arc<Mutex<Slab<UserData>>>;
[dependencies]
hyper = "0.12"
futures = "0.1"
slab = "0.4.2"

The request handler for CRUD functions on the shared data

fn user_handler(req: Request<Body>, user_db: &UserDb)    -> impl Future<Item=Response<Body>, Error=Error>
[dependencies]
hyper = "0.12"
futures = "0.1"
slab = "0.4.2"
lazy_static = "1.4.0"
regex = "1.4.2"
lazy_static! {    static ref USER_PATH: Regex = 
Regex::new("^/user/((?P<user_id>\\d+?)/?)?$").unwrap();
static ref USERS_PATH: Regex =
Regex::new("^/users/?$").unwrap();
}
fn user_handler(req: Request<Body>, user_db: &UserDb) -> impl           Future<Item=Response<Body>, Error=Error> {    let response = ...;    ...    future::ok(response)
}
let response = {    let method = req.method();    let path = req.uri().path();    let mut users = user_db.lock().unwrap();    ...

Rust’s pattern matching for different request paths and methods

...let mut users = user_db.lock().unwrap();if USERS_PATH.is_match(path) {    if method == &Method::GET {        let list = users.iter()            .map(|(id, _)| id.to_string())            .collect::<Vec<String>>()            .join(",");        Response::new(list.into())    } else {
response_status(StatusCode::METHOD_NOT_ALLOWED)
}
} else if let Some(cap) = USER_PATH.captures(path) {...
...
} else if let Some(cap) = USER_PATH.captures(path) {
let user_id = cap.name("user_id").and_then(|m| { m.as_str() .parse::<UserId>() .ok() .map(|x| x as usize) }); match (method, user_id) { (&Method::GET, Some(id)) => { if let Some(data) = users.get(id) { Response::new(data.to_string().into()) } else { response_status(StatusCode::NOT_FOUND) }
},
(&Method::POST, None) => { let id = users.insert(UserData); Response::new(id.to_string().into()) }, (&Method::POST, Some(_)) => { response_status(StatusCode::BAD_REQUEST) }, (&Method::PUT, Some(id)) => { if let Some(user) = users.get_mut(id) { *user = UserData; response_status(StatusCode::OK) } else { response_status(StatusCode::NOT_FOUND) } }, (&Method::DELETE, Some(id)) => { if users.contains(id) { users.remove(id); response_status(StatusCode::OK) } else { response_status(StatusCode::NOT_FOUND) } }, _ => { response_status(StatusCode::METHOD_NOT_ALLOWED) }, }} else { response_status(StatusCode::NOT_FOUND)}...

Server configuration and runtime

let user_db = Arc::new(Mutex::new(Slab::new()));let server = builder.serve(move || {let user_db = user_db.clone();service_fn(move |req| user_handler(req, &user_db))});
let server = server.map_err(drop);hyper::rt::run(server);

Containerization the microservices

cd nightly
docker build -t rust:nightly .
FROM rust:nightlyRUN USER=root cargo new --bin rust-microservicesWORKDIR /rust-microservicesCOPY ./Cargo.toml ./Cargo.tomlRUN cargo buildRUN rm src/*.rsCOPY ./src ./srcRUN rm ./target/debug/deps/rust_microservice*RUN cargo buildCMD ["./target/debug/rust-microservice"]EXPOSE 8080
cd ..
docker-compose build
docker-compose up -d

Testing

curl -X POST http://localhost:8080/user/
curl -X POST http://localhost:8080/user/
curl -X POST http://localhost:8080/user/
curl -X POST http://localhost:8080/user/
curl -X DELETE http://localhost:8080/user/2
curl http://localhost:8080/users/

Summary

Solutions Architect, AWS CSAA/CDA: microservices, kubernetes, algorithms, Java, Rust, Golang, React, JavaScript…

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store