Add basic load balancing
This commit is contained in:
@@ -7,9 +7,13 @@ pub mod hello_world {
|
|||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let origin = http::Uri::from_static("http://[::1]:50051");
|
let origin = vec![
|
||||||
|
http::Uri::from_static("http://[::1]:50051"),
|
||||||
|
http::Uri::from_static("http://[::1]:50051"),
|
||||||
|
http::Uri::from_static("http://[::1]:50051"),
|
||||||
|
];
|
||||||
|
|
||||||
let svc = Channel::builder().build(origin)?;
|
let svc = Channel::builder().balance_list(origin)?;
|
||||||
|
|
||||||
let mut client = hello_world::GreeterClient::new(svc);
|
let mut client = hello_world::GreeterClient::new(svc);
|
||||||
|
|
||||||
|
|||||||
+12
-3
@@ -18,7 +18,7 @@ tower-service = "=0.3.0-alpha.1"
|
|||||||
tokio-codec = "=0.2.0-alpha.4"
|
tokio-codec = "=0.2.0-alpha.4"
|
||||||
async-stream = { git = "https://github.com/tokio-rs/async-stream" }
|
async-stream = { git = "https://github.com/tokio-rs/async-stream" }
|
||||||
http-body = "0.2.0-alpha.1"
|
http-body = "0.2.0-alpha.1"
|
||||||
pin-project = "0.4.0-alpha.2"
|
pin-project = "0.4.0-alpha.7"
|
||||||
|
|
||||||
# optional
|
# optional
|
||||||
hyper = { git = "https://github.com/hyperium/hyper", optional = true}
|
hyper = { git = "https://github.com/hyperium/hyper", optional = true}
|
||||||
@@ -26,6 +26,9 @@ tokio = { version = "=0.2.0-alpha.4", default-features = false, features = ["tcp
|
|||||||
tower-make = "=0.1.0-alpha.2"
|
tower-make = "=0.1.0-alpha.2"
|
||||||
tower-reconnect = { git = "https://github.com/tower-rs/tower", branch = "lucio/update-reconnect-buffer", optional = true }
|
tower-reconnect = { git = "https://github.com/tower-rs/tower", branch = "lucio/update-reconnect-buffer", optional = true }
|
||||||
tower-buffer = { git = "https://github.com/tower-rs/tower", branch = "lucio/update-reconnect-buffer", optional = true }
|
tower-buffer = { git = "https://github.com/tower-rs/tower", branch = "lucio/update-reconnect-buffer", optional = true }
|
||||||
|
tower-balance = { git = "https://github.com/tower-rs/tower", branch = "lucio/update-balance", optional = true }
|
||||||
|
tower-load = { git = "https://github.com/tower-rs/tower", branch = "lucio/update-balance", optional = true }
|
||||||
|
tower-discover = { git = "https://github.com/tower-rs/tower", branch = "lucio/update-balance", optional = true }
|
||||||
|
|
||||||
# openssl
|
# openssl
|
||||||
tokio-openssl = { version = "=0.4.0-alpha.4", optional = true }
|
tokio-openssl = { version = "=0.4.0-alpha.4", optional = true }
|
||||||
@@ -38,10 +41,16 @@ tokio-rustls = { version = "0.12.0-alpha.2", optional = true }
|
|||||||
default = ["transport"]
|
default = ["transport"]
|
||||||
transport = [
|
transport = [
|
||||||
"hyper",
|
"hyper",
|
||||||
"tower-reconnect",
|
"tower",
|
||||||
"tower-buffer",
|
|
||||||
"tokio",
|
"tokio",
|
||||||
"openssl-1",
|
"openssl-1",
|
||||||
]
|
]
|
||||||
|
tower = [
|
||||||
|
"tower-reconnect",
|
||||||
|
"tower-buffer",
|
||||||
|
"tower-balance",
|
||||||
|
"tower-load",
|
||||||
|
"tower-discover"
|
||||||
|
]
|
||||||
openssl-1 = ["openssl", "tokio-openssl"]
|
openssl-1 = ["openssl", "tokio-openssl"]
|
||||||
rustls = ["tokio-rustls"]
|
rustls = ["tokio-rustls"]
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
use super::{add_origin::AddOrigin, connector::Connector};
|
||||||
|
use crate::body::BoxBody;
|
||||||
|
use http::{Request, Response, Uri};
|
||||||
|
use hyper::client::conn::Builder;
|
||||||
|
use hyper::client::service::Connect as HyperConnect;
|
||||||
|
use std::future::Future;
|
||||||
|
use std::pin::Pin;
|
||||||
|
use std::task::{Context, Poll};
|
||||||
|
use tower_load::Load;
|
||||||
|
use tower_reconnect::Reconnect;
|
||||||
|
use tower_service::Service;
|
||||||
|
|
||||||
|
pub struct Connection {
|
||||||
|
inner: AddOrigin<Reconnect<HyperConnect<Connector, BoxBody, Uri>, Uri>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Connection {
|
||||||
|
pub fn new(uri: Uri) -> Self {
|
||||||
|
let connector = Connector::new();
|
||||||
|
let settings = Builder::new().http2_only(true).clone();
|
||||||
|
let connect = HyperConnect::new(connector, settings);
|
||||||
|
let reconnect = Reconnect::new(connect, uri.clone());
|
||||||
|
let inner = AddOrigin::new(reconnect, uri);
|
||||||
|
|
||||||
|
Self { inner }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Service<Request<BoxBody>> for Connection {
|
||||||
|
type Response = Response<hyper::Body>;
|
||||||
|
type Error = crate::Error;
|
||||||
|
|
||||||
|
type Future =
|
||||||
|
Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send + 'static>>;
|
||||||
|
|
||||||
|
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||||
|
Service::poll_ready(&mut self.inner, cx).map_err(Into::into)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call(&mut self, req: Request<BoxBody>) -> Self::Future {
|
||||||
|
let fut = self.inner.call(req);
|
||||||
|
Box::pin(fut)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Load for Connection {
|
||||||
|
type Metric = usize;
|
||||||
|
|
||||||
|
fn load(&self) -> Self::Metric {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
use super::io::BoxedIo;
|
||||||
|
use http::Uri;
|
||||||
|
use hyper::client::connect::HttpConnector;
|
||||||
|
use std::future::Future;
|
||||||
|
use std::pin::Pin;
|
||||||
|
use std::task::{Context, Poll};
|
||||||
|
use tower_make::MakeConnection;
|
||||||
|
use tower_service::Service;
|
||||||
|
|
||||||
|
type ConnectFuture = <HttpConnector as MakeConnection<Uri>>::Future;
|
||||||
|
|
||||||
|
pub struct Connector {
|
||||||
|
http: HttpConnector,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Connector {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
http: HttpConnector::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Service<Uri> for Connector {
|
||||||
|
type Response = BoxedIo;
|
||||||
|
type Error = crate::Error;
|
||||||
|
|
||||||
|
type Future =
|
||||||
|
Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send + 'static>>;
|
||||||
|
|
||||||
|
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||||
|
MakeConnection::poll_ready(&mut self.http, cx).map_err(Into::into)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call(&mut self, uri: Uri) -> Self::Future {
|
||||||
|
let connect_fut = MakeConnection::make_connection(&mut self.http, uri);
|
||||||
|
|
||||||
|
Box::pin(connect(connect_fut))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn connect(connect: ConnectFuture) -> Result<BoxedIo, crate::Error> {
|
||||||
|
let io = connect.await?;
|
||||||
|
|
||||||
|
// TODO: build tls based on creds and features
|
||||||
|
|
||||||
|
Ok(BoxedIo::new(io))
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
use super::connect::Connection;
|
||||||
|
use http::Uri;
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
use std::task::{Context, Poll};
|
||||||
|
use tower_discover::{Change, Discover};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ServiceList {
|
||||||
|
list: VecDeque<Uri>,
|
||||||
|
i: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ServiceList {
|
||||||
|
pub fn new(list: Vec<Uri>) -> Self {
|
||||||
|
Self {
|
||||||
|
list: list.into(),
|
||||||
|
i: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Discover for ServiceList {
|
||||||
|
type Key = usize;
|
||||||
|
type Service = Connection;
|
||||||
|
type Error = hyper::Error;
|
||||||
|
|
||||||
|
fn poll(
|
||||||
|
&mut self,
|
||||||
|
_cx: &mut Context<'_>,
|
||||||
|
) -> Poll<Result<Change<Self::Key, Self::Service>, Self::Error>> {
|
||||||
|
match self.list.pop_front() {
|
||||||
|
Some(uri) => {
|
||||||
|
let i = self.i;
|
||||||
|
self.i += 1;
|
||||||
|
let service = Connection::new(uri);
|
||||||
|
Poll::Ready(Ok(Change::Insert(i, service)))
|
||||||
|
}
|
||||||
|
None => Poll::Pending,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
use std::io;
|
||||||
|
use std::pin::Pin;
|
||||||
|
use std::task::{Context, Poll};
|
||||||
|
use tokio::io::{AsyncRead, AsyncWrite};
|
||||||
|
|
||||||
|
pub(super) trait Io: AsyncRead + AsyncWrite + Send + Unpin + 'static {}
|
||||||
|
|
||||||
|
impl<T> Io for T where T: AsyncRead + AsyncWrite + Send + Unpin + 'static {}
|
||||||
|
|
||||||
|
pub struct BoxedIo(Pin<Box<dyn Io>>);
|
||||||
|
|
||||||
|
impl BoxedIo {
|
||||||
|
pub(super) fn new<I: Io>(io: I) -> Self {
|
||||||
|
BoxedIo(Box::pin(io))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsyncRead for BoxedIo {
|
||||||
|
fn poll_read(
|
||||||
|
mut self: Pin<&mut Self>,
|
||||||
|
cx: &mut Context<'_>,
|
||||||
|
buf: &mut [u8],
|
||||||
|
) -> Poll<io::Result<usize>> {
|
||||||
|
Pin::new(&mut self.0).poll_read(cx, buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsyncWrite for BoxedIo {
|
||||||
|
fn poll_write(
|
||||||
|
mut self: Pin<&mut Self>,
|
||||||
|
cx: &mut Context<'_>,
|
||||||
|
buf: &[u8],
|
||||||
|
) -> Poll<io::Result<usize>> {
|
||||||
|
Pin::new(&mut self.0).poll_write(cx, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||||
|
Pin::new(&mut self.0).poll_flush(cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||||
|
Pin::new(&mut self.0).poll_shutdown(cx)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,14 @@
|
|||||||
mod add_origin;
|
mod add_origin;
|
||||||
mod boxed;
|
mod boxed;
|
||||||
mod grpc;
|
mod grpc;
|
||||||
|
// mod reconnect;
|
||||||
|
mod connect;
|
||||||
|
mod connector;
|
||||||
|
mod discover;
|
||||||
|
mod io;
|
||||||
|
mod tls;
|
||||||
|
|
||||||
pub use self::add_origin::AddOrigin;
|
pub use self::add_origin::AddOrigin;
|
||||||
pub use self::boxed::BoxService;
|
pub use self::boxed::BoxService;
|
||||||
|
pub use self::discover::ServiceList;
|
||||||
pub use self::grpc::GrpcService;
|
pub use self::grpc::GrpcService;
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
use tower_make::MakeService;
|
||||||
|
use tower_service::Service;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Reconnect<M> {
|
||||||
|
inner: M,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<M, Target, Request> Service<Target> for Reconnect<M>
|
||||||
|
where
|
||||||
|
M: MakeService<Target, Request>,
|
||||||
|
{
|
||||||
|
type Response = M::Response;
|
||||||
|
type Error = M::Error;
|
||||||
|
type Future = M::Future;
|
||||||
|
|
||||||
|
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||||
|
self.inner.poll_ready(cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call(&mut self, req: Target) -> Self::Future {
|
||||||
|
unimplmented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
body::BoxBody,
|
body::BoxBody,
|
||||||
service::{AddOrigin, BoxService, GrpcService},
|
service::{AddOrigin, BoxService, GrpcService, ServiceList},
|
||||||
};
|
};
|
||||||
use futures_util::try_future::{MapErr, TryFutureExt};
|
use futures_util::try_future::{MapErr, TryFutureExt};
|
||||||
use http::Uri;
|
use http::Uri;
|
||||||
@@ -57,10 +57,11 @@ impl GrpcService<BoxBody> for Channel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Builder {
|
pub struct Builder<D = ServiceList> {
|
||||||
ca: Option<Vec<u8>>,
|
ca: Option<Vec<u8>>,
|
||||||
override_domain: Option<String>,
|
override_domain: Option<String>,
|
||||||
buffer_size: usize,
|
buffer_size: usize,
|
||||||
|
balance: Option<D>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Builder {
|
impl Builder {
|
||||||
@@ -69,6 +70,7 @@ impl Builder {
|
|||||||
ca: None,
|
ca: None,
|
||||||
override_domain: None,
|
override_domain: None,
|
||||||
buffer_size: 1024,
|
buffer_size: 1024,
|
||||||
|
balance: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,6 +91,19 @@ impl Builder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn balance_list(&mut self, list: Vec<Uri>) -> Result<Channel, super::Error> {
|
||||||
|
let discover = ServiceList::new(list);
|
||||||
|
let svc = tower_balance::p2c::Balance::from_entropy(discover);
|
||||||
|
let svc = BoxService::new(svc);
|
||||||
|
let svc = Buffer::new(Box::new(svc) as Inner, 100);
|
||||||
|
Ok(Channel { svc })
|
||||||
|
}
|
||||||
|
|
||||||
|
// pub fn balance<D: Discover>(&mut self, discover: D) -> &mut Self<D> {
|
||||||
|
// self.balance = Some(discover);
|
||||||
|
// self
|
||||||
|
// }
|
||||||
|
|
||||||
pub fn build<T>(&self, uri: T) -> Result<Channel, super::Error>
|
pub fn build<T>(&self, uri: T) -> Result<Channel, super::Error>
|
||||||
where
|
where
|
||||||
Uri: http::HttpTryFrom<T>,
|
Uri: http::HttpTryFrom<T>,
|
||||||
@@ -128,6 +143,7 @@ impl Builder {
|
|||||||
let svc = tower_reconnect::Reconnect::new(maker, uri.clone());
|
let svc = tower_reconnect::Reconnect::new(maker, uri.clone());
|
||||||
|
|
||||||
let svc = AddOrigin::new(svc, uri);
|
let svc = AddOrigin::new(svc, uri);
|
||||||
|
|
||||||
let svc = BoxService::new(svc);
|
let svc = BoxService::new(svc);
|
||||||
Buffer::new(Box::new(svc) as Inner, 100)
|
Buffer::new(Box::new(svc) as Inner, 100)
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user