-
Notifications
You must be signed in to change notification settings - Fork 204
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
tarpc is now instrumented with tracing primitives extended with OpenTelemetry traces. Using a compatible tracing-opentelemetry subscriber like Jaeger, each RPC can be traced through the client, server, amd other dependencies downstream of the server. Even for applications not connected to a distributed tracing collector, the instrumentation can also be ingested by regular loggers like env_logger. # Breaking Changes ## Logging Logged events are now structured using tracing. For applications using a logger and not a tracing subscriber, these logs may look different or contain information in a less consumable manner. The easiest solution is to add a tracing subscriber that logs to stdout, such as tracing_subscriber::fmt. ## Context - Context no longer has parent_span, which was actually never needed, because the context sent in an RPC is inherently the parent context. For purposes of distributed tracing, the client side of the RPC has all necessary information to link the span to its parent; the server side need do nothing more than export the (trace ID, span ID) tuple. - Context has a new field, SamplingDecision, which has two variants, Sampled and Unsampled. This field can be used by downstream systems to determine whether a trace needs to be exported. If the parent span is sampled, the expectation is that all child spans be exported, as well; to do otherwise could result in lossy traces being exported. Note that if an Openetelemetry tracing subscriber is not installed, the fallback context will still be used, but the Context's sampling decision will always be inherited by the parent Context's sampling decision. - Context::scope has been removed. Context propagation is now done via tracing's task-local spans. Spans can be propagated across tasks via Span::in_scope. When a service receives a request, it attaches an Opentelemetry context to the local Span created before request handling, and this context contains the request deadline. This span-local deadline is retrieved by Context::current, but it cannot be modified so that future Context::current calls contain a different deadline. However, the deadline in the context passed into an RPC call will override it, so users can retrieve the current context and then modify the deadline field, as has been historically possible. - Context propgation precedence changes: when an RPC is initiated, the current Span's Opentelemetry context takes precedence over the trace context passed into the RPC method. If there is no current Span, then the trace context argument is used as it has been historically. Note that Opentelemetry context propagation requires an Opentelemetry tracing subscriber to be installed. ## Server - The server::Channel trait now has an additional required associated type and method which returns the underlying transport. This makes it more ergonomic for users to retrieve transport-specific information, like IP Address. BaseChannel implements Channel::transport by returning the underlying transport, and channel decorators like Throttler just delegate to the Channel::transport method of the wrapped channel. # References [1] https://github.com/tokio-rs/tracing [2] https://opentelemetry.io [3] https://github.com/open-telemetry/opentelemetry-rust/tree/main/opentelemetry-jaeger [4] https://github.com/env-logger-rs/env_logger
- Loading branch information
Showing
23 changed files
with
865 additions
and
523 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,11 @@ | ||
[workspace] | ||
resolver = "2" | ||
|
||
members = [ | ||
"example-service", | ||
"tarpc", | ||
"plugins", | ||
] | ||
|
||
[profile.dev] | ||
split-debuginfo = "unpacked" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,57 +4,49 @@ | |
// license that can be found in the LICENSE file or at | ||
// https://opensource.org/licenses/MIT. | ||
|
||
use clap::{App, Arg}; | ||
use std::{io, net::SocketAddr}; | ||
use clap::Clap; | ||
use service::{init_tracing, WorldClient}; | ||
use std::{net::SocketAddr, time::Duration}; | ||
use tarpc::{client, context, tokio_serde::formats::Json}; | ||
use tokio::time::sleep; | ||
use tracing::Instrument; | ||
|
||
#[derive(Clap)] | ||
struct Flags { | ||
/// Sets the server address to connect to. | ||
#[clap(long)] | ||
server_addr: SocketAddr, | ||
/// Sets the name to say hello to. | ||
#[clap(long)] | ||
name: String, | ||
} | ||
|
||
#[tokio::main] | ||
async fn main() -> io::Result<()> { | ||
env_logger::init(); | ||
|
||
let flags = App::new("Hello Client") | ||
.version("0.1") | ||
.author("Tim <[email protected]>") | ||
.about("Say hello!") | ||
.arg( | ||
Arg::with_name("server_addr") | ||
.long("server_addr") | ||
.value_name("ADDRESS") | ||
.help("Sets the server address to connect to.") | ||
.required(true) | ||
.takes_value(true), | ||
) | ||
.arg( | ||
Arg::with_name("name") | ||
.short("n") | ||
.long("name") | ||
.value_name("STRING") | ||
.help("Sets the name to say hello to.") | ||
.required(true) | ||
.takes_value(true), | ||
) | ||
.get_matches(); | ||
|
||
let server_addr = flags.value_of("server_addr").unwrap(); | ||
let server_addr = server_addr | ||
.parse::<SocketAddr>() | ||
.unwrap_or_else(|e| panic!(r#"--server_addr value "{}" invalid: {}"#, server_addr, e)); | ||
|
||
let name = flags.value_of("name").unwrap().into(); | ||
|
||
let mut transport = tarpc::serde_transport::tcp::connect(server_addr, Json::default); | ||
transport.config_mut().max_frame_length(usize::MAX); | ||
async fn main() -> anyhow::Result<()> { | ||
let flags = Flags::parse(); | ||
let _uninstall = init_tracing("Tarpc Example Client")?; | ||
|
||
let transport = tarpc::serde_transport::tcp::connect(flags.server_addr, Json::default); | ||
|
||
// WorldClient is generated by the service attribute. It has a constructor `new` that takes a | ||
// config and any Transport as input. | ||
let client = service::WorldClient::new(client::Config::default(), transport.await?).spawn()?; | ||
|
||
// The client has an RPC method for each RPC defined in the annotated trait. It takes the same | ||
// args as defined, with the addition of a Context, which is always the first arg. The Context | ||
// specifies a deadline and trace information which can be helpful in debugging requests. | ||
let hello = client.hello(context::current(), name).await?; | ||
|
||
println!("{}", hello); | ||
let client = WorldClient::new(client::Config::default(), transport.await?).spawn()?; | ||
|
||
let hello = async move { | ||
// Send the request twice, just to be safe! ;) | ||
tokio::select! { | ||
hello1 = client.hello(context::current(), format!("{}1", flags.name)) => { hello1 } | ||
hello2 = client.hello(context::current(), format!("{}2", flags.name)) => { hello2 } | ||
} | ||
} | ||
.instrument(tracing::info_span!("Two Hellos")) | ||
.await; | ||
|
||
tracing::info!("{:?}", hello); | ||
|
||
// Let the background span processor finish. | ||
sleep(Duration::from_micros(1)).await; | ||
opentelemetry::global::shutdown_tracer_provider(); | ||
|
||
Ok(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,18 +4,30 @@ | |
// license that can be found in the LICENSE file or at | ||
// https://opensource.org/licenses/MIT. | ||
|
||
use clap::{App, Arg}; | ||
use clap::Clap; | ||
use futures::{future, prelude::*}; | ||
use service::World; | ||
use rand::{ | ||
distributions::{Distribution, Uniform}, | ||
thread_rng, | ||
}; | ||
use service::{init_tracing, World}; | ||
use std::{ | ||
io, | ||
net::{IpAddr, SocketAddr}, | ||
time::Duration, | ||
}; | ||
use tarpc::{ | ||
context, | ||
server::{self, Channel, Incoming}, | ||
tokio_serde::formats::Json, | ||
}; | ||
use tokio::time; | ||
|
||
#[derive(Clap)] | ||
struct Flags { | ||
/// Sets the port number to listen on. | ||
#[clap(long)] | ||
port: u16, | ||
} | ||
|
||
// This is the type that implements the generated World trait. It is the business logic | ||
// and is used to start the server. | ||
|
@@ -25,35 +37,19 @@ struct HelloServer(SocketAddr); | |
#[tarpc::server] | ||
impl World for HelloServer { | ||
async fn hello(self, _: context::Context, name: String) -> String { | ||
let sleep_time = | ||
Duration::from_millis(Uniform::new_inclusive(1, 10).sample(&mut thread_rng())); | ||
time::sleep(sleep_time).await; | ||
format!("Hello, {}! You are connected from {:?}.", name, self.0) | ||
} | ||
} | ||
|
||
#[tokio::main] | ||
async fn main() -> io::Result<()> { | ||
env_logger::init(); | ||
|
||
let flags = App::new("Hello Server") | ||
.version("0.1") | ||
.author("Tim <[email protected]>") | ||
.about("Say hello!") | ||
.arg( | ||
Arg::with_name("port") | ||
.short("p") | ||
.long("port") | ||
.value_name("NUMBER") | ||
.help("Sets the port number to listen on") | ||
.required(true) | ||
.takes_value(true), | ||
) | ||
.get_matches(); | ||
|
||
let port = flags.value_of("port").unwrap(); | ||
let port = port | ||
.parse() | ||
.unwrap_or_else(|e| panic!(r#"--port value "{}" invalid: {}"#, port, e)); | ||
async fn main() -> anyhow::Result<()> { | ||
let flags = Flags::parse(); | ||
let _uninstall = init_tracing("Tarpc Example Server")?; | ||
|
||
let server_addr = (IpAddr::from([0, 0, 0, 0]), port); | ||
let server_addr = (IpAddr::from([0, 0, 0, 0]), flags.port); | ||
|
||
// JSON transport is provided by the json_transport tarpc module. It makes it easy | ||
// to start up a serde-powered json serialization strategy over TCP. | ||
|
@@ -64,12 +60,12 @@ async fn main() -> io::Result<()> { | |
.filter_map(|r| future::ready(r.ok())) | ||
.map(server::BaseChannel::with_defaults) | ||
// Limit channels to 1 per IP. | ||
.max_channels_per_key(1, |t| t.as_ref().peer_addr().unwrap().ip()) | ||
.max_channels_per_key(1, |t| t.transport().peer_addr().unwrap().ip()) | ||
// serve is generated by the service attribute. It takes as input any type implementing | ||
// the generated World trait. | ||
.map(|channel| { | ||
let server = HelloServer(channel.as_ref().as_ref().peer_addr().unwrap()); | ||
channel.requests().execute(server.serve()) | ||
let server = HelloServer(channel.transport().peer_addr().unwrap()); | ||
channel.execute(server.serve()) | ||
}) | ||
// Max 10 channels. | ||
.buffer_unordered(10) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.