Ecat is a Rust Microservices Framework

Ecat

English | 简体中文

e-cat is a Rust microservices framework benchmarked against go-kratos/kratos v3.

It provides an API-first development experience, a pluggable component architecture, a unified HTTP/gRPC middleware abstraction, and a full CLI toolchain. Developers already familiar with Kratos can get up to speed seamlessly, while taking full advantage of Rust’s type safety, zero-cost abstractions, and extreme performance.

Design Architecture

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
┌──────────────────────────────────────────────────────────────┐
│ ecat-cli │
│ (new │ proto │ run │ build) │
├──────────────────────────────────────────────────────────────┤
│ ecat (app lifecycle) │
│ AppBuilder → App { name, servers, hooks, ... } │
├────────────────────┬────────────────────┬────────────────────┤
│ transport │ middleware │ registry │
│ ───────── │ ────────── │ ──────── │
│ HTTP (axum) │ RecoveryLayer │ memory │
│ gRPC (tonic) │ TracingLayer │ │
│ encoding │ LoggingLayer │ │
│ │ TimeoutLayer │ │
│ │ SecurityLayer │ │
├────────────────────┼────────────────────┼────────────────────┤
│ config │ errors │ metadata │
│ ────── │ ────── │ ──────── │
│ file / env │ ErrorCode │ key-value │
│ remote source │ Error │ HTTP/gRPC │
├────────────────────┴────────────────────┴────────────────────┤
│ data layer │
│ ──────────────────────────────────────────────── │
│ rdbms: SQLite / PostgreSQL / MySQL / TiDB │
│ cache: Redis / Memcached │
│ olap: ClickHouse │
│ search: OpenSearch / Elasticsearch │
│ graph: Neo4j / NebulaGraph / ArangoDB │
│ tsdb: InfluxDB / Apache IoTDB / QuestDB │
├──────────────────────────────────────────────────────────────┤
│ ecat-protos │
│ (shared .proto definitions: errors, metadata, ...) │
└──────────────────────────────────────────────────────────────┘

Request Handling Flow

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
Client request

├─ HTTP :8000 ────→ axum::Router ──┐
│ │
└─ gRPC :9000 ────→ tonic::Server ─┤

┌───────┴───────┐
│ Middleware │
│ ────────── │
│ 1. Recovery │ catch panics
│ 2. Tracing │ inject trace_id
│ 3. Logging │ request logs
│ 4. Auth │ authentication & authorization
│ 5. Metrics │ metrics collection
│ 6. Security │ attack detection (alerts only)
└───────┬───────┘

┌───────┴───────┐
│ Handler │ user business logic
│ (tower::Service)│
└───────┬───────┘

┌───────┴───────┐
│ Response │ encoding & serialization
│ JSON/Protobuf │
└───────────────┘

Features

  • API-first: Define APIs, error codes, and metadata in Protobuf; code generation via prost + tonic-build
  • Dual protocol support: HTTP (axum) and gRPC (tonic) share the same tower::Layer middleware
  • Pluggable architecture: Registry, Config, Logging, and Encoding are all abstracted behind traits, with production-ready implementations provided by default
  • Middleware system: Built-in Recovery, Tracing, Logging, Timeout, and Security; composed with tower::ServiceBuilder
  • Application lifecycle: Build an App with the Builder pattern, start multiple servers concurrently, handle SIGTERM/SIGINT, and hook into start/stop lifecycle events
  • Type safety: A protobuf-based error code system with compile-time HTTP status code mapping
  • Observability: tracing + opentelemetry + Prometheus out of the box
  • Attack detection: Automatically recognizes 27 attack patterns (SQL injection, XSS, SSRF, path traversal, etc.), logs an alert only, never blocks
  • Multiple data sources: RDBMS (SQLite/PG/MySQL/TiDB), cache, OLAP, search engine, graph database, time series database

Kratos Concept Mapping

Kratos (Go) e-cat (Rust) Notes
kratos.New() App::builder() Builder pattern
http.Handler tower::Service The standard trait in the Rust ecosystem
http.Server axum::Router Mainstream community HTTP framework
grpc.Server tonic::transport::Server The most mature gRPC implementation
proto generate prost + tonic-build Community-standard protobuf
registry.Discovery Registry trait Pluggable service registry and discovery
config.Source ConfigSource trait Multi-source configuration loading

Tech Stack

Component Choice
Async runtime tokio
HTTP axum
gRPC tonic
Protobuf prost + tonic-build
Middleware tower::Service / Layer
Logging / tracing tracing + opentelemetry-rust
Metrics prometheus
Serialization serde + prost
Attack detection security-rust
RDBMS sqlx
CLI clap

Supported Databases

Category Database Crate Rust driver
RDBMS SQLite ecat-data-sqlx sqlx
RDBMS PostgreSQL ecat-data-sqlx sqlx
RDBMS MySQL ecat-data-sqlx sqlx
RDBMS TiDB ecat-data-sqlx sqlx
Cache Redis ecat-data-redis redis-rs
Cache Memcached ecat-data-memcached memcache
OLAP ClickHouse ecat-data-clickhouse clickhouse-rs
Search OpenSearch ecat-data-opensearch opensearch
Search Elasticsearch ecat-data-elasticsearch elasticsearch
Graph Neo4j ecat-data-neo4j neo4rs
Graph NebulaGraph ecat-data-nebulagraph nebula-client
Graph ArangoDB ecat-data-arangodb arangors
Time series InfluxDB ecat-data-influxdb influxdb2
Time series Apache IoTDB ecat-data-iotdb iotdb-client-rs
Time series QuestDB ecat-data-questdb questdb-rs (ILP)

