SlideShare ist ein Scribd-Unternehmen logo
1 von 35
Downloaden Sie, um offline zu lesen
Microservices mit Rust
Jens Siebert (@jens_siebert)
betterCode(Rust), 13. Oktober 2021
Über mich
• Senior Software Developer bei
doks.innovation in Kassel
• Drohnen-Steuerung, Computer
Vision, Architektur
• Maker, 3D-Drucker, Nerd
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
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
Und Rust?
Rust:
• Nicht ganz so einfach zu lernen
• Sicherheit durch automatische Speicherverwaltung
• Garbage Collection
• Schneller Start-up/Ahead-of-Time Kompilierung
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
Discord „Read States“ Service
https://discord.com/blog/why-discord-is-switching-from-go-to-rust
Discord „Read States“ Service
https://discord.com/blog/why-discord-is-switching-from-go-to-rust
Discord „Read States“ Service
https://discord.com/blog/why-discord-is-switching-from-go-to-rust
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/
actix-web Architektur
actix-web
Client
tokio
Operating System
High Performance Asynchronous IO
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
Projekt Setup
1. cargo new actix-hello-world
2.
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
}
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
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))
}
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)
}
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)"
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/"
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/"
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
}
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)
}
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
}
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
Datenbankzugriff mit Diesel -- Setup
cargo install diesel_cli --no-default-features --features sqlite
echo "DATABASE_URL=test.db" > .env
diesel setup
Datenbankzugriff mit Diesel -- Migrations
mkdir migrations
diesel migration generate users
diesel migration run
Datenbankzugriff mit Diesel -- Mapping
#[derive(Debug, Clone, Serialize, Deserialize, Queryable, Insertable)]
pub struct User {
pub id: String,
pub name: String
}
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
}
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)
}
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))
}
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");
}
}
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
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!)
Literatur
(01/2022) (12/2021)
Vielen Dank!
https://www.rust-lang.org/learn
https://actix.rs/docs
https://diesel.rs/guides
Twitter: @jens_siebert

Weitere ähnliche Inhalte

Ähnlich wie Microservices mit Rust

Production-ready Infrastruktur in 3 Wochen
Production-ready Infrastruktur in 3 WochenProduction-ready Infrastruktur in 3 Wochen
Production-ready Infrastruktur in 3 WochenAndré Goliath
 
Cloud Native & Java EE: Freund oder Feind?
Cloud Native & Java EE: Freund oder Feind?Cloud Native & Java EE: Freund oder Feind?
Cloud Native & Java EE: Freund oder Feind?QAware GmbH
 
Cloud Native und Java EE: Freund oder Feind?
Cloud Native und Java EE: Freund oder Feind?Cloud Native und Java EE: Freund oder Feind?
Cloud Native und Java EE: Freund oder Feind?Josef Adersberger
 
IPC 2015 Zend Framework 3 Reloaded
IPC 2015 Zend Framework 3 ReloadedIPC 2015 Zend Framework 3 Reloaded
IPC 2015 Zend Framework 3 ReloadedRalf Eggert
 
Elegantes In-Memory Computing mit Apache Ignite und Kubernetes. @data2day
Elegantes In-Memory Computing mit Apache Ignite und Kubernetes. @data2dayElegantes In-Memory Computing mit Apache Ignite und Kubernetes. @data2day
Elegantes In-Memory Computing mit Apache Ignite und Kubernetes. @data2dayMario-Leander Reimer
 
In-Memory Computing mit Apache Ignite und Kubernetes
In-Memory Computing mit Apache Ignite und KubernetesIn-Memory Computing mit Apache Ignite und Kubernetes
In-Memory Computing mit Apache Ignite und KubernetesQAware GmbH
 
Rhomobile
RhomobileRhomobile
RhomobileJan Ow
 
Microprofile-Anwendungen mit Quarkus
Microprofile-Anwendungen mit Quarkus Microprofile-Anwendungen mit Quarkus
Microprofile-Anwendungen mit Quarkus gedoplan
 
