Slide 1

Slide 1 text

🦀 tower battle / Rust Tokyo 2023 Yuta Ide (@sadnessOjisan)

Slide 2

Slide 2 text

About me { “name”: “Yuta Ide”, “account”: “@sadnessOjisan”, “belong”: “Nikkei / Japanese News Paper Company”, “lang”: [“JS/TS(2018-)”, “Rust(2022-)”], “misc”: “rust.tokyo 2019 stuff” }

Slide 3

Slide 3 text

Section0: About Nikkei

Slide 4

Slide 4 text

Nikkei is a Japanese Newspaper Company https://www.nikkei.com/

Slide 5

Slide 5 text

We adopt micro frontend architecture https://hack.nikkei.com/blog/denshiban-ssr/ https://hack.nikkei.com/blog/denshiban-ssr/ About Nikkei Denshiban (https://www.nikkei.com)

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

https://hack.nikkei.com/blog/denshiban-ssr/

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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)

Slide 10

Slide 10 text

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 😇

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

We want to replace it… The choice is… ● Node.js v20 + TypeScript + eslint + vitest ● Golang ● Rust How about Rust?

Slide 16

Slide 16 text

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)

Slide 17

Slide 17 text

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…

Slide 18

Slide 18 text

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.

Slide 19

Slide 19 text

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.

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

Section1: About Axum

Slide 22

Slide 22 text

Hello World!

Slide 23

Slide 23 text

Axum architecture ● Axum is HTTP Server FW. ● Axum = hyper + tower. hyper future future future future tower service call user req response tokio

Slide 24

Slide 24 text

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/

Slide 25

Slide 25 text

Tower provides abstract of req → res ● Official description is `async fn(Request) -> Result` ● 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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

LIVE READING axum depends on tower

Slide 29

Slide 29 text

● 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

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

Section2: How to use axum

Slide 34

Slide 34 text

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.

Slide 35

Slide 35 text

How to avoid tight coupling with axum ● Introduce Layered architecture, don’t use axum except for controller ● Use tower service ● Create E2E test

Slide 36

Slide 36 text

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" }

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

Service is not only for tower https://tokio.rs/blog/2021-05-14-inventing-the-service-trait

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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.