Rust, which communicates with client apps over gRPC. The server is running on Cloud Run(dev) and internal GKE clusters(staging and production). In addition, I'm developing a CLI for developers, markup parser and AST transformers in Rust.
the most abstract and fundamental layer. It has a signiture: async fn(Request) -> Result<Response, Error> . It accepts inputs of type Request and returns either successful value of type Response or failed value of type Error in async context. Request ・ Response ・ Error here are protocol agnostic generic types. In other words, they are not tied to HTTP. tower has a concept of layer: Request -> Request , Response -> Response , which allows developers to share common concerns between tower services.
HTTP concerns. hyper is a relatively low-level library, meant to be a building block for libraries and applications. Axum and Warp, well-known Rust web frameworks, and reqwest HTTP client are based on hyper. The latest hyper uses its own service interface instead of tower_service for simplicity through older hyper depends on tower_sercice.
is not hard to write a snippet for HTTP request using hyper. let client: hyper::Client<HttpsConnector<HttpConnector>, Body> = todo!(); let payload = serde_json::to_vec(&payloadable).unwrap(); let req = Request::builder() .uri(endpoint) .method("POST") .header(CONTENT_TYPE, "application/json") .header(ACCEPT, "application/json") .body(Body::from(payload)) .unwrap(); let res = client .request(req) .await .unwrap(); ref: https://github.com/i10416/firebase-messaging- rs/blob/57279c2bb2aed2782ab679eff98bf7ea813cffef/src/lib.rs#L83
cheap to clone and cloning is the recommended way to share a Client . In fact, the internal pool is wrapped inside Arc<Mutex<_>> . pub(super) struct Pool<T> { // If the pool is disabled, this is None. inner: Option<Arc<Mutex<PoolInner<T>>>>, } ref: https://github.com/hyperium/hyper/blob/a22c5122e1d2d58e3f30d059978c3eed14cca0 82/src/client/pool.rs#L19
tonic: Rust's gRPC implementation and codegen based on tower and hyper Prost reads and parses protobuf schema into Rust types. It delegates the rpc parts to respective implementations. For example, tonic provides gRPC implementation.
can use tower layers to add features to a server in a composable way. The following example demonstrates how easy it is to support both gRPC and gRPC web with a few lines. #[cfg(not(feature = "grpc-web"))] let server = Server::builder() .layer(TraceLayer::new_for_grpc()); #[cfg(feature = "grpc-web")] let server = Server::builder() .accept_http1(true) .layer(TraceLayer::new_for_http()) .layer(GrpcWebLayer::new()); server .layer(JWTVerificationLayer::new(..)) .add_service(health_service) // ... .serve()
useful for developing gRPC web apps on GCP. tonic-health gcloud-sdk-rs types and RPCs generated from googleapis proto and REST api schema with Workload Identity Federation(OIDC) support firestore-rs ergonomic gcloud-sdk-rs wapper with fluent syntax and serde. tracing+tracing-stackdriver Easy structured logging compatible with stackdriver logging format.
public interface. When your application depends on tonic of a version different from one gcloud- sdk-rs depends on, it won't compile. let client: GoogleApi<IamCredentialsClient<GoogleAuthMiddleware>> = GoogleApiClient::from_function( IamCredentialsClient::new, "https://iamcredentials.googleapis.com/v1", None, ) .await .unwrap(); let req: GenerateIdTokenRequest = todo!(); match client .get() .generate_id_token(tonic::Request::new(req)) .await
layer which entails I/O operation such as JWT verification because tower middleware does not provide easy API to implement async layer. there is an async layer helper crate...but it does not seem officially supported.
service boundaries(e.g. rpc, file and db i/o) and you get strong type safety in the rest of codebase easy to build, easy to deploy with container async runtime has define-and-run semantics powerful yet concise syntax with algebraic data types and pattern matching
cargo +nightly udeps use Swatinem/rust-cache on CI setup cache generate action as in this repository ref: https://github.com/Swatinem/rust-cache/issues/95 ※ GitHub Actions restrict users from sharing cache between branches. Make good use of cache from base branches on PRs to avoid cache miss. https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed- up-workflows#restrictions-for-accessing-a-cache
functional languages, such as Scala, also give similar compile time safety and higher abstraction with less cognitive cost and less steep learning curve(at the cost of performance overhead). In particular, Scala has a great ecosystem that corresponds to tokio and tower ecosystem. Rust Scala A => F[B] + HTTP tower, hyper http4s gRPC tonic fs2-grpc, http4s-grpc Async Runtime( F[_] ) tokio cats-effect Ad: Rustacean のための Scala 3 入門