OpenTuesday: Jede Sekunde zählt - Webbeschleuniger Varnish im Einsatz
OpenTuesday: Jede Sekunde zählt - Webbeschleuniger Varnish im EinsatzOpenTuesday: Jede Sekunde zählt - Webbeschleuniger Varnish im Einsatz
OpenTuesday: Jede Sekunde zählt - Webbeschleuniger Varnish im EinsatzDigicomp Academy AG
 
Kuck mal, Node.js! Einstieg für .NET Entwickler mit Visual Studio Code und Ty...
Kuck mal, Node.js! Einstieg für .NET Entwickler mit Visual Studio Code und Ty...Kuck mal, Node.js! Einstieg für .NET Entwickler mit Visual Studio Code und Ty...
Kuck mal, Node.js! Einstieg für .NET Entwickler mit Visual Studio Code und Ty...Gregor Biswanger
 
Docker und Kubernetes Patterns & Anti-Patterns
Docker und Kubernetes Patterns & Anti-PatternsDocker und Kubernetes Patterns & Anti-Patterns
Docker und Kubernetes Patterns & Anti-PatternsJosef Adersberger
 
Docker und Kubernetes Patterns & Anti-Patterns
Docker und Kubernetes Patterns & Anti-PatternsDocker und Kubernetes Patterns & Anti-Patterns
Docker und Kubernetes Patterns & Anti-PatternsQAware GmbH
 
Service Mesh mit Istio und MicroProfile - eine harmonische Kombination?
Service Mesh mit Istio und MicroProfile - eine harmonische Kombination?Service Mesh mit Istio und MicroProfile - eine harmonische Kombination?
Service Mesh mit Istio und MicroProfile - eine harmonische Kombination?Michael Hofmann
 
.NET Summit 2016 in München: ASP.NET Core 1
.NET Summit 2016 in München: ASP.NET Core 1.NET Summit 2016 in München: ASP.NET Core 1
.NET Summit 2016 in München: ASP.NET Core 1Manfred Steyer
 

Ähnlich wie Microservices mit Rust (20)

Production-ready Infrastruktur in 3 Wochen
Production-ready Infrastruktur in 3 WochenProduction-ready Infrastruktur in 3 Wochen
Production-ready Infrastruktur in 3 Wochen
 
TypeScript
TypeScriptTypeScript
TypeScript
 
Hdc2012 cordova-präsi
Hdc2012 cordova-präsiHdc2012 cordova-präsi
Hdc2012 cordova-präsi
 
Cloud Native & Java EE: Freund oder Feind?
Cloud Native & Java EE: Freund oder Feind?Cloud Native & Java EE: Freund oder Feind?
Cloud Native & Java EE: Freund oder Feind?
 
Cloud Native und Java EE: Freund oder Feind?
Cloud Native und Java EE: Freund oder Feind?Cloud Native und Java EE: Freund oder Feind?
Cloud Native und Java EE: Freund oder Feind?
 
Webapplikationen mit Node.js
Webapplikationen mit Node.jsWebapplikationen mit Node.js
Webapplikationen mit Node.js
 
IPC 2015 Zend Framework 3 Reloaded
IPC 2015 Zend Framework 3 ReloadedIPC 2015 Zend Framework 3 Reloaded
IPC 2015 Zend Framework 3 Reloaded
 
Elegantes In-Memory Computing mit Apache Ignite und Kubernetes. @data2day
Elegantes In-Memory Computing mit Apache Ignite und Kubernetes. @data2dayElegantes In-Memory Computing mit Apache Ignite und Kubernetes. @data2day
Elegantes In-Memory Computing mit Apache Ignite und Kubernetes. @data2day
 
In-Memory Computing mit Apache Ignite und Kubernetes
In-Memory Computing mit Apache Ignite und KubernetesIn-Memory Computing mit Apache Ignite und Kubernetes
In-Memory Computing mit Apache Ignite und Kubernetes
 
Rhomobile
RhomobileRhomobile
Rhomobile
 
Testing tools
Testing toolsTesting tools
Testing tools
 
