Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions tonic-xds/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ fastrand = "2"
tokio-stream = "0.1"
backoff = "0.4"
shared_http_body = "0.1"
tonic-prost = { version = "0.14", optional = true }

[lints]
workspace = true
Expand All @@ -54,3 +55,18 @@ tokio = { version = "1", features = ["rt-multi-thread", "macros", "net"] }
tonic = { version = "0.14", features = [ "server", "channel", "tls-ring" ] }
tonic-prost = "0.14"
tonic-prost-build = "0.14"
async-stream = "0.3"

[features]
testutil = ["dep:tonic-prost"]

[[example]]
name = "channel"
required-features = ["testutil"]

[[example]]
name = "greeter_server"
required-features = ["testutil"]

[[example]]
name = "xds_server"
69 changes: 69 additions & 0 deletions tonic-xds/examples/channel.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
//! Example: send gRPC requests through an xDS-aware channel.
//!
//! Builds an xDS channel, then sends HelloRequest RPCs through it in a loop.
//! The channel discovers endpoints via the xDS management server and
//! load-balances across them.
//!
//! # Quick start
//!
//! Run all three examples (greeter backend, xDS server, this client) together:
//!
//! ```sh
//! ./tonic-xds/examples/run_xds_example.sh
//! ```
//!
//! # Running individually
//!
//! ```sh
//! # Terminal 1: greeter backend
//! PORT=50051 cargo run -p tonic-xds --example greeter_server --features testutil
//!
//! # Terminal 2: xDS control plane
//! cargo run -p tonic-xds --example xds_server
//!
//! # Terminal 3: xDS client
//! GRPC_XDS_BOOTSTRAP_CONFIG='{"xds_servers":[{"server_uri":"http://localhost:18000"}],"node":{"id":"test"}}' \
//! cargo run -p tonic-xds --example channel --features testutil
//! ```
//!
//! # Configuration
//!
//! - `GRPC_XDS_BOOTSTRAP` — path to a bootstrap JSON file, **or**
//! - `GRPC_XDS_BOOTSTRAP_CONFIG` — inline bootstrap JSON
//! - `XDS_TARGET` — xDS target URI (default: `xds:///my-service`)

use tonic_xds::testutil::proto::helloworld::{HelloRequest, greeter_client::GreeterClient};
use tonic_xds::{XdsChannelBuilder, XdsChannelConfig, XdsUri};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let target_str = std::env::var("XDS_TARGET").unwrap_or_else(|_| "xds:///my-service".into());
let target = XdsUri::parse(&target_str)?;

println!("Building xDS channel for target: {target_str}");

let channel = XdsChannelBuilder::new(XdsChannelConfig::new(target)).build_grpc_channel()?;

let mut client = GreeterClient::new(channel);

println!("Channel built. Sending requests (Ctrl-C to stop)...\n");

for i in 1.. {
let request = HelloRequest {
name: format!("request-{i}"),
};

match client.say_hello(request).await {
Ok(response) => {
println!("[{i}] Response: {}", response.into_inner().message);
}
Err(status) => {
eprintln!("[{i}] Error: {status}");
}
}

tokio::time::sleep(tokio::time::Duration::from_secs(5)).await;
}

Ok(())
}
62 changes: 62 additions & 0 deletions tonic-xds/examples/greeter_server.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
//! Example: standalone gRPC greeter server for testing xDS.
//!
//! Starts a greeter backend on a given port. Used together with the
//! `xds_server` and `channel` examples.
//!
//! # Quick start
//!
//! ```sh
//! ./tonic-xds/examples/run_xds_example.sh
//! ```
//!
//! # Running individually
//!
//! ```sh
//! # Start on port 50051 (default):
//! cargo run -p tonic-xds --example greeter_server --features testutil
//!
//! # Custom port:
//! PORT=50052 cargo run -p tonic-xds --example greeter_server --features testutil
//! ```

use tonic::transport::Server;
use tonic::{Request, Response, Status};
use tonic_xds::testutil::proto::helloworld::{
HelloReply, HelloRequest,
greeter_server::{Greeter, GreeterServer},
};

struct MyGreeter {
addr: String,
}

#[tonic::async_trait]
impl Greeter for MyGreeter {
async fn say_hello(
&self,
request: Request<HelloRequest>,
) -> Result<Response<HelloReply>, Status> {
let name = request.into_inner().name;
println!("Received request: name={name}");
Ok(Response::new(HelloReply {
message: format!("Hello {name} from {}", self.addr),
}))
}
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let port = std::env::var("PORT").unwrap_or_else(|_| "50051".to_string());
let addr: std::net::SocketAddr = format!("0.0.0.0:{port}").parse()?;

println!("Greeter server listening on {addr}");

Server::builder()
.add_service(GreeterServer::new(MyGreeter {
addr: addr.to_string(),
}))
.serve(addr)
.await?;

Ok(())
}
29 changes: 29 additions & 0 deletions tonic-xds/examples/run_xds_example.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/usr/bin/env bash
# Run the tonic-xds example: xDS server + greeter backend + channel client.
set -euo pipefail

prefix() {
local tag="$1"
sed -u "s/^/[$tag] /"
}

cleanup() {
echo "Shutting down..."
kill "$GREETER_PID" "$XDS_PID" 2>/dev/null || true
}
trap cleanup EXIT

# 1. Start greeter backend
PORT=50051 cargo run -p tonic-xds --example greeter_server --features testutil 2>&1 | prefix "greeter" &
GREETER_PID=$!

# 2. Start xDS control plane
cargo run -p tonic-xds --example xds_server 2>&1 | prefix "xds" &
XDS_PID=$!

# Wait for servers to be ready
sleep 2

# 3. Run xDS-aware client
GRPC_XDS_BOOTSTRAP_CONFIG='{"xds_servers":[{"server_uri":"http://localhost:18000"}],"node":{"id":"test"}}' \
cargo run -p tonic-xds --example channel --features testutil 2>&1 | prefix "client"
Loading
Loading