Every data backend goes through a unified trait abstraction (RdbmsClient / Cache / SearchClient / GraphClient / TsdbClient); pull in the matching contrib crate as needed.

Project Structure

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
e-cat/
├── ecat/ # core: App lifecycle
├── ecat-transport/ # transport abstraction (Server trait)
├── ecat-transport-http/ # axum implementation
├── ecat-transport-grpc/ # tonic implementation
├── ecat-middleware/ # tower::Layer middleware
├── ecat-protos/ # Protobuf definitions
├── ecat-errors/ # error code system
├── ecat-metadata/ # metadata propagation
├── ecat-encoding/ # serialization abstraction
├── ecat-logging/ # tracing integration
├── ecat-registry/ # service registry and discovery
├── ecat-config/ # configuration management
├── ecat-metrics/ # Prometheus integration
├── ecat-data/ # data access traits
├── ecat-security/ # attack detection (security-rust)
├── ecat-cli/ # CLI tool
├── docs/ # design docs and implementation plans
└── examples/ # example projects

Quick Start

Prerequisites

  • Rust 1.80+ (stable toolchain)
  • protoc (the Protocol Buffers compiler)

Install the CLI

1
cargo install ecat-cli

Create a Service

1
2
3
4
5
6
7
8
9
10
11
12
13
# scaffold a project
ecat new helloworld
cd helloworld

# add a proto definition
ecat proto add api/helloworld/helloworld.proto

# generate client and server code
ecat proto client api/helloworld/helloworld.proto
ecat proto server api/helloworld/helloworld.proto -t internal/service

# run in dev mode
ecat run

Visit http://localhost:8000/helloworld/ecat.

Code Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
use ecat::App;
use ecat_transport_http::HttpServer;
use ecat_transport_grpc::GrpcServer;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let http_srv = HttpServer::new(":8000");
let grpc_srv = GrpcServer::new(":9000");

let app = App::builder()
.name("my-service")
.version("v1.0.0")
.server(http_srv)
.server(grpc_srv)
.on_start(|| async {
tracing::info!("service started");
Ok(())
})
.on_stop(|| async {
tracing::info!("service stopped");
Ok(())
})
.build()?;

app.run().await?; // blocks until SIGTERM/SIGINT
Ok(())
}

Middleware

1
2
3
4
5
6
7
8
9
10
use tower::ServiceBuilder;
use ecat_middleware::{RecoveryLayer, TracingLayer, LoggingLayer, TimeoutLayer};
use std::time::Duration;

let layer = ServiceBuilder::new()
.layer(RecoveryLayer)
.layer(TracingLayer)
.layer(LoggingLayer)
.layer(TimeoutLayer::new(Duration::from_secs(30)))
.layer(SecurityLayer::new());

Error Handling

1
2
3
4
5
6
7
8
9
10
11
12
use ecat_errors::{Error, ErrorCode};

fn get_user(id: u64) -> Result<User, Error> {
if id == 0 {
return Err(Error::new(
ErrorCode::InvalidArgument,
"bad_request",
"user id must be positive",
));
}
// ...
}

Implementation Phases

Phase Status Content
Phase 1 ✅ Done Project skeleton, protos, errors, metadata, encoding, logging
Phase 2 ✅ Done Transport layer (HTTP + gRPC)
Phase 3 ✅ Done Middleware system (Recovery/Tracing/Logging/Timeout)
Phase 4 ✅ Done App lifecycle management
Phase 5 ✅ Done Registry, Config, Metrics
Phase 5.5 ✅ Done Data access layer (traits + sqlx backends)
Phase 6 ✅ Done CLI toolchain (new/proto/run/build)
Phase 7 ✅ Done README, examples (helloworld), design docs
Phase 8 ✅ Done Attack detection integration (security-rust, ecat-security)

Design Goals

# Goal Notes
1 Kratos alignment Keep Kratos’s API-first, pluggable, unified-abstraction philosophy
2 Idiomatic Rust Reuse tower::Service, trait generics, and zero-cost abstractions; no “Go in Rust”
3 Type safety Catch errors at compile time; every Protobuf definition is strongly typed
4 Pluggable Registry, Config, Logging, and Encoding are all abstracted behind traits
5 Complete toolchain The CLI covers project scaffolding, proto code generation, and dev runs
6 Performance first Zero-cost abstractions + async runtime
7 Observable tracing + OpenTelemetry + Prometheus out of the box

Technical Notes

Why tower::Service

tower::Service is the Rust async ecosystem’s equivalent of http.Handler. Both axum and tonic are built on tower, so e-cat needs no custom middleware trait — shipping tower::Layer implementations directly gives you the same effect as Kratos middleware, with zero adapter overhead.

Why a Cargo Workspace

It matches Kratos’s modular design. Every ecat-* crate is versioned and compiled independently, and users pull in only what they need. Core crates keep their dependencies minimal, while contrib crates provide optional integrations.

Why prost (and not protobuf-rs)

prost is the most widely used protobuf implementation in the Rust community; it generates type-safe code at compile time and integrates deeply with tonic.

Design Docs

License

MIT