Microprofile-Anwendungen mit Quarkus
Microprofile-Anwendungen mit Quarkus Microprofile-Anwendungen mit Quarkus
Microprofile-Anwendungen mit Quarkus
 
OpenTuesday: Jede Sekunde zählt - Webbeschleuniger Varnish im Einsatz
OpenTuesday: Jede Sekunde zählt - Webbeschleuniger Varnish im EinsatzOpenTuesday: Jede Sekunde zählt - Webbeschleuniger Varnish im Einsatz
OpenTuesday: Jede Sekunde zählt - Webbeschleuniger Varnish im Einsatz
 
Kuck mal, Node.js! Einstieg für .NET Entwickler mit Visual Studio Code und Ty...
Kuck mal, Node.js! Einstieg für .NET Entwickler mit Visual Studio Code und Ty...Kuck mal, Node.js! Einstieg für .NET Entwickler mit Visual Studio Code und Ty...
Kuck mal, Node.js! Einstieg für .NET Entwickler mit Visual Studio Code und Ty...
 
Docker und Kubernetes Patterns & Anti-Patterns
Docker und Kubernetes Patterns & Anti-PatternsDocker und Kubernetes Patterns & Anti-Patterns
Docker und Kubernetes Patterns & Anti-Patterns
 
Docker und Kubernetes Patterns & Anti-Patterns
Docker und Kubernetes Patterns & Anti-PatternsDocker und Kubernetes Patterns & Anti-Patterns
Docker und Kubernetes Patterns & Anti-Patterns
 
Service Mesh mit Istio und MicroProfile - eine harmonische Kombination?
Service Mesh mit Istio und MicroProfile - eine harmonische Kombination?Service Mesh mit Istio und MicroProfile - eine harmonische Kombination?
Service Mesh mit Istio und MicroProfile - eine harmonische Kombination?
 
.NET Summit 2016 in München: ASP.NET Core 1
.NET Summit 2016 in München: ASP.NET Core 1.NET Summit 2016 in München: ASP.NET Core 1
.NET Summit 2016 in München: ASP.NET Core 1
 
Node.js für Webapplikationen
Node.js für WebapplikationenNode.js für Webapplikationen
Node.js für Webapplikationen
 
node.js
node.jsnode.js
node.js
 

Mehr von Jens Siebert

TinyML – Machine Learning für eingebettete Systeme
TinyML – Machine Learning für eingebettete SystemeTinyML – Machine Learning für eingebettete Systeme
TinyML – Machine Learning für eingebettete SystemeJens Siebert
 
Deep Learning mit TensorFlow.js
Deep Learning mit TensorFlow.jsDeep Learning mit TensorFlow.js
Deep Learning mit TensorFlow.jsJens Siebert
 
Chatbots bauen mit dem Microsoft Bot Framework
Chatbots bauen mit dem Microsoft Bot FrameworkChatbots bauen mit dem Microsoft Bot Framework
Chatbots bauen mit dem Microsoft Bot FrameworkJens Siebert
 
Integrating The Things Network Applications with Azure IoT Services
Integrating The Things Network Applications with Azure IoT ServicesIntegrating The Things Network Applications with Azure IoT Services
Integrating The Things Network Applications with Azure IoT ServicesJens Siebert
 
Embedded JavaScript
Embedded JavaScriptEmbedded JavaScript
Embedded JavaScriptJens Siebert
 
Windows 10 IoT Core
Windows 10 IoT CoreWindows 10 IoT Core
Windows 10 IoT CoreJens Siebert
 
Microsoft Bot Framework (Node.js Edition)
Microsoft Bot Framework (Node.js Edition)Microsoft Bot Framework (Node.js Edition)
Microsoft Bot Framework (Node.js Edition)Jens Siebert
 
Microsoft Bot Framework (.NET Edition)
Microsoft Bot Framework (.NET Edition)Microsoft Bot Framework (.NET Edition)
Microsoft Bot Framework (.NET Edition)Jens Siebert
 
Windows 10 IoT Core
Windows 10 IoT CoreWindows 10 IoT Core
Windows 10 IoT CoreJens Siebert
 
