mirror of
https://github.com/OMGeeky/tarpc.git
synced 2026-02-23 15:49:54 +01:00
Make server methods more composable.
-- Connection Limits The problem with having ConnectionFilter default-enabled is elaborated on in https://github.com/google/tarpc/issues/217. The gist of it is not all servers want a policy based on `SocketAddr`. This PR allows customizing the behavior of ConnectionFilter, at the cost of not having it enabled by default. However, enabling it is as simple as one line: incoming.max_channels_per_key(10, ip_addr) The second argument is a key function that takes the user-chosen transport and returns some hashable, equatable, cloneable key. In the above example, it returns an `IpAddr`. This also allows the `Transport` trait to have the addr fns removed, which means it has become simply an alias for `Stream + Sink`. -- Per-Channel Request Throttling With respect to Channel's throttling behavior, the same argument applies. There isn't a one size fits all solution to throttling requests, and the policy applied by tarpc is just one of potentially many solutions. As such, `Channel` is now a trait that offers a few combinators, one of which is throttling: channel.max_concurrent_requests(10).respond_with(serve(Server)) This functionality is also available on the existing `Handler` trait, which applies it to all incoming channels and can be used in tandem with connection limits: incoming .max_channels_per_key(10, ip_addr) .max_concurrent_requests_per_channel(10).respond_with(serve(Server)) -- Global Request Throttling I've entirely removed the overall request limit enforced across all channels. This functionality is easily gotten back via [`StreamExt::buffer_unordered`](https://rust-lang-nursery.github.io/futures-api-docs/0.3.0-alpha.1/futures/stream/trait.StreamExt.html#method.buffer_unordered), with the difference being that the previous behavior allowed you to spawn channels onto different threads, whereas `buffer_unordered ` means the `Channels` are handled on a single thread (the per-request handlers are still spawned). Considering the existing options, I don't believe that the benefit provided by this functionality held its own.
This commit is contained in:
@@ -13,10 +13,9 @@ readme = "../README.md"
|
||||
description = "A JSON-based transport for tarpc services."
|
||||
|
||||
[dependencies]
|
||||
futures-preview = { version = "0.3.0-alpha.16", features = ["compat"] }
|
||||
futures-preview = { version = "0.3.0-alpha.17", features = ["compat"] }
|
||||
futures_legacy = { version = "0.1", package = "futures" }
|
||||
pin-utils = "0.1.0-alpha.4"
|
||||
rpc = { package = "tarpc-lib", version = "0.6", path = "../rpc", features = ["serde1"] }
|
||||
serde = "1.0"
|
||||
serde_json = "1.0"
|
||||
tokio = "0.1"
|
||||
@@ -27,9 +26,10 @@ tokio-tcp = "0.1"
|
||||
[dev-dependencies]
|
||||
env_logger = "0.6"
|
||||
humantime = "1.0"
|
||||
libtest = "0.0.1"
|
||||
log = "0.4"
|
||||
rand = "0.6"
|
||||
rand = "0.7"
|
||||
rand_distr = "0.2"
|
||||
rpc = { package = "tarpc-lib", version = "0.6", path = "../rpc", features = ["serde1"] }
|
||||
tokio = "0.1"
|
||||
tokio-executor = "0.1"
|
||||
tokio-reactor = "0.1"
|
||||
|
||||
@@ -67,7 +67,7 @@ where
|
||||
S: AsyncWrite,
|
||||
SinkItem: Serialize,
|
||||
{
|
||||
type SinkError = io::Error;
|
||||
type Error = io::Error;
|
||||
|
||||
fn start_send(self: Pin<&mut Self>, item: SinkItem) -> io::Result<()> {
|
||||
self.inner()
|
||||
@@ -88,7 +88,9 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn convert<E: Into<Box<Error + Send + Sync>>>(poll: Poll<Result<(), E>>) -> Poll<io::Result<()>> {
|
||||
fn convert<E: Into<Box<dyn Error + Send + Sync>>>(
|
||||
poll: Poll<Result<(), E>>,
|
||||
) -> Poll<io::Result<()>> {
|
||||
match poll {
|
||||
Poll::Pending => Poll::Pending,
|
||||
Poll::Ready(Ok(())) => Poll::Ready(Ok(())),
|
||||
@@ -96,15 +98,9 @@ fn convert<E: Into<Box<Error + Send + Sync>>>(poll: Poll<Result<(), E>>) -> Poll
|
||||
}
|
||||
}
|
||||
|
||||
impl<Item, SinkItem> rpc::Transport for Transport<TcpStream, Item, SinkItem>
|
||||
where
|
||||
Item: for<'de> Deserialize<'de>,
|
||||
SinkItem: Serialize,
|
||||
{
|
||||
type Item = Item;
|
||||
type SinkItem = SinkItem;
|
||||
|
||||
fn peer_addr(&self) -> io::Result<SocketAddr> {
|
||||
impl<Item, SinkItem> Transport<TcpStream, Item, SinkItem> {
|
||||
/// Returns the peer address of the underlying TcpStream.
|
||||
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
|
||||
self.inner
|
||||
.get_ref()
|
||||
.get_ref()
|
||||
@@ -113,7 +109,8 @@ where
|
||||
.peer_addr()
|
||||
}
|
||||
|
||||
fn local_addr(&self) -> io::Result<SocketAddr> {
|
||||
/// Returns the local address of the underlying TcpStream.
|
||||
pub fn local_addr(&self) -> io::Result<SocketAddr> {
|
||||
self.inner
|
||||
.get_ref()
|
||||
.get_ref()
|
||||
|
||||
@@ -8,8 +8,10 @@
|
||||
|
||||
#![feature(test, integer_atomics, async_await)]
|
||||
|
||||
extern crate test;
|
||||
|
||||
use futures::{compat::Executor01CompatExt, prelude::*};
|
||||
use libtest::stats::Stats;
|
||||
use test::stats::Stats;
|
||||
use rpc::{
|
||||
client, context,
|
||||
server::{Handler, Server},
|
||||
@@ -20,8 +22,9 @@ use std::{
|
||||
};
|
||||
|
||||
async fn bench() -> io::Result<()> {
|
||||
let listener = tarpc_json_transport::listen(&"0.0.0.0:0".parse().unwrap())?;
|
||||
let addr = listener.local_addr();
|
||||
let listener = tarpc_json_transport::listen(&"0.0.0.0:0".parse().unwrap())?
|
||||
.filter_map(|r| future::ready(r.ok()));
|
||||
let addr = listener.get_ref().local_addr();
|
||||
|
||||
tokio_executor::spawn(
|
||||
Server::<u32, u32>::default()
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
//! Tests client/server control flow.
|
||||
|
||||
#![feature(async_await)]
|
||||
#![feature(async_closure)]
|
||||
|
||||
use futures::{
|
||||
compat::{Executor01CompatExt, Future01CompatExt},
|
||||
@@ -14,8 +15,11 @@ use futures::{
|
||||
stream::FuturesUnordered,
|
||||
};
|
||||
use log::{info, trace};
|
||||
use rand::distributions::{Distribution, Normal};
|
||||
use rpc::{client, context, server::Server};
|
||||
use rand_distr::{Distribution, Normal};
|
||||
use rpc::{
|
||||
client, context,
|
||||
server::{Channel, Server},
|
||||
};
|
||||
use std::{
|
||||
io,
|
||||
time::{Duration, Instant, SystemTime},
|
||||
@@ -34,18 +38,14 @@ impl AsDuration for SystemTime {
|
||||
}
|
||||
|
||||
async fn run() -> io::Result<()> {
|
||||
let listener = tarpc_json_transport::listen(&"0.0.0.0:0".parse().unwrap())?;
|
||||
let addr = listener.local_addr();
|
||||
let listener = tarpc_json_transport::listen(&"0.0.0.0:0".parse().unwrap())?
|
||||
.filter_map(|r| future::ready(r.ok()));
|
||||
let addr = listener.get_ref().local_addr();
|
||||
let server = Server::<String, String>::default()
|
||||
.incoming(listener)
|
||||
.take(1)
|
||||
.for_each(async move |channel| {
|
||||
let channel = if let Ok(channel) = channel {
|
||||
channel
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
let client_addr = *channel.client_addr();
|
||||
let client_addr = channel.get_ref().peer_addr().unwrap();
|
||||
let handler = channel.respond_with(move |ctx, request| {
|
||||
// Sleep for a time sampled from a normal distribution with:
|
||||
// - mean: 1/2 the deadline.
|
||||
@@ -53,7 +53,7 @@ async fn run() -> io::Result<()> {
|
||||
let deadline: Duration = ctx.deadline.as_duration();
|
||||
let deadline_millis = deadline.as_secs() * 1000 + deadline.subsec_millis() as u64;
|
||||
let distribution =
|
||||
Normal::new(deadline_millis as f64 / 2., deadline_millis as f64 / 2.);
|
||||
Normal::new(deadline_millis as f64 / 2., deadline_millis as f64 / 2.).unwrap();
|
||||
let delay_millis = distribution.sample(&mut rand::thread_rng()).max(0.);
|
||||
let delay = Duration::from_millis(delay_millis as u64);
|
||||
|
||||
@@ -79,20 +79,16 @@ async fn run() -> io::Result<()> {
|
||||
let client = client::new::<String, String, _>(client::Config::default(), conn).await?;
|
||||
|
||||
// Proxy service
|
||||
let listener = tarpc_json_transport::listen(&"0.0.0.0:0".parse().unwrap())?;
|
||||
let addr = listener.local_addr();
|
||||
let listener = tarpc_json_transport::listen(&"0.0.0.0:0".parse().unwrap())?
|
||||
.filter_map(|r| future::ready(r.ok()));
|
||||
let addr = listener.get_ref().local_addr();
|
||||
let proxy_server = Server::<String, String>::default()
|
||||
.incoming(listener)
|
||||
.take(1)
|
||||
.for_each(move |channel| {
|
||||
let client = client.clone();
|
||||
async move {
|
||||
let channel = if let Ok(channel) = channel {
|
||||
channel
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
let client_addr = *channel.client_addr();
|
||||
let client_addr = channel.get_ref().peer_addr().unwrap();
|
||||
let handler = channel.respond_with(move |ctx, request| {
|
||||
trace!("[{}/{}] Proxying request.", ctx.trace_id(), client_addr);
|
||||
let mut client = client.clone();
|
||||
|
||||
@@ -7,14 +7,18 @@
|
||||
//! Tests client/server control flow.
|
||||
|
||||
#![feature(async_await)]
|
||||
#![feature(async_closure)]
|
||||
|
||||
use futures::{
|
||||
compat::{Executor01CompatExt, Future01CompatExt},
|
||||
prelude::*,
|
||||
};
|
||||
use log::{error, info, trace};
|
||||
use rand::distributions::{Distribution, Normal};
|
||||
use rpc::{client, context, server::Server};
|
||||
use rand_distr::{Distribution, Normal};
|
||||
use rpc::{
|
||||
client, context,
|
||||
server::{Channel, Server},
|
||||
};
|
||||
use std::{
|
||||
io,
|
||||
time::{Duration, Instant, SystemTime},
|
||||
@@ -33,18 +37,14 @@ impl AsDuration for SystemTime {
|
||||
}
|
||||
|
||||
async fn run() -> io::Result<()> {
|
||||
let listener = tarpc_json_transport::listen(&"0.0.0.0:0".parse().unwrap())?;
|
||||
let addr = listener.local_addr();
|
||||
let listener = tarpc_json_transport::listen(&"0.0.0.0:0".parse().unwrap())?
|
||||
.filter_map(|r| future::ready(r.ok()));
|
||||
let addr = listener.get_ref().local_addr();
|
||||
let server = Server::<String, String>::default()
|
||||
.incoming(listener)
|
||||
.take(1)
|
||||
.for_each(async move |channel| {
|
||||
let channel = if let Ok(channel) = channel {
|
||||
channel
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
let client_addr = *channel.client_addr();
|
||||
let client_addr = channel.get_ref().peer_addr().unwrap();
|
||||
let handler = channel.respond_with(move |ctx, request| {
|
||||
// Sleep for a time sampled from a normal distribution with:
|
||||
// - mean: 1/2 the deadline.
|
||||
@@ -52,7 +52,7 @@ async fn run() -> io::Result<()> {
|
||||
let deadline: Duration = ctx.deadline.as_duration();
|
||||
let deadline_millis = deadline.as_secs() * 1000 + deadline.subsec_millis() as u64;
|
||||
let distribution =
|
||||
Normal::new(deadline_millis as f64 / 2., deadline_millis as f64 / 2.);
|
||||
Normal::new(deadline_millis as f64 / 2., deadline_millis as f64 / 2.).unwrap();
|
||||
let delay_millis = distribution.sample(&mut rand::thread_rng()).max(0.);
|
||||
let delay = Duration::from_millis(delay_millis as u64);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user