Skip to content

Commit 0e2050a

Browse files
authored
Add Loadbalance trait (#199)
* refact(ld): add LoadBalancer trait, impl random ld * refact(loadbalance): update ld api design * style: cargo fmt * example(greet): update client * chore: add tag field * style: cargo fmt * refact(dubbo): use target_family * style: cargo check
1 parent a68707a commit 0e2050a

File tree

11 files changed

+218
-134
lines changed

11 files changed

+218
-134
lines changed

application.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ dubbo:
1313
provider:
1414
services:
1515
GreeterProvider:
16+
tag: red
1617
version: 1.0.0
1718
group: test
1819
protocol: triple

dubbo/src/config/config.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,8 @@ impl RootConfig {
119119
.group("test".to_string())
120120
.version("1.0.0".to_string())
121121
.interface("helloworld.Greeter".to_string())
122-
.protocol("triple".to_string()),
122+
.protocol("triple".to_string())
123+
.tag("read".to_string()),
123124
);
124125
self.protocols.insert(
125126
"triple".to_string(),

dubbo/src/config/service.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ pub struct ServiceConfig {
2323
pub group: String,
2424
pub protocol: String,
2525
pub interface: String,
26+
pub tag: String,
2627
}
2728

2829
impl ServiceConfig {
@@ -41,4 +42,7 @@ impl ServiceConfig {
4142
pub fn protocol(self, protocol: String) -> Self {
4243
Self { protocol, ..self }
4344
}
45+
pub fn tag(self, tag: String) -> Self {
46+
Self { tag, ..self }
47+
}
4448
}

dubbo/src/extension/invoker_extension.rs

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -40,30 +40,32 @@ pub trait Invoker {
4040
async fn url(&self) -> Result<Url, StdError>;
4141
}
4242

43-
pub enum CallType {
44-
Unary,
45-
ClientStream,
46-
ServerStream,
47-
BiStream,
48-
}
43+
// pub enum CallType {
44+
// Unary,
45+
// ClientStream,
46+
// ServerStream,
47+
// BiStream,
48+
// }
4949

5050
pub struct GrpcInvocation {
51-
service_name: String,
52-
method_name: String,
53-
arguments: Vec<Argument>,
54-
attachments: HashMap<String, String>,
55-
call_type: CallType,
51+
// service_name: String,
52+
// method_name: String,
53+
// arguments: Vec<Argument>,
54+
// attachments: HashMap<String, String>,
55+
// call_type: CallType,
5656
}
5757

58-
pub struct Argument {
59-
name: String,
60-
value: Box<dyn Stream<Item = Box<dyn Serializable + Send + 'static>> + Send + 'static>,
61-
}
58+
// pub struct Argument {
59+
// name: String,
60+
// value: Box<dyn Stream<Item = Box<dyn Serializable + Send + 'static>> + Send + 'static>,
61+
// }
6262

63+
#[allow(dead_code)]
6364
pub trait Serializable {
6465
fn serialize(&self, serialization_type: String) -> Result<Bytes, StdError>;
6566
}
6667

68+
#[allow(dead_code)]
6769
pub trait Deserializable {
6870
fn deserialize(&self, bytes: Bytes, deserialization_type: String) -> Result<Self, StdError>
6971
where
@@ -138,8 +140,8 @@ pub mod proxy {
138140
}
139141
}
140142

141-
impl From<Box<dyn Invoker + Send + 'static>> for InvokerProxy {
142-
fn from(invoker: Box<dyn Invoker + Send + 'static>) -> Self {
143+
impl From<Box<dyn Invoker + Send + Sync + 'static>> for InvokerProxy {
144+
fn from(invoker: Box<dyn Invoker + Send + Sync + 'static>) -> Self {
143145
let (tx, mut rx) = tokio::sync::mpsc::channel(64);
144146
tokio::spawn(async move {
145147
while let Some(opt) = rx.recv().await {
@@ -220,7 +222,11 @@ impl InvokerExtensionLoader {
220222
type InvokerExtensionConstructor = fn(
221223
Url,
222224
) -> Pin<
223-
Box<dyn Future<Output = Result<Box<dyn Invoker + Send + 'static>, StdError>> + Send + 'static>,
225+
Box<
226+
dyn Future<Output = Result<Box<dyn Invoker + Send + Sync + 'static>, StdError>>
227+
+ Send
228+
+ 'static,
229+
>,
224230
>;
225231
pub(crate) struct InvokerExtensionFactory {
226232
constructor: InvokerExtensionConstructor,
@@ -275,7 +281,7 @@ where
275281
impl<T> ExtensionMetaInfo for InvokerExtension<T>
276282
where
277283
T: Invoker + Send + 'static,
278-
T: Extension<Target = Box<dyn Invoker + Send + 'static>>,
284+
T: Extension<Target = Box<dyn Invoker + Send + Sync + 'static>>,
279285
{
280286
fn name() -> String {
281287
T::name()

dubbo/src/loadbalancer/mod.rs

Lines changed: 90 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,26 +14,33 @@
1414
* See the License for the specific language governing permissions and
1515
* limitations under the License.
1616
*/
17-
use crate::StdError;
17+
18+
pub mod random;
19+
1820
use futures_core::future::BoxFuture;
21+
use std::error::Error;
22+
use tokio::time::Duration;
1923
use tower::{discover::ServiceList, ServiceExt};
2024
use tower_service::Service;
25+
use tracing::debug;
2126

2227
use crate::{
2328
codegen::RpcInvocation,
29+
invocation::Metadata,
2430
invoker::{clone_body::CloneBody, clone_invoker::CloneInvoker},
31+
loadbalancer::random::RandomLoadBalancer,
2532
param::Param,
33+
protocol::triple::triple_invoker::TripleInvoker,
2634
svc::NewService,
35+
StdError,
2736
};
2837

29-
use crate::protocol::triple::triple_invoker::TripleInvoker;
30-
3138
pub struct NewLoadBalancer<N> {
3239
inner: N,
3340
}
3441

3542
#[derive(Clone)]
36-
pub struct LoadBalancer<S> {
43+
pub struct LoadBalancerSvc<S> {
3744
inner: S, // Routes service
3845
}
3946

@@ -53,17 +60,17 @@ where
5360
// NewRoutes
5461
N: NewService<T>,
5562
{
56-
type Service = LoadBalancer<N::Service>;
63+
type Service = LoadBalancerSvc<N::Service>;
5764

5865
fn new_service(&self, target: T) -> Self::Service {
5966
// Routes service
6067
let svc = self.inner.new_service(target);
6168

62-
LoadBalancer { inner: svc }
69+
LoadBalancerSvc { inner: svc }
6370
}
6471
}
6572

66-
impl<N> Service<http::Request<CloneBody>> for LoadBalancer<N>
73+
impl<N> Service<http::Request<CloneBody>> for LoadBalancerSvc<N>
6774
where
6875
// Routes service
6976
N: Service<(), Response = Vec<CloneInvoker<TripleInvoker>>> + Clone,
@@ -94,18 +101,87 @@ where
94101
Ok(routes) => routes,
95102
};
96103

97-
let service_list: Vec<_> = routes
98-
.into_iter()
99-
.map(|invoker| tower::load::Constant::new(invoker, 1))
100-
.collect();
104+
// let service_list: Vec<_> = routes
105+
// .into_iter()
106+
// // .map(|invoker| tower::load::Constant::new(invoker, 1))
107+
// .collect();
101108

102-
let service_list = ServiceList::new(service_list);
109+
// let rdm = RandomLoadBalancer::default();
110+
let metadata = Metadata::from_headers(req.headers().clone());
111+
// let invks = rdm.select_invokers(service_list, metadata);
112+
// invks.oneshot(req).await
113+
// let service_list = ServiceList::new(service_list);
103114

104-
let p2c = tower::balance::p2c::Balance::new(service_list);
115+
// let p2c = tower::balance::p2c::Balance::new(service_list);
116+
// let p: Box<dyn LoadBalancer<Invoker = BoxService<http::Request<CloneBody>, http::Response<UnsyncBoxBody<bytes::Bytes, status::Status>>, Box<dyn std::error::Error + std::marker::Send + std::marker::Sync>>> + std::marker::Send + std::marker::Sync> = get_loadbalancer("p2c").into();
117+
let p = get_loadbalancer("p2c");
118+
// let ivk = p.select_invokers(invokers, metadata);
119+
let ivk = p.select_invokers(routes, metadata);
105120

106-
p2c.oneshot(req).await
121+
ivk.oneshot(req).await
107122
};
108123

109124
Box::pin(fut)
110125
}
111126
}
127+
128+
type DubboBoxService = tower::util::BoxService<
129+
http::Request<CloneBody>,
130+
http::Response<crate::BoxBody>,
131+
Box<dyn Error + Send + Sync>,
132+
>;
133+
134+
pub trait LoadBalancer {
135+
type Invoker;
136+
137+
fn select_invokers(
138+
&self,
139+
invokers: Vec<CloneInvoker<TripleInvoker>>,
140+
metadata: Metadata,
141+
) -> Self::Invoker;
142+
}
143+
144+
fn get_loadbalancer(
145+
loadbalancer: &str,
146+
) -> Box<dyn LoadBalancer<Invoker = DubboBoxService> + Send + Sync + 'static> {
147+
match loadbalancer {
148+
"random" => {
149+
println!("random!");
150+
Box::new(RandomLoadBalancer::default())
151+
}
152+
"p2c" => Box::new(P2cBalancer::default()),
153+
_ => Box::new(P2cBalancer::default()),
154+
}
155+
}
156+
const DEFAULT_RTT: Duration = Duration::from_millis(30);
157+
#[derive(Debug, Default)]
158+
pub struct P2cBalancer {}
159+
160+
impl LoadBalancer for P2cBalancer {
161+
type Invoker = DubboBoxService;
162+
163+
fn select_invokers(
164+
&self,
165+
invokers: Vec<CloneInvoker<TripleInvoker>>,
166+
_metadata: Metadata,
167+
) -> Self::Invoker {
168+
debug!("p2c load balancer");
169+
let service_list: Vec<_> = invokers
170+
.into_iter()
171+
.map(|invoker| tower::load::Constant::new(invoker, 1))
172+
.collect();
173+
174+
let decay = Duration::from_secs(10);
175+
let service_list = ServiceList::new(service_list);
176+
let s = tower::load::PeakEwmaDiscover::new(
177+
service_list,
178+
DEFAULT_RTT,
179+
decay,
180+
tower::load::CompleteOnResponse::default(),
181+
);
182+
183+
let p = tower::balance::p2c::Balance::new(s);
184+
let svc = DubboBoxService::new(p);
185+
svc
186+
}
187+
}

dubbo/src/loadbalancer/random.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
use rand::prelude::SliceRandom;
19+
use tracing::debug;
20+
21+
use super::{DubboBoxService, LoadBalancer};
22+
use crate::{
23+
invocation::Metadata, loadbalancer::CloneInvoker,
24+
protocol::triple::triple_invoker::TripleInvoker,
25+
};
26+
27+
#[derive(Clone, Default)]
28+
pub struct RandomLoadBalancer {}
29+
30+
impl LoadBalancer for RandomLoadBalancer {
31+
type Invoker = DubboBoxService;
32+
33+
fn select_invokers(
34+
&self,
35+
invokers: Vec<CloneInvoker<TripleInvoker>>,
36+
metadata: Metadata,
37+
) -> Self::Invoker {
38+
debug!("random loadbalance {:?}", metadata);
39+
let ivk = invokers.choose(&mut rand::thread_rng()).unwrap().clone();
40+
DubboBoxService::new(ivk)
41+
}
42+
}

dubbo/src/triple/transport/connector/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
pub mod http_connector;
1919
pub mod https_connector;
20-
#[cfg(any(target_os = "macos", target_os = "unix"))]
20+
#[cfg(any(target_os = "macos", target_family = "unix"))]
2121
pub mod unix_connector;
2222

2323
use hyper::Uri;
@@ -84,7 +84,7 @@ pub fn get_connector(connector: &str) -> BoxCloneService<Uri, BoxIO, crate::Erro
8484
let c = https_connector::HttpsConnector::new();
8585
BoxCloneService::new(Connector::new(c))
8686
}
87-
#[cfg(any(target_os = "macos", target_os = "unix"))]
87+
#[cfg(any(target_os = "macos", target_family = "unix"))]
8888
"unix" => {
8989
let c = unix_connector::UnixConnector::new();
9090
BoxCloneService::new(Connector::new(c))

dubbo/src/triple/transport/listener/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
*/
1717

1818
pub mod tcp_listener;
19-
#[cfg(any(target_os = "macos", target_os = "unix"))]
19+
#[cfg(any(target_os = "macos", target_family = "unix"))]
2020
pub mod unix_listener;
2121

2222
use std::net::SocketAddr;
@@ -65,7 +65,7 @@ impl<T: Listener> Listener for WrappedListener<T> {
6565
pub async fn get_listener(name: String, addr: SocketAddr) -> Result<BoxListener, crate::Error> {
6666
match name.as_str() {
6767
"tcp" => Ok(TcpListener::bind(addr).await?.boxed()),
68-
#[cfg(any(target_os = "macos", target_os = "unix"))]
68+
#[cfg(any(target_os = "macos", target_family = "unix"))]
6969
"unix" => Ok(unix_listener::UnixListener::bind(addr).await?.boxed()),
7070
_ => {
7171
warn!("no support listener: {:?}", name);

0 commit comments

Comments
 (0)