Skip to content

pwalski/tchannel_rs

Repository files navigation

build status License: MIT crate documentation

tchannel_rs

TChannel is a network multiplexing and framing RPC protocol created by Uber (protocol specs).

Overview

Features of TChannel protocol implemented so far:

  • A request/response model,
  • Multiplexing multiple requests across the same TCP socket,
  • Out-of-order responses,
  • Streaming requests and responses,
  • Checksums of frame args (only None),
  • Transport of arbitrary payloads:
    • Thrift
    • SThrift (streaming Thrift)
    • JSON
    • HTTP
    • Raw
  • Routing mesh
  • Tracing

Other TODOs:

  • Request response TTL
  • Cancel request
  • Claim requests
  • Use Tower?
  • Implement Serde Serialize/Deserialize for Message types
  • Convert Thrift related Makefile to build.rs when implementing Thrift payloads
  • Proper tests (right now only few happy paths)
  • Make request handlers generic (no associated types)

The goal of the project is to provide a similar API to Java TChannel implementation which is why both connection pools and server task handler are hidden from user.

Disclaimer

It is an unofficial implementation of TChannel protocol. The project is used to learn Rust and it still has some missing features, so it will not go out of 0.0.x before implementing them and a proper testing. Future 0.0.x releases may include API breaking changes.

Examples

use tchannel_rs::{Config, TChannel, TResult};
use tchannel_rs::handler::{HandlerResult, RequestHandler};
use tchannel_rs::messages::{MessageChannel, RawMessage};

#[tokio::main]
async fn main() -> TResult<()> {
    // Server
    let mut tserver = TChannel::new(Config::default())?;
    let subchannel = tserver.subchannel("service").await?;
    subchannel.register("endpoint", Handler {}).await?;
    tserver.start_server()?;

    // Client
    let tclient = TChannel::new(Config::default())?;
    let subchannel = tclient.subchannel("service").await?;
    let request = RawMessage::new("endpoint".into(), "header".into(), "req body".into());
    let response = subchannel.send(request, "127.0.0.1:8888").await.unwrap();

    // Server shutdown
    tserver.shutdown_server();

    assert_eq!("header", response.header());
    assert_eq!("res body".as_bytes(), response.body().as_ref());
    Ok(())
}

#[derive(Debug)]
struct Handler {}
impl RequestHandler for Handler {
    type REQ = RawMessage;
    type RES = RawMessage;
    fn handle(&mut self, request: Self::REQ) -> HandlerResult<Self::RES> {
        Ok(RawMessage::new(request.endpoint().clone(), request.header().clone(), "res body".into()))
    }
}

To run above example following dependencies are required:

tchannel_rs = *
tokio =  { version = "^1", features = ["macros"] }
env_logger = "^0" # log impl to print tchannel_rs logs

Build

Examples Subproject

Sample server:

RUST_LOG=DEBUG cargo run --example server

Sample client:

RUST_LOG=DEBUG cargo run --example client

Sample tchannel-java server (to check Rust client compatibility):

# with local Maven/JDK
mvn -f examples-jvm-server package exec:exec -Pserver
# or with Docker
docker-compose --project-directory examples-jvm-server up
# or with Podman (no podman-compose because of network issues)
podman build --file examples-jvm-server/Dockerfile
podman run -p 8888:8888 localhost/examples-jvm-server_tchannel-jvm-server

Update of README.md

cargo install cargo-readme
cargo readme > README.md

License: MIT

About

Network multiplexing and framing RPC protocol

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages