Successfully reported this slideshow.
Your SlideShare is downloading. ×

Microservices mit Rust

Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Upcoming SlideShare
Embedded Rust
Embedded Rust
Loading in …3
×

Check these out next

1 of 35 Ad
Advertisement

More Related Content

Recently uploaded (20)

Advertisement

Microservices mit Rust

  1. 1. Microservices mit Rust Jens Siebert (@jens_siebert) betterCode(Rust), 13. Oktober 2021
  2. 2. Über mich • Senior Software Developer bei doks.innovation in Kassel • Drohnen-Steuerung, Computer Vision, Architektur • Maker, 3D-Drucker, Nerd
  3. 3. Quo vadis, Backend-Entwicklung? Interpretierte Sprachen: • Java/JVM-basierte Sprachen: Spring, Micronaut, MicroProfile • Javascript/Typescript: Node.js, Deno • Python: Django, Flask Kompilierte Sprachen: • Go: Kite, go-kit, go-micro • Rust: actix-web, rocket, tide, warp
  4. 4. Quo vadis, Backend-Entwicklung? Interpretierte Sprachen: • Einfach zu lernen • Sicherheit durch automatische Speicherverwaltung • Garbage Collection • Langsamer Start-up/Just-in-Time Kompilierung Go: • Einfach zu lernen • Sicherheit durch automatische Speicherverwaltung • Garbage Collection • Schneller Start-up/Ahead-of-Time Kompilierung
  5. 5. Und Rust? Rust: • Nicht ganz so einfach zu lernen • Sicherheit durch automatische Speicherverwaltung • Garbage Collection • Schneller Start-up/Ahead-of-Time Kompilierung
  6. 6. Rust vs. Go? Rust AND Go! For most companies and users, Go is the right default option. Its performance is strong, Go is easy to adopt, and Go’s highly modular nature makes it particularly good for situations where requirements are changing or evolving. As your product matures, and requirements stabilize, there may be opportunities to have large wins from marginal increases in performance. In these cases, using Rust to maximize performance may well be worth the initial investment. https://thenewstack.io/rust-vs-go-why-theyre-better-together
  7. 7. Discord „Read States“ Service https://discord.com/blog/why-discord-is-switching-from-go-to-rust
  8. 8. Discord „Read States“ Service https://discord.com/blog/why-discord-is-switching-from-go-to-rust
  9. 9. Discord „Read States“ Service https://discord.com/blog/why-discord-is-switching-from-go-to-rust
  10. 10. Web Frameworks für Rust • actix-web (https://actix.rs) • rocket (https://rocket.rs) • tide (https://github.com/http-rs/tide) • warp (https://github.com/seanmonstar/warp) Auswahlhilfe: https://www.lpalmieri.com/posts/2020-07-04-choosing-a-rust-web-framework-2020-edition/
  11. 11. actix-web Architektur actix-web Client tokio Operating System High Performance Asynchronous IO
  12. 12. actix-web Features • Vollständig asynchron • HTTP/1.x und HTTP/2 • Request Routing • Middlewares (Logger, Session, CORS, etc.) • Transparente (De-)Kompression • WebSockets • Streams • Unterstützung für SSL/TLS • Unterstützung für Keep-Alive und Slow Requests • Statische Assets
  13. 13. Projekt Setup 1. cargo new actix-hello-world 2.
  14. 14. Hello World! use actix_web::{web, App, HttpRequest, HttpServer, Responder}; async fn greet(req: HttpRequest) -> impl Responder { let name = req.match_info().get("name").unwrap_or("World"); format!("Hello {}!", &name) } #[actix_web::main] async fn main() -> std::io::Result<()> { HttpServer::new(|| { App::new() .route("/", web::get().to(greet)) .route("/{name}", web::get().to(greet)) }) .bind("127.0.0.1:8000")? .run() .await }
  15. 15. Main-Funktion #[actix_web::main] async fn main() -> std::io::Result<()> { HttpServer::new(|| { App::new() .route("/", web::get().to(greet)) .route("/{name}", web::get().to(greet)) }) .bind("127.0.0.1:8000")? .run() .await } Server-Initialisierung App-Initialisierung Route-Mapping Adress-Bindung Entry-Point
  16. 16. Handler-Funktion // Impliziter HttpResponse async fn greet(req: HttpRequest) -> impl Responder { let name = req.match_info().get("name").unwrap_or("World"); format!("Hello {}!", &name) } // Expliziter HttpResponse async fn greet(req: HttpRequest) -> Result<HttpResponse, Error> { let name = req.match_info().get("name").unwrap_or("World"); let body = format!("Hello {}!", &name); Ok(HttpResponse::Ok().body(body)) }
  17. 17. Extractors -- Path #[get("/{name}")] async fn greet(web::Path(name): web::Path<String>) -> impl Responder { let value = if name.is_empty() { String::from("World!") } else { name }; format!("Hello {}!", &value) }
  18. 18. Extractors -- Query #[derive(Deserialize)] struct Info { name: String, } #[get("/")] async fn greet(info: web::Query<Info>) -> impl Responder { format!("Welcome {}!", info.name) } curl "http://localhost:8000?name=betterCode(Rust)"
  19. 19. Extractors -- JSON #[derive(Deserialize)] struct Info { name: String, } #[get("/")] async fn greet(info: web::Json<Info>) -> impl Responder { format!("Welcome {}!", info.name) } curl -X GET -H "Content-type: application/json“ -H "Accept: application/json" -d ‘{"name":"betterCode(Rust)"}‘ http://localhost:8000/"
  20. 20. Extractors – Form Data #[derive(Deserialize)] struct Info { name: String, } #[post("/")] async fn greet(info: web::Form<Info>) -> impl Responder { format!("Welcome {}!", info.name) } curl -X POST -H "Content-type: application/x-www-form-urlencoded" -d "name=betterCode(Rust)" http://localhost:8000/"
  21. 21. Middleware use actix_web::{web, middleware, App, HttpRequest, HttpServer, Responder}; […] #[actix_web::main] async fn main() -> std::io::Result<()> { std::env::set_var("RUST_LOG", "actix_web=info"); env_logger::init(); HttpServer::new(|| { App::new() .wrap(middleware::Logger::default()) .route("/", web::get().to(greet)) .route("/{name}", web::get().to(greet)) }) .bind("127.0.0.1:8000")? .run() .await }
  22. 22. Authentifizierung use actix_web::dev::ServiceRequest; use actix_web_httpauth::middleware::HttpAuthentication; use actix_web_httpauth::extractors::basic::BasicAuth; use actix_web_httpauth::extractors::bearer::BearerAuth; async fn basic_validation(req: ServiceRequest, _cred: BasicAuth) -> Result<ServiceRequest, Error> { // Validate credetials here... Ok(req) } async fn bearer_validation(req: ServiceRequest, _cred: BearerAuth) -> Result<ServiceRequest, Error> { // Validate credetials here... Ok(req) }
  23. 23. Authentifizierung -- Middleware #[actix_web::main] async fn main() -> std::io::Result<()> { HttpServer::new(|| { let basic_auth = HttpAuthentication::basic(basic_validation); let bearer_auth = HttpAuthentication::bearer(bearer_validation); App::new() .wrap(middleware::Logger::default()) .wrap(basic_auth) // oder .wrap(bearer_auth) .route("/", web::get().to(greet)) .route("/{name}", web::get().to(greet)) }) .bind("127.0.0.1:8000")? .run() .await }
  24. 24. Datenbankzugriff mit Diesel • Object Relational Mapper und Query Builder für Rust • Fokus auf möglichst schlanke Abstraktionen • Hohe Performance • Unterstützte Datenbanken: • MySQL • PostgreSQL • SQLite
  25. 25. Datenbankzugriff mit Diesel -- Setup cargo install diesel_cli --no-default-features --features sqlite echo "DATABASE_URL=test.db" > .env diesel setup
  26. 26. Datenbankzugriff mit Diesel -- Migrations mkdir migrations diesel migration generate users diesel migration run
  27. 27. Datenbankzugriff mit Diesel -- Mapping #[derive(Debug, Clone, Serialize, Deserialize, Queryable, Insertable)] pub struct User { pub id: String, pub name: String }
  28. 28. Datenbankzugriff mit Diesel -- Verbindung #[actix_web::main] async fn main() -> std::io::Result<()> { let connspec = std::env::var("DATABASE_URL").expect("DATABASE_URL"); let manager = ConnectionManager::<SqliteConnection>::new(connspec); let pool = r2d2::Pool::builder() .build(manager) .expect("Failed to create pool."); HttpServer::new(move || { App::new() .data(pool.clone()) .service(get_user) .service(add_user) }) .bind("127.0.0.1:8000")? .run() .await }
  29. 29. Datenbankzugriff mit Diesel -- Insert pub fn insert_new_user(nm: &str, conn: &SqliteConnection,) -> Result<User, diesel::result::Error> { use crate::schema::users::dsl::*; let new_user = User { id: Uuid::new_v4().to_string(), name: nm.to_owned() }; diesel::insert_into(users).values(&new_user).execute(conn)?; Ok(new_user) }
  30. 30. Datenbankzugriff mit Diesel -- Insert-Handler #[post("/user")] async fn add_user(pool: web::Data<DbPool>, form: web::Json<NewUser>) -> Result<HttpResponse, Error> { let conn = pool.get().expect("couldn't get db connection from pool"); let user = web::block(move || insert_new_user(&form.name, &conn)) .await .map_err(|e| { eprintln!("{}", e); HttpResponse::InternalServerError().finish() })?; Ok(HttpResponse::Ok().json(user)) }
  31. 31. Testen #[cfg(test)] mod tests { use super::*; use actix_web::test; #[actix_rt::test] async fn test_user_creation_ok() { // DB connection and logging setup let mut app = test::init_service( // App initialization ) .await; let req = test::TestRequest::post() .uri("/user") .set_json(&NewUser { name: "Test user".to_owned(), }).to_request(); let resp: User = test::read_response_json(&mut app, req).await; assert_eq!(resp.name, "Test user"); } }
  32. 32. Continuous Integration • Tests: • cargo test • Code Coverage: • cargo install tarpaulin * • cargo tarpaulin --ignore-tests • Lint: • rustup component add clippy • cargo clippy -- -D warnings • Formatting: • rustup component add rustfmt • cargo fmt -- --check • Auditing: • cargo install cargo-audit • cargo audit * = unterstützt zurzeit nur x86_64-linux
  33. 33. Fazit Rust als Basis für Microservices bietet: • Hohe Performance durch Ahead-of-Time Kompilierung, Zero Cost Abstractions • Sicheres Speichermanagement bereits während der Kompilierung • Sichere Nebenläufigkeit • Keine Einbrüche bei der Performance durch Garbage Collection • Komfortables Tooling, welches etablierten Sprachen und Frameworks in nichts nachsteht • Freundliche und hilfsbereite Community • Teilweise steile Lernkurve (aber es lohnt sich!)
  34. 34. Literatur (01/2022) (12/2021)
  35. 35. Vielen Dank! https://www.rust-lang.org/learn https://actix.rs/docs https://diesel.rs/guides Twitter: @jens_siebert

×