Windows 10 IoT Core
Windows 10 IoT CoreWindows 10 IoT Core
Windows 10 IoT CoreJens Siebert
 

Mehr von Jens Siebert (17)

WebAssembly
WebAssemblyWebAssembly
WebAssembly
 
Embedded Rust
Embedded RustEmbedded Rust
Embedded Rust
 
Embedded Rust
Embedded RustEmbedded Rust
Embedded Rust
 
TinyML – Machine Learning für eingebettete Systeme
TinyML – Machine Learning für eingebettete SystemeTinyML – Machine Learning für eingebettete Systeme
TinyML – Machine Learning für eingebettete Systeme
 
Deep Learning mit TensorFlow.js
Deep Learning mit TensorFlow.jsDeep Learning mit TensorFlow.js
Deep Learning mit TensorFlow.js
 
Chatbots bauen mit dem Microsoft Bot Framework
Chatbots bauen mit dem Microsoft Bot FrameworkChatbots bauen mit dem Microsoft Bot Framework
Chatbots bauen mit dem Microsoft Bot Framework
 
Integrating The Things Network Applications with Azure IoT Services
Integrating The Things Network Applications with Azure IoT ServicesIntegrating The Things Network Applications with Azure IoT Services
Integrating The Things Network Applications with Azure IoT Services
 
GraphQL
GraphQLGraphQL
GraphQL
 
Embedded JavaScript
Embedded JavaScriptEmbedded JavaScript
Embedded JavaScript
 
Windows 10 IoT Core
Windows 10 IoT CoreWindows 10 IoT Core
Windows 10 IoT Core
 
Microsoft Bot Framework (Node.js Edition)
Microsoft Bot Framework (Node.js Edition)Microsoft Bot Framework (Node.js Edition)
Microsoft Bot Framework (Node.js Edition)
 
Microsoft Bot Framework (.NET Edition)
Microsoft Bot Framework (.NET Edition)Microsoft Bot Framework (.NET Edition)
Microsoft Bot Framework (.NET Edition)
 
Electron
ElectronElectron
Electron
 
Windows 10 IoT Core
Windows 10 IoT CoreWindows 10 IoT Core
Windows 10 IoT Core
 
Physical Web
Physical WebPhysical Web
Physical Web
 
Windows 10 IoT Core
Windows 10 IoT CoreWindows 10 IoT Core
Windows 10 IoT Core
 
TypeScript
TypeScriptTypeScript
TypeScript
 

Microservices mit Rust

  • 1. Microservices mit Rust Jens Siebert (@jens_siebert) betterCode(Rust), 13. Oktober 2021
  • 2. Über mich • Senior Software Developer bei doks.innovation in Kassel • Drohnen-Steuerung, Computer Vision, Architektur • Maker, 3D-Drucker, Nerd
  • 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. 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. Und Rust? Rust: • Nicht ganz so einfach zu lernen • Sicherheit durch automatische Speicherverwaltung • Garbage Collection • Schneller Start-up/Ahead-of-Time Kompilierung
  • 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. Discord „Read States“ Service https://discord.com/blog/why-discord-is-switching-from-go-to-rust
  • 8. Discord „Read States“ Service https://discord.com/blog/why-discord-is-switching-from-go-to-rust
  • 9. Discord „Read States“ Service https://discord.com/blog/why-discord-is-switching-from-go-to-rust
  • 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/
  • 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. Projekt Setup 1. cargo new actix-hello-world 2.
  • 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. Datenbankzugriff mit Diesel -- Setup cargo install diesel_cli --no-default-features --features sqlite echo "DATABASE_URL=test.db" > .env diesel setup
  • 26. Datenbankzugriff mit Diesel -- Migrations mkdir migrations diesel migration generate users diesel migration run
  • 27. Datenbankzugriff mit Diesel -- Mapping #[derive(Debug, Clone, Serialize, Deserialize, Queryable, Insertable)] pub struct User { pub id: String, pub name: String }
  • 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. 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. 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. 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. 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. 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!)