Share
Embed
Transcript
40 slides
-
About me { “name”: “Yuta Ide”, “account”: “@sadnessOjisan”, “belong”: “Nikkei
/ Japanese News Paper Company”, “lang”: [“JS/TS(2018-)”, “Rust(2022-)”], “misc”: “rust.tokyo 2019 stuff” } -
We adopt micro frontend architecture CDN (Fastly) Node.js Article Detail
Search Market Article Index connect by anchor tags We love HTTP and HTML … handlebars handlebars VCL + handlebars react split with vertical aggregate -
We adopt micro frontend architecture CDN (Fastly) Node.js Article Detail
Topic Index Search Market Article Index connect by anchor tags We love HTTP and HTML handlebars handlebars handlebars VCL + handlebars We maintain 24 micro frontend services with limited resource -
We adopt micro frontend architecture CDN (Fastly) Node.js Article Detail
Topic Index Search Market Article Index connect by anchor tags We love HTTP and HTML handlebars handlebars handlebars VCL + handlebars We maintain 24 micro frontend services with Node.js v12 (current is v21) (Including tooling, we maintain 40↑ projects with old Node.js) -
We adopt micro frontend architecture CDN (Fastly) Node.js Article Detail
Topic Index Search Market Article Index connect by anchor tags We love HTTP and HTML handlebars handlebars handlebars VCL + handlebars We are now replacing v12 → v16 → v18→ v20 for 11 months 😇 -
Updating Node.js and library is so hard • Node.js releases
a major update every 6 months. It is frequent. • We must update Node.js version for EOL. • After updating Node.js, sometimes (I often experience) fail npm install, or fail building app with bundler. Due to node-gyp, node-sass, --openssl-legacy-provider… 😭 We need maintanability -
Should we follow the new lang and lib version? application
With UI JSON API Developing Stable Sometimes it needs maintenance e.g. serious security patch, EOL We always doesn’t have to keep up with each new version. Always it needs maintenance -
Should we follow the new lang and lib version? application
With UI JSON API Developing Stable Sometimes it needs maintenance e.g. serious security patch, EOL We always doesn’t have to keep up with each new version. Always it needs maintenance We considered replacing a new tech stack -
Our requirements for the new tech stack are… • Typing
• Lint • Test • OAS (Open API Schema) • Documentation / Gen Doc • Clever pkg manager • Error handling • Logging / Tracing Rich and modern feature help our maintenance -
We want to replace it… The choice is… • Node.js
v20 + TypeScript + eslint + vitest • Golang • Rust How about Rust? -
Rust includes… • Typing → strict typing. e.g. lifetime, no
hatch except for unsafe • Lint → Clippy • Test → cargo test, in-source testing • OAS → 3rd party lib. e.g. utopia • Documentation / Gen Doc → cargo doc, utopia • Clever pkg manager → cargo • Error handling → Result. thiserror (de facto 3rd party lib) • Logging / Tracing → tracing (de facto 3rd party lib) -
Rust with Nikkei Some teams at Nikkei adopted Rust in
production • Using Fastly Computing@Edge for delivery and handling access log. ◦ access log server handles 6500 req/sec • Wave BFF Server uses Rust for schemaful endpoint with gRPC and HTTP. ◦ Wave: https://hack.nikkei.com/blog/advent20201218/ So I’m in a good environment to choose Rust. But… -
Is choosing Rust appropriate? My concern is • Rust is
not batteries-included lang. We must choose 3rd party libs. ◦ async runtime ◦ web server ◦ common util (serializer, date, …) • Long-term stability of 3rd party ecosystems. -
But we chose Rust • Rust itself is solid •
Although Rust is not batteries-included lang, there are many de facto libs. (e.g. tokio, thiserror, anyhow, chrono…) • Rust can find breaking changes easily at compile time. -
We choose axum as Web FW • hyper ◦ primitive.
I need router. • actix-web ◦ good choice • axum ◦ tokio-rs maintain this. I believe maintenance continue to If we choose a web fw, the choices are -
Axum architecture • Axum is HTTP Server FW. • Axum
= hyper + tower. hyper future future future future tower service call user req response tokio -
HTTP Server with Hyper • Hyper is a tokio based
HTTP library. • It’s too primitive. (e.g. Hyper doesn’t have a router.) Hyper is not for web app developer but for library developer https://hyper.rs/ -
Tower provides abstract of req → res • Official description
is `async fn(Request) -> Result<Response, Error>` • Tower provides a trait for creating middleware. • HTTP Server can be described as composition of middlewares. Router, Auth, Logger, Retry, Rate Limit, Timeout, and so on. https://github.com/tower-rs/tower -
Service is a main concept of tower • Service is
an abstract model of processing req -> res. • Main part is call function. This accepts a request and returns a response future. https://docs.rs/tower/latest/tower/trait.Service.html -
Layer can stack Services • Each tower service should have
a single role. (SRP of SOLID) • User can combine services. In mental model, that’s stacking service on layer. requests | v +----- layer_three -----+ | +---- layer_two ----+ | | | +-- layer_one --+ | | | | | | | | | | | handler | | | | | | | | | | | +-- layer_one --+ | | | +---- layer_two ----+ | +----- layer_three -----+ | v responses https://docs.rs/axum/latest/axum/middleware/index.html -
• Router is a Tower Service in axum. call return
Oneshot which is tower service and have handler Fn. • Register path and handler • Handler value is into HTTP Response, the converter is also tower service -
Router is a Tower Service in axum • Router’s role
is matching path, and execute handler. • Router is implemented as tower Service. call finds matched path, and execute handler function. handler is also service. • Router’s call return RouteFuture. This includes Oneshot struct which impl Future poll of calling handler service. https://docs.rs/axum/latest/axum/struct.Router.html -
Register path and handler • Router is a KV store.
• key is path • value is handler https://docs.rs/axum/latest/axum/struct.Router.html#method.route -
Handler value is into HTTP Response, the converter is also
tower service • Routing handler is a function. The return value can be HTTP Response with any Status Code. • IntoResponse trait control it. str and Result and many struct has default IntoResponse impl. So, automatically convert to HTTP. • Endpoint have MapResponse struct which has into_response Fn. MapResponse is also tower service. The service is called in polling Oneshot. HTTP/1.1 200 OK content-type: text/plain; charset=utf-8 content-length: 13 date: Thu, 19 Oct 2023 03:22:40 GMT Hello, World -
I want to avoid tight coupling with axum • Although
axum is popular Web FW, it is 3rd party. • Sometimes our app may fail updating axum version because of our wrong implement. -
How to avoid tight coupling with axum • Introduce Layered
architecture, don’t use axum except for controller • Use tower service • Create E2E test -
Introduce Layered Architecture • Cargo can manage dependency graph through
its workspaces feature.(e.g. controller cannot import repository) • I recommend you split your application into modules as it grows • And use axum only in controller and router. entity repository service usecase controller [package] name = "controller" [dependencies] axum = "0.6.20" entity = { path = "../entity" } usecase = { path = "../usecase" } -
Separation of cross-cutting concerns • Separation of cross-cutting concerns with
tower. • Middleware is auth, logging, rate limit, delay, retry and so on. • Any layer from any framework is reusable if it implements tower interface. axtix-web is not tower based FW. But that middleware is like tower service. https://actix.rs/docs/middleware -
Case: Easily move to hyper • Axum = hyper +
tower • We can use hyper directly • service_fn transform function to Service • Axum router’s handler is just function which return &str, Result and so on. It’s reusable. • For hyper v1, it may need tower-hyper-http-body-compat. Service is general. can run axum router handler -
Conclusion • Although developing HTTP server in Rust require 3rd
party FW, Rust would be good choice. • Most FW use simple function as router, so you can easily replace to each library. • For replace, let’s adopt layered architecture, and use FW in only router and controller. • About AOP logic, If we adopt tower interface, we may be able to replace to each library because other library also adopt tower or tower like Service. So I use tower based FW.