From 2192217244dfabd431c0220eec3641689406dc61 Mon Sep 17 00:00:00 2001 From: yangyang <962032265@qq.com> Date: Wed, 7 Dec 2022 21:51:52 +0800 Subject: [PATCH 01/48] feat(dubbo): add unix feature --- dubbo/Cargo.toml | 3 +++ dubbo/src/triple/transport/connector/mod.rs | 2 ++ dubbo/src/triple/transport/listener/mod.rs | 5 +++-- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/dubbo/Cargo.toml b/dubbo/Cargo.toml index 7b38a28f..67402e52 100644 --- a/dubbo/Cargo.toml +++ b/dubbo/Cargo.toml @@ -34,3 +34,6 @@ async-stream = "0.3" flate2 = "1.0" dubbo-config = {path = "../config", version = "0.2.0"} + +[features] +unix = [] \ No newline at end of file diff --git a/dubbo/src/triple/transport/connector/mod.rs b/dubbo/src/triple/transport/connector/mod.rs index 89ae555a..c4d5811a 100644 --- a/dubbo/src/triple/transport/connector/mod.rs +++ b/dubbo/src/triple/transport/connector/mod.rs @@ -16,6 +16,7 @@ */ pub mod http_connector; +#[cfg(feature = "unix")] pub mod unix_connector; use hyper::Uri; @@ -78,6 +79,7 @@ pub fn get_connector(connector: String) -> BoxCloneService { let c = unix_connector::UnixConnector::new(); BoxCloneService::new(Connector::new(c)) diff --git a/dubbo/src/triple/transport/listener/mod.rs b/dubbo/src/triple/transport/listener/mod.rs index d755cd3b..17457543 100644 --- a/dubbo/src/triple/transport/listener/mod.rs +++ b/dubbo/src/triple/transport/listener/mod.rs @@ -16,6 +16,7 @@ */ pub mod tcp_listener; +#[cfg(feature = "unix")] pub mod unix_listener; use std::net::SocketAddr; @@ -25,7 +26,6 @@ use tokio::io::{AsyncRead, AsyncWrite}; use super::io::BoxIO; pub use tcp_listener::TcpListener; -pub use unix_listener::UnixListener; #[async_trait] pub trait Listener: Send + Sync { @@ -64,7 +64,8 @@ impl Listener for WrappedListener { pub async fn get_listener(name: String, addr: SocketAddr) -> Result { match name.as_str() { "tcp" => Ok(TcpListener::bind(addr).await?.boxed()), - "unix" => Ok(UnixListener::bind(addr).await?.boxed()), + #[cfg(feature = "unix")] + "unix" => Ok(unix_listener::UnixListener::bind(addr).await?.boxed()), _ => { tracing::warn!("no support listener: {:?}", name); Err(Box::new(crate::status::DubboError::new(format!( From ea06ca18ab323b70e8c7612a86ab24e2de16d179 Mon Sep 17 00:00:00 2001 From: yangyang <962032265@qq.com> Date: Thu, 8 Dec 2022 22:22:29 +0800 Subject: [PATCH 02/48] Rft: replace feature with target_os cfg --- dubbo/Cargo.toml | 3 --- dubbo/src/framework.rs | 2 +- dubbo/src/triple/transport/connector/mod.rs | 4 ++-- dubbo/src/triple/transport/listener/mod.rs | 4 ++-- 4 files changed, 5 insertions(+), 8 deletions(-) diff --git a/dubbo/Cargo.toml b/dubbo/Cargo.toml index 67402e52..7b38a28f 100644 --- a/dubbo/Cargo.toml +++ b/dubbo/Cargo.toml @@ -34,6 +34,3 @@ async-stream = "0.3" flate2 = "1.0" dubbo-config = {path = "../config", version = "0.2.0"} - -[features] -unix = [] \ No newline at end of file diff --git a/dubbo/src/framework.rs b/dubbo/src/framework.rs index da21ab88..412ff36b 100644 --- a/dubbo/src/framework.rs +++ b/dubbo/src/framework.rs @@ -53,7 +53,7 @@ impl Dubbo { } pub fn init(&mut self) { - let conf = self.config.get_or_insert_with(|| get_global_config()); + let conf = self.config.get_or_insert_with(get_global_config); tracing::debug!("global conf: {:?}", conf); for (name, url) in conf.registries.iter() { diff --git a/dubbo/src/triple/transport/connector/mod.rs b/dubbo/src/triple/transport/connector/mod.rs index c4d5811a..82b9281c 100644 --- a/dubbo/src/triple/transport/connector/mod.rs +++ b/dubbo/src/triple/transport/connector/mod.rs @@ -16,7 +16,7 @@ */ pub mod http_connector; -#[cfg(feature = "unix")] +#[cfg(target_os = "unix")] pub mod unix_connector; use hyper::Uri; @@ -79,7 +79,7 @@ pub fn get_connector(connector: String) -> BoxCloneService { let c = unix_connector::UnixConnector::new(); BoxCloneService::new(Connector::new(c)) diff --git a/dubbo/src/triple/transport/listener/mod.rs b/dubbo/src/triple/transport/listener/mod.rs index 17457543..6beb2157 100644 --- a/dubbo/src/triple/transport/listener/mod.rs +++ b/dubbo/src/triple/transport/listener/mod.rs @@ -16,7 +16,7 @@ */ pub mod tcp_listener; -#[cfg(feature = "unix")] +#[cfg(target_os = "unix")] pub mod unix_listener; use std::net::SocketAddr; @@ -64,7 +64,7 @@ impl Listener for WrappedListener { pub async fn get_listener(name: String, addr: SocketAddr) -> Result { match name.as_str() { "tcp" => Ok(TcpListener::bind(addr).await?.boxed()), - #[cfg(feature = "unix")] + #[cfg(target_os = "unix")] "unix" => Ok(unix_listener::UnixListener::bind(addr).await?.boxed()), _ => { tracing::warn!("no support listener: {:?}", name); From 5f5b31fe098664bb320d174b1b96b2131fd913fe Mon Sep 17 00:00:00 2001 From: yangyang <962032265@qq.com> Date: Fri, 9 Dec 2022 21:38:04 +0800 Subject: [PATCH 03/48] Rft(dubbo): add ClientBuilder for client --- dubbo/Cargo.toml | 2 +- dubbo/src/codegen.rs | 1 + dubbo/src/triple/client/builder.rs | 87 +++++++++++++++++++++ dubbo/src/triple/client/connection.rs | 8 +- dubbo/src/triple/client/mod.rs | 1 + dubbo/src/triple/client/triple.rs | 35 ++++++--- dubbo/src/triple/transport/connector/mod.rs | 4 +- 7 files changed, 120 insertions(+), 18 deletions(-) create mode 100644 dubbo/src/triple/client/builder.rs diff --git a/dubbo/Cargo.toml b/dubbo/Cargo.toml index bdde2ea3..145b4a0e 100644 --- a/dubbo/Cargo.toml +++ b/dubbo/Cargo.toml @@ -14,7 +14,7 @@ hyper = { version = "0.14.19", features = ["full"]} http = "0.2" tower-service = "0.3.1" http-body = "0.4.4" -tower = "0.4.12" +tower = { version = "0.4.12", features = ["timeout"]} futures-util = "0.3.23" futures-core ="0.3.23" tokio = { version = "1.0", features = [ "rt-multi-thread", "time", "fs", "macros", "net", "signal"] } diff --git a/dubbo/src/codegen.rs b/dubbo/src/codegen.rs index 9ffce899..f485dc53 100644 --- a/dubbo/src/codegen.rs +++ b/dubbo/src/codegen.rs @@ -38,4 +38,5 @@ pub use super::triple::server::TripleServer; pub use super::{empty_body, BoxBody, BoxFuture, StdError}; pub use crate::filter::service::FilterService; pub use crate::filter::Filter; +pub use crate::triple::client::builder::{ClientBoxService, ClientBuilder}; pub use crate::triple::client::connection::Connection; diff --git a/dubbo/src/triple/client/builder.rs b/dubbo/src/triple/client/builder.rs new file mode 100644 index 00000000..e95070b6 --- /dev/null +++ b/dubbo/src/triple/client/builder.rs @@ -0,0 +1,87 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use http::Uri; +use hyper::client::conn::Builder; +use tokio::time::Duration; +use tower::ServiceBuilder; + +use crate::triple::client::connection::Connection; +use crate::utils::boxed::BoxService; + +pub type ClientBoxService = + BoxService, http::Response, crate::Error>; + +#[derive(Clone, Debug, Default)] +pub struct ClientBuilder { + pub uri: Uri, + pub timeout: Option, + pub connector: &'static str, +} + +impl ClientBuilder { + pub fn new() -> ClientBuilder { + ClientBuilder { + uri: Uri::builder().build().unwrap(), + timeout: None, + connector: "", + } + } + + pub fn from_static(s: &'static str) -> ClientBuilder { + Self::from(Uri::from_static(s)) + } + + pub fn with_timeout(self, timeout: u64) -> Self { + Self { + timeout: Some(timeout), + ..self + } + } + + pub fn with_host(self, host: &'static str) -> Self { + Self { + uri: Uri::from_static(host), + ..self + } + } + + pub fn connect(self) -> ClientBoxService { + let builder = ServiceBuilder::new(); + let timeout = self.timeout.unwrap_or(0); + let builder = builder.timeout(Duration::from_secs(timeout)); + + let mut b = Builder::new(); + let hyper_builder = b.http2_only(true); + let conn = Connection::new() + .with_host(self.uri.clone()) + .with_connector(self.connector) + .with_builder(hyper_builder.to_owned()); + + BoxService::new(builder.service(conn)) + } +} + +impl From for ClientBuilder { + fn from(u: Uri) -> Self { + Self { + uri: u, + timeout: None, + connector: "", + } + } +} diff --git a/dubbo/src/triple/client/connection.rs b/dubbo/src/triple/client/connection.rs index ce453096..d9bda6fb 100644 --- a/dubbo/src/triple/client/connection.rs +++ b/dubbo/src/triple/client/connection.rs @@ -27,7 +27,7 @@ use crate::triple::transport::connector::get_connector; #[derive(Debug, Clone)] pub struct Connection { host: hyper::Uri, - connector: String, + connector: &'static str, builder: Builder, } @@ -41,12 +41,12 @@ impl Connection { pub fn new() -> Self { Connection { host: hyper::Uri::default(), - connector: "http".to_string(), + connector: "http", builder: Builder::new(), } } - pub fn with_connector(mut self, connector: String) -> Self { + pub fn with_connector(mut self, connector: &'static str) -> Self { self.connector = connector; self } @@ -83,7 +83,7 @@ where fn call(&mut self, req: http::Request) -> Self::Future { let builder = self.builder.clone().http2_only(true).to_owned(); - let mut connector = Connect::new(get_connector(self.connector.clone()), builder); + let mut connector = Connect::new(get_connector(self.connector), builder); let uri = self.host.clone(); let fut = async move { let mut con = connector.call(uri).await.unwrap(); diff --git a/dubbo/src/triple/client/mod.rs b/dubbo/src/triple/client/mod.rs index 333a60a4..48b6b22b 100644 --- a/dubbo/src/triple/client/mod.rs +++ b/dubbo/src/triple/client/mod.rs @@ -15,6 +15,7 @@ * limitations under the License. */ +pub mod builder; pub mod connection; pub mod triple; diff --git a/dubbo/src/triple/client/triple.rs b/dubbo/src/triple/client/triple.rs index 36c9ce94..d4864efd 100644 --- a/dubbo/src/triple/client/triple.rs +++ b/dubbo/src/triple/client/triple.rs @@ -21,7 +21,7 @@ use futures_util::{future, stream, StreamExt, TryStreamExt}; use http::HeaderValue; use tower_service::Service; -use super::connection::Connection; +use super::builder::{ClientBoxService, ClientBuilder}; use crate::filter::service::FilterService; use crate::filter::Filter; use crate::invocation::{IntoStreamingRequest, Metadata, Request, Response}; @@ -32,33 +32,43 @@ use crate::triple::encode::encode; #[derive(Debug, Clone, Default)] pub struct TripleClient { - host: Option, + builder: Option, inner: T, send_compression_encoding: Option, } -impl TripleClient { +impl TripleClient { pub fn connect(host: String) -> Self { let uri = match http::Uri::from_str(&host) { - Ok(v) => Some(v), + Ok(v) => v, Err(err) => { tracing::error!("http uri parse error: {}, host: {}", err, host); panic!("http uri parse error: {}, host: {}", err, host) } }; + let builder = ClientBuilder::from(uri); + TripleClient { - host: uri.clone(), - inner: Connection::new().with_host(uri.unwrap()), + builder: Some(builder.clone()), + inner: builder.connect(), + send_compression_encoding: Some(CompressionEncoding::Gzip), + } + } + + pub fn with_builder(builder: ClientBuilder) -> Self { + TripleClient { + builder: Some(builder.clone()), + inner: builder.connect(), send_compression_encoding: Some(CompressionEncoding::Gzip), } } } impl TripleClient { - pub fn new(inner: T, host: Option) -> Self { + pub fn new(inner: T, builder: ClientBuilder) -> Self { TripleClient { - host, + builder: Some(builder), inner, send_compression_encoding: Some(CompressionEncoding::Gzip), } @@ -68,7 +78,10 @@ impl TripleClient { where F: Filter, { - TripleClient::new(FilterService::new(self.inner, filter), self.host) + TripleClient::new( + FilterService::new(self.inner, filter), + self.builder.unwrap(), + ) } } @@ -82,8 +95,8 @@ where path: http::uri::PathAndQuery, body: hyper::Body, ) -> http::Request { - let mut parts = match self.host.as_ref() { - Some(v) => v.to_owned().into_parts(), + let mut parts = match self.builder.as_ref() { + Some(v) => v.to_owned().uri.into_parts(), None => { tracing::error!("client host is empty"); return http::Request::new(hyper::Body::empty()); diff --git a/dubbo/src/triple/transport/connector/mod.rs b/dubbo/src/triple/transport/connector/mod.rs index 82b9281c..6e0e0622 100644 --- a/dubbo/src/triple/transport/connector/mod.rs +++ b/dubbo/src/triple/transport/connector/mod.rs @@ -73,8 +73,8 @@ where } } -pub fn get_connector(connector: String) -> BoxCloneService { - match connector.as_str() { +pub fn get_connector(connector: &'static str) -> BoxCloneService { + match connector { "http" => { let c = http_connector::HttpConnector::new(); BoxCloneService::new(Connector::new(c)) From 176cb57e5524aea0166f1eced5e19286a6340758 Mon Sep 17 00:00:00 2001 From: yangyang <962032265@qq.com> Date: Fri, 9 Dec 2022 21:39:21 +0800 Subject: [PATCH 04/48] =?UTF-8?q?Rft=C3=AF(dubbo-build):=20add=20build=20a?= =?UTF-8?q?pi=20for=20client?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dubbo-build/src/client.rs | 12 +- examples/echo/src/echo/client.rs | 5 +- examples/echo/src/protos/hello_echo.rs | 183 +++++++++++++++++-------- 3 files changed, 137 insertions(+), 63 deletions(-) diff --git a/dubbo-build/src/client.rs b/dubbo-build/src/client.rs index 10fa4468..8d58ab16 100644 --- a/dubbo-build/src/client.rs +++ b/dubbo-build/src/client.rs @@ -70,13 +70,19 @@ pub fn generate( inner: TripleClient, } - impl #service_ident { + impl #service_ident { pub fn connect(host: String) -> Self { let cli = TripleClient::connect(host); #service_ident { inner: cli, } } + + pub fn build(builder: ClientBuilder) -> Self { + Self { + inner: TripleClient::with_builder(builder), + } + } } impl #service_ident @@ -84,9 +90,9 @@ pub fn generate( T: Service, Response = http::Response>, T::Error: Into, { - pub fn new(inner: T) -> Self { + pub fn new(inner: T, builder: ClientBuilder) -> Self { Self { - inner: TripleClient::new(inner, None), + inner: TripleClient::new(inner, builder), } } diff --git a/examples/echo/src/echo/client.rs b/examples/echo/src/echo/client.rs index 8b3cc9bf..73bc02ef 100644 --- a/examples/echo/src/echo/client.rs +++ b/examples/echo/src/echo/client.rs @@ -32,8 +32,9 @@ impl Filter for FakeFilter { #[tokio::main] async fn main() { let mut cli = EchoClient::connect("http://127.0.0.1:8888".to_string()); - let mut unary_cli = cli.clone().with_filter(FakeFilter {}); - let resp = unary_cli + // let mut unary_cli = cli.clone().with_filter(FakeFilter {}); + // let mut cli = EchoClient::build(ClientBuilder::from_static("http://127.0.0.1:8888")); + let resp = cli .unary_echo(Request::new(EchoRequest { message: "message from client".to_string(), })) diff --git a/examples/echo/src/protos/hello_echo.rs b/examples/echo/src/protos/hello_echo.rs index 86daf6fb..cca549ce 100644 --- a/examples/echo/src/protos/hello_echo.rs +++ b/examples/echo/src/protos/hello_echo.rs @@ -15,16 +15,17 @@ * limitations under the License. */ + /// EchoRequest is the request for echo. #[derive(Clone, PartialEq, ::prost::Message)] pub struct EchoRequest { - #[prost(string, tag = "1")] + #[prost(string, tag="1")] pub message: ::prost::alloc::string::String, } /// EchoResponse is the response for echo. #[derive(Clone, PartialEq, ::prost::Message)] pub struct EchoResponse { - #[prost(string, tag = "1")] + #[prost(string, tag="1")] pub message: ::prost::alloc::string::String, } /// Generated client implementations. @@ -36,20 +37,25 @@ pub mod echo_client { pub struct EchoClient { inner: TripleClient, } - impl EchoClient { + impl EchoClient { pub fn connect(host: String) -> Self { let cli = TripleClient::connect(host); EchoClient { inner: cli } } + pub fn build(builder: ClientBuilder) -> Self { + Self { + inner: TripleClient::with_builder(builder), + } + } } impl EchoClient where T: Service, Response = http::Response>, T::Error: Into, { - pub fn new(inner: T) -> Self { + pub fn new(inner: T, builder: ClientBuilder) -> Self { Self { - inner: TripleClient::new(inner, None), + inner: TripleClient::new(inner, builder), } } pub fn with_filter(self, filter: F) -> EchoClient> @@ -64,9 +70,13 @@ pub mod echo_client { &mut self, request: Request, ) -> Result, dubbo::status::Status> { - let codec = - dubbo::codegen::ProstCodec::::default(); - let path = http::uri::PathAndQuery::from_static("/grpc.examples.echo.Echo/UnaryEcho"); + let codec = dubbo::codegen::ProstCodec::< + super::EchoRequest, + super::EchoResponse, + >::default(); + let path = http::uri::PathAndQuery::from_static( + "/grpc.examples.echo.Echo/UnaryEcho", + ); self.inner.unary(request, codec, path).await } /// ServerStreamingEcho is server side streaming. @@ -74,8 +84,10 @@ pub mod echo_client { &mut self, request: Request, ) -> Result>, dubbo::status::Status> { - let codec = - dubbo::codegen::ProstCodec::::default(); + let codec = dubbo::codegen::ProstCodec::< + super::EchoRequest, + super::EchoResponse, + >::default(); let path = http::uri::PathAndQuery::from_static( "/grpc.examples.echo.Echo/ServerStreamingEcho", ); @@ -86,8 +98,10 @@ pub mod echo_client { &mut self, request: impl IntoStreamingRequest, ) -> Result, dubbo::status::Status> { - let codec = - dubbo::codegen::ProstCodec::::default(); + let codec = dubbo::codegen::ProstCodec::< + super::EchoRequest, + super::EchoResponse, + >::default(); let path = http::uri::PathAndQuery::from_static( "/grpc.examples.echo.Echo/ClientStreamingEcho", ); @@ -98,8 +112,10 @@ pub mod echo_client { &mut self, request: impl IntoStreamingRequest, ) -> Result>, dubbo::status::Status> { - let codec = - dubbo::codegen::ProstCodec::::default(); + let codec = dubbo::codegen::ProstCodec::< + super::EchoRequest, + super::EchoResponse, + >::default(); let path = http::uri::PathAndQuery::from_static( "/grpc.examples.echo.Echo/BidirectionalStreamingEcho", ); @@ -120,7 +136,9 @@ pub mod echo_server { request: Request, ) -> Result, dubbo::status::Status>; ///Server streaming response type for the ServerStreamingEcho method. - type ServerStreamingEchoStream: futures_util::Stream> + type ServerStreamingEchoStream: futures_util::Stream< + Item = Result, + > + Send + 'static; /// ServerStreamingEcho is server side streaming. @@ -134,14 +152,19 @@ pub mod echo_server { request: Request>, ) -> Result, dubbo::status::Status>; ///Server streaming response type for the BidirectionalStreamingEcho method. - type BidirectionalStreamingEchoStream: futures_util::Stream> + type BidirectionalStreamingEchoStream: futures_util::Stream< + Item = Result, + > + Send + 'static; /// BidirectionalStreamingEcho is bidi streaming. async fn bidirectional_streaming_echo( &self, request: Request>, - ) -> Result, dubbo::status::Status>; + ) -> Result< + Response, + dubbo::status::Status, + >; } /// Echo is the echo service. #[derive(Debug)] @@ -171,7 +194,10 @@ pub mod echo_server { type Response = http::Response; type Error = std::convert::Infallible; type Future = BoxFuture; - fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { + fn poll_ready( + &mut self, + _cx: &mut Context<'_>, + ) -> Poll> { Poll::Ready(Ok(())) } fn call(&mut self, req: http::Request) -> Self::Future { @@ -184,18 +210,26 @@ pub mod echo_server { } impl UnarySvc for UnaryEchoServer { type Response = super::EchoResponse; - type Future = BoxFuture, dubbo::status::Status>; - fn call(&mut self, request: Request) -> Self::Future { + type Future = BoxFuture< + Response, + dubbo::status::Status, + >; + fn call( + &mut self, + request: Request, + ) -> Self::Future { let inner = self.inner.0.clone(); let fut = async move { inner.unary_echo(request).await }; Box::pin(fut) } } let fut = async move { - let mut server = TripleServer::new(dubbo::codegen::ProstCodec::< - super::EchoResponse, - super::EchoRequest, - >::default()); + let mut server = TripleServer::new( + dubbo::codegen::ProstCodec::< + super::EchoResponse, + super::EchoRequest, + >::default(), + ); let res = server.unary(UnaryEchoServer { inner }, req).await; Ok(res) }; @@ -206,22 +240,32 @@ pub mod echo_server { struct ServerStreamingEchoServer { inner: _Inner, } - impl ServerStreamingSvc for ServerStreamingEchoServer { + impl ServerStreamingSvc + for ServerStreamingEchoServer { type Response = super::EchoResponse; type ResponseStream = T::ServerStreamingEchoStream; - type Future = - BoxFuture, dubbo::status::Status>; - fn call(&mut self, request: Request) -> Self::Future { + type Future = BoxFuture< + Response, + dubbo::status::Status, + >; + fn call( + &mut self, + request: Request, + ) -> Self::Future { let inner = self.inner.0.clone(); - let fut = async move { inner.server_streaming_echo(request).await }; + let fut = async move { + inner.server_streaming_echo(request).await + }; Box::pin(fut) } } let fut = async move { - let mut server = TripleServer::new(dubbo::codegen::ProstCodec::< - super::EchoResponse, - super::EchoRequest, - >::default()); + let mut server = TripleServer::new( + dubbo::codegen::ProstCodec::< + super::EchoResponse, + super::EchoRequest, + >::default(), + ); let res = server .server_streaming(ServerStreamingEchoServer { inner }, req) .await; @@ -234,23 +278,31 @@ pub mod echo_server { struct ClientStreamingEchoServer { inner: _Inner, } - impl ClientStreamingSvc for ClientStreamingEchoServer { + impl ClientStreamingSvc + for ClientStreamingEchoServer { type Response = super::EchoResponse; - type Future = BoxFuture, dubbo::status::Status>; + type Future = BoxFuture< + Response, + dubbo::status::Status, + >; fn call( &mut self, request: Request>, ) -> Self::Future { let inner = self.inner.0.clone(); - let fut = async move { inner.client_streaming_echo(request).await }; + let fut = async move { + inner.client_streaming_echo(request).await + }; Box::pin(fut) } } let fut = async move { - let mut server = TripleServer::new(dubbo::codegen::ProstCodec::< - super::EchoResponse, - super::EchoRequest, - >::default()); + let mut server = TripleServer::new( + dubbo::codegen::ProstCodec::< + super::EchoResponse, + super::EchoRequest, + >::default(), + ); let res = server .client_streaming(ClientStreamingEchoServer { inner }, req) .await; @@ -263,41 +315,56 @@ pub mod echo_server { struct BidirectionalStreamingEchoServer { inner: _Inner, } - impl StreamingSvc for BidirectionalStreamingEchoServer { + impl StreamingSvc + for BidirectionalStreamingEchoServer { type Response = super::EchoResponse; type ResponseStream = T::BidirectionalStreamingEchoStream; - type Future = - BoxFuture, dubbo::status::Status>; + type Future = BoxFuture< + Response, + dubbo::status::Status, + >; fn call( &mut self, request: Request>, ) -> Self::Future { let inner = self.inner.0.clone(); - let fut = - async move { inner.bidirectional_streaming_echo(request).await }; + let fut = async move { + inner.bidirectional_streaming_echo(request).await + }; Box::pin(fut) } } let fut = async move { - let mut server = TripleServer::new(dubbo::codegen::ProstCodec::< - super::EchoResponse, - super::EchoRequest, - >::default()); + let mut server = TripleServer::new( + dubbo::codegen::ProstCodec::< + super::EchoResponse, + super::EchoRequest, + >::default(), + ); let res = server - .bidi_streaming(BidirectionalStreamingEchoServer { inner }, req) + .bidi_streaming( + BidirectionalStreamingEchoServer { + inner, + }, + req, + ) .await; Ok(res) }; Box::pin(fut) } - _ => Box::pin(async move { - Ok(http::Response::builder() - .status(200) - .header("grpc-status", "12") - .header("content-type", "application/grpc") - .body(empty_body()) - .unwrap()) - }), + _ => { + Box::pin(async move { + Ok( + http::Response::builder() + .status(200) + .header("grpc-status", "12") + .header("content-type", "application/grpc") + .body(empty_body()) + .unwrap(), + ) + }) + } } } } From b4db021407d8bfdf147238779602e6435f463cd0 Mon Sep 17 00:00:00 2001 From: yangyang <962032265@qq.com> Date: Fri, 9 Dec 2022 21:47:11 +0800 Subject: [PATCH 05/48] style(examples): cargo fmt --- examples/echo/src/protos/hello_echo.rs | 172 ++++++++----------------- 1 file changed, 55 insertions(+), 117 deletions(-) diff --git a/examples/echo/src/protos/hello_echo.rs b/examples/echo/src/protos/hello_echo.rs index cca549ce..08a1d299 100644 --- a/examples/echo/src/protos/hello_echo.rs +++ b/examples/echo/src/protos/hello_echo.rs @@ -15,17 +15,16 @@ * limitations under the License. */ - /// EchoRequest is the request for echo. #[derive(Clone, PartialEq, ::prost::Message)] pub struct EchoRequest { - #[prost(string, tag="1")] + #[prost(string, tag = "1")] pub message: ::prost::alloc::string::String, } /// EchoResponse is the response for echo. #[derive(Clone, PartialEq, ::prost::Message)] pub struct EchoResponse { - #[prost(string, tag="1")] + #[prost(string, tag = "1")] pub message: ::prost::alloc::string::String, } /// Generated client implementations. @@ -70,13 +69,9 @@ pub mod echo_client { &mut self, request: Request, ) -> Result, dubbo::status::Status> { - let codec = dubbo::codegen::ProstCodec::< - super::EchoRequest, - super::EchoResponse, - >::default(); - let path = http::uri::PathAndQuery::from_static( - "/grpc.examples.echo.Echo/UnaryEcho", - ); + let codec = + dubbo::codegen::ProstCodec::::default(); + let path = http::uri::PathAndQuery::from_static("/grpc.examples.echo.Echo/UnaryEcho"); self.inner.unary(request, codec, path).await } /// ServerStreamingEcho is server side streaming. @@ -84,10 +79,8 @@ pub mod echo_client { &mut self, request: Request, ) -> Result>, dubbo::status::Status> { - let codec = dubbo::codegen::ProstCodec::< - super::EchoRequest, - super::EchoResponse, - >::default(); + let codec = + dubbo::codegen::ProstCodec::::default(); let path = http::uri::PathAndQuery::from_static( "/grpc.examples.echo.Echo/ServerStreamingEcho", ); @@ -98,10 +91,8 @@ pub mod echo_client { &mut self, request: impl IntoStreamingRequest, ) -> Result, dubbo::status::Status> { - let codec = dubbo::codegen::ProstCodec::< - super::EchoRequest, - super::EchoResponse, - >::default(); + let codec = + dubbo::codegen::ProstCodec::::default(); let path = http::uri::PathAndQuery::from_static( "/grpc.examples.echo.Echo/ClientStreamingEcho", ); @@ -112,10 +103,8 @@ pub mod echo_client { &mut self, request: impl IntoStreamingRequest, ) -> Result>, dubbo::status::Status> { - let codec = dubbo::codegen::ProstCodec::< - super::EchoRequest, - super::EchoResponse, - >::default(); + let codec = + dubbo::codegen::ProstCodec::::default(); let path = http::uri::PathAndQuery::from_static( "/grpc.examples.echo.Echo/BidirectionalStreamingEcho", ); @@ -136,9 +125,7 @@ pub mod echo_server { request: Request, ) -> Result, dubbo::status::Status>; ///Server streaming response type for the ServerStreamingEcho method. - type ServerStreamingEchoStream: futures_util::Stream< - Item = Result, - > + type ServerStreamingEchoStream: futures_util::Stream> + Send + 'static; /// ServerStreamingEcho is server side streaming. @@ -152,19 +139,14 @@ pub mod echo_server { request: Request>, ) -> Result, dubbo::status::Status>; ///Server streaming response type for the BidirectionalStreamingEcho method. - type BidirectionalStreamingEchoStream: futures_util::Stream< - Item = Result, - > + type BidirectionalStreamingEchoStream: futures_util::Stream> + Send + 'static; /// BidirectionalStreamingEcho is bidi streaming. async fn bidirectional_streaming_echo( &self, request: Request>, - ) -> Result< - Response, - dubbo::status::Status, - >; + ) -> Result, dubbo::status::Status>; } /// Echo is the echo service. #[derive(Debug)] @@ -194,10 +176,7 @@ pub mod echo_server { type Response = http::Response; type Error = std::convert::Infallible; type Future = BoxFuture; - fn poll_ready( - &mut self, - _cx: &mut Context<'_>, - ) -> Poll> { + fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } fn call(&mut self, req: http::Request) -> Self::Future { @@ -210,26 +189,18 @@ pub mod echo_server { } impl UnarySvc for UnaryEchoServer { type Response = super::EchoResponse; - type Future = BoxFuture< - Response, - dubbo::status::Status, - >; - fn call( - &mut self, - request: Request, - ) -> Self::Future { + type Future = BoxFuture, dubbo::status::Status>; + fn call(&mut self, request: Request) -> Self::Future { let inner = self.inner.0.clone(); let fut = async move { inner.unary_echo(request).await }; Box::pin(fut) } } let fut = async move { - let mut server = TripleServer::new( - dubbo::codegen::ProstCodec::< - super::EchoResponse, - super::EchoRequest, - >::default(), - ); + let mut server = TripleServer::new(dubbo::codegen::ProstCodec::< + super::EchoResponse, + super::EchoRequest, + >::default()); let res = server.unary(UnaryEchoServer { inner }, req).await; Ok(res) }; @@ -240,32 +211,22 @@ pub mod echo_server { struct ServerStreamingEchoServer { inner: _Inner, } - impl ServerStreamingSvc - for ServerStreamingEchoServer { + impl ServerStreamingSvc for ServerStreamingEchoServer { type Response = super::EchoResponse; type ResponseStream = T::ServerStreamingEchoStream; - type Future = BoxFuture< - Response, - dubbo::status::Status, - >; - fn call( - &mut self, - request: Request, - ) -> Self::Future { + type Future = + BoxFuture, dubbo::status::Status>; + fn call(&mut self, request: Request) -> Self::Future { let inner = self.inner.0.clone(); - let fut = async move { - inner.server_streaming_echo(request).await - }; + let fut = async move { inner.server_streaming_echo(request).await }; Box::pin(fut) } } let fut = async move { - let mut server = TripleServer::new( - dubbo::codegen::ProstCodec::< - super::EchoResponse, - super::EchoRequest, - >::default(), - ); + let mut server = TripleServer::new(dubbo::codegen::ProstCodec::< + super::EchoResponse, + super::EchoRequest, + >::default()); let res = server .server_streaming(ServerStreamingEchoServer { inner }, req) .await; @@ -278,31 +239,23 @@ pub mod echo_server { struct ClientStreamingEchoServer { inner: _Inner, } - impl ClientStreamingSvc - for ClientStreamingEchoServer { + impl ClientStreamingSvc for ClientStreamingEchoServer { type Response = super::EchoResponse; - type Future = BoxFuture< - Response, - dubbo::status::Status, - >; + type Future = BoxFuture, dubbo::status::Status>; fn call( &mut self, request: Request>, ) -> Self::Future { let inner = self.inner.0.clone(); - let fut = async move { - inner.client_streaming_echo(request).await - }; + let fut = async move { inner.client_streaming_echo(request).await }; Box::pin(fut) } } let fut = async move { - let mut server = TripleServer::new( - dubbo::codegen::ProstCodec::< - super::EchoResponse, - super::EchoRequest, - >::default(), - ); + let mut server = TripleServer::new(dubbo::codegen::ProstCodec::< + super::EchoResponse, + super::EchoRequest, + >::default()); let res = server .client_streaming(ClientStreamingEchoServer { inner }, req) .await; @@ -315,56 +268,41 @@ pub mod echo_server { struct BidirectionalStreamingEchoServer { inner: _Inner, } - impl StreamingSvc - for BidirectionalStreamingEchoServer { + impl StreamingSvc for BidirectionalStreamingEchoServer { type Response = super::EchoResponse; type ResponseStream = T::BidirectionalStreamingEchoStream; - type Future = BoxFuture< - Response, - dubbo::status::Status, - >; + type Future = + BoxFuture, dubbo::status::Status>; fn call( &mut self, request: Request>, ) -> Self::Future { let inner = self.inner.0.clone(); - let fut = async move { - inner.bidirectional_streaming_echo(request).await - }; + let fut = + async move { inner.bidirectional_streaming_echo(request).await }; Box::pin(fut) } } let fut = async move { - let mut server = TripleServer::new( - dubbo::codegen::ProstCodec::< - super::EchoResponse, - super::EchoRequest, - >::default(), - ); + let mut server = TripleServer::new(dubbo::codegen::ProstCodec::< + super::EchoResponse, + super::EchoRequest, + >::default()); let res = server - .bidi_streaming( - BidirectionalStreamingEchoServer { - inner, - }, - req, - ) + .bidi_streaming(BidirectionalStreamingEchoServer { inner }, req) .await; Ok(res) }; Box::pin(fut) } - _ => { - Box::pin(async move { - Ok( - http::Response::builder() - .status(200) - .header("grpc-status", "12") - .header("content-type", "application/grpc") - .body(empty_body()) - .unwrap(), - ) - }) - } + _ => Box::pin(async move { + Ok(http::Response::builder() + .status(200) + .header("grpc-status", "12") + .header("content-type", "application/grpc") + .body(empty_body()) + .unwrap()) + }), } } } From e9e0a6780e09be8c573532ad82497f32dbcf2b52 Mon Sep 17 00:00:00 2001 From: yangyang <962032265@qq.com> Date: Tue, 20 Dec 2022 21:31:02 +0800 Subject: [PATCH 06/48] Rft: move connection from client to transport mod --- dubbo/src/codegen.rs | 2 +- dubbo/src/protocol/triple/triple_invoker.rs | 2 +- dubbo/src/triple/client/builder.rs | 2 +- dubbo/src/triple/client/mod.rs | 1 - dubbo/src/triple/{client => transport}/connection.rs | 0 dubbo/src/triple/transport/mod.rs | 1 + 6 files changed, 4 insertions(+), 4 deletions(-) rename dubbo/src/triple/{client => transport}/connection.rs (100%) diff --git a/dubbo/src/codegen.rs b/dubbo/src/codegen.rs index f485dc53..16c8c1ee 100644 --- a/dubbo/src/codegen.rs +++ b/dubbo/src/codegen.rs @@ -39,4 +39,4 @@ pub use super::{empty_body, BoxBody, BoxFuture, StdError}; pub use crate::filter::service::FilterService; pub use crate::filter::Filter; pub use crate::triple::client::builder::{ClientBoxService, ClientBuilder}; -pub use crate::triple::client::connection::Connection; +pub use crate::triple::transport::connection::Connection; diff --git a/dubbo/src/protocol/triple/triple_invoker.rs b/dubbo/src/protocol/triple/triple_invoker.rs index 2ec3f4a8..01938513 100644 --- a/dubbo/src/protocol/triple/triple_invoker.rs +++ b/dubbo/src/protocol/triple/triple_invoker.rs @@ -21,7 +21,7 @@ use tower_service::Service; use crate::common::url::Url; use crate::protocol::Invoker; -use crate::triple::client::connection::Connection; +use crate::triple::transport::connection::Connection; #[allow(dead_code)] #[derive(Clone, Default)] diff --git a/dubbo/src/triple/client/builder.rs b/dubbo/src/triple/client/builder.rs index e95070b6..14f30c97 100644 --- a/dubbo/src/triple/client/builder.rs +++ b/dubbo/src/triple/client/builder.rs @@ -20,7 +20,7 @@ use hyper::client::conn::Builder; use tokio::time::Duration; use tower::ServiceBuilder; -use crate::triple::client::connection::Connection; +use crate::triple::transport::connection::Connection; use crate::utils::boxed::BoxService; pub type ClientBoxService = diff --git a/dubbo/src/triple/client/mod.rs b/dubbo/src/triple/client/mod.rs index 48b6b22b..7a8e0131 100644 --- a/dubbo/src/triple/client/mod.rs +++ b/dubbo/src/triple/client/mod.rs @@ -16,7 +16,6 @@ */ pub mod builder; -pub mod connection; pub mod triple; pub use triple::TripleClient; diff --git a/dubbo/src/triple/client/connection.rs b/dubbo/src/triple/transport/connection.rs similarity index 100% rename from dubbo/src/triple/client/connection.rs rename to dubbo/src/triple/transport/connection.rs diff --git a/dubbo/src/triple/transport/mod.rs b/dubbo/src/triple/transport/mod.rs index 228efff3..8d1efb55 100644 --- a/dubbo/src/triple/transport/mod.rs +++ b/dubbo/src/triple/transport/mod.rs @@ -15,6 +15,7 @@ * limitations under the License. */ +pub mod connection; pub mod connector; mod io; pub mod listener; From e67e59e9b6cc489e68fe285a8af6f56737d52f27 Mon Sep 17 00:00:00 2001 From: yangyang <962032265@qq.com> Date: Mon, 26 Dec 2022 20:30:01 +0800 Subject: [PATCH 07/48] Rft(dubbo): add default timeout for client --- dubbo/src/triple/client/builder.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dubbo/src/triple/client/builder.rs b/dubbo/src/triple/client/builder.rs index 14f30c97..087f2a25 100644 --- a/dubbo/src/triple/client/builder.rs +++ b/dubbo/src/triple/client/builder.rs @@ -62,7 +62,7 @@ impl ClientBuilder { pub fn connect(self) -> ClientBoxService { let builder = ServiceBuilder::new(); - let timeout = self.timeout.unwrap_or(0); + let timeout = self.timeout.unwrap_or(5); let builder = builder.timeout(Duration::from_secs(timeout)); let mut b = Builder::new(); From e066505fd557472f521b057e9d02b31dfbfc53ad Mon Sep 17 00:00:00 2001 From: yangyang <962032265@qq.com> Date: Tue, 27 Dec 2022 22:05:47 +0800 Subject: [PATCH 08/48] Ftr: add serverBuilder for Server, support multiple ways to start server --- config/src/config.rs | 6 +- config/src/protocol.rs | 22 +++- docs/generic-protocol-design.md | 12 ++ docs/readme.md | 10 +- dubbo.yaml | 2 + dubbo/readme.md | 24 +++- dubbo/src/codegen.rs | 1 + dubbo/src/common/url.rs | 15 ++- dubbo/src/framework.rs | 17 ++- dubbo/src/protocol/triple/triple_protocol.rs | 4 +- dubbo/src/protocol/triple/triple_server.rs | 57 ++------- dubbo/src/triple/server/builder.rs | 119 +++++++++++++++++++ dubbo/src/triple/server/mod.rs | 1 + dubbo/src/triple/transport/service.rs | 2 +- examples/echo/src/echo/server.rs | 30 +++-- 15 files changed, 246 insertions(+), 76 deletions(-) create mode 100644 dubbo/src/triple/server/builder.rs diff --git a/config/src/config.rs b/config/src/config.rs index a17e31a5..672319a4 100644 --- a/config/src/config.rs +++ b/config/src/config.rs @@ -115,7 +115,8 @@ impl RootConfig { let triple_config = ProtocolConfig::default() .name("triple".to_string()) .ip("0.0.0.0".to_string()) - .port("8888".to_string()); + .port("8888".to_string()) + .listener("tcp".to_string()); let service_config = service_config.add_protocol_configs(triple_config); self.service @@ -135,7 +136,8 @@ impl RootConfig { ProtocolConfig::default() .name("triple".to_string()) .ip("0.0.0.0".to_string()) - .port("8889".to_string()), + .port("8889".to_string()) + .listener("tcp".to_string()), ); provider.services = self.service.clone(); diff --git a/config/src/protocol.rs b/config/src/protocol.rs index 24531529..632e912d 100644 --- a/config/src/protocol.rs +++ b/config/src/protocol.rs @@ -24,6 +24,7 @@ pub struct ProtocolConfig { pub ip: String, pub port: String, pub name: String, + pub listener: String, #[serde(skip_serializing, skip_deserializing)] pub params: HashMap, @@ -42,11 +43,30 @@ impl ProtocolConfig { Self { port, ..self } } + pub fn listener(self, listener: String) -> Self { + Self { listener, ..self } + } + pub fn params(self, params: HashMap) -> Self { Self { params, ..self } } + pub fn add_param(mut self, key: String, value: String) -> Self { + self.params.insert(key, value); + self + } + pub fn to_url(&self) -> String { - format!("{}://{}:{}", self.name, self.ip, self.port) + let mut params_vec: Vec = Vec::new(); + for (k, v) in self.params.iter() { + // let tmp = format!("{}={}", k, v); + params_vec.push(format!("{}={}", k, v)); + } + let param = params_vec.join("&"); + + format!( + "{}://{}:{}?listener={}&{}", + self.name, self.ip, self.port, self.listener, param + ) } } diff --git a/docs/generic-protocol-design.md b/docs/generic-protocol-design.md index d91ed857..709e3481 100644 --- a/docs/generic-protocol-design.md +++ b/docs/generic-protocol-design.md @@ -18,10 +18,22 @@ Protocol模块的核心功能: + 通用、高效的Listener层 + 等等 +Protocol API支持管理多个底层协议的server以及在一个server上暴露多个服务 ++ 多个底层通信的server:location(ip:port): server ++ 一个server上暴露多个服务: + ### Exporter ### Invoker +Invoker: 客户端通用能力的封装。获取需要一个private withInvoker 接口 + +Invoker应该是基于Connection(Service)实现的Service。并且进行扩展新的接口。 +protocol.rs模块:根据Url返回初始化好的Invoker实例。 ++ 如何设计Invoker接口:扩展Service接口 ++ cluster模块如何使用Invoker实例呢?这里需要画一个数据流转图 ++ 如何将初始化好的Invoker与tower::Layer相结合 + Invoker提供的通用的接口,使得dubbo在不同的协议下遵循相同的接口抽象。 在Invoker中,需要做的功能包括 diff --git a/docs/readme.md b/docs/readme.md index ee2738f7..bba7562b 100644 --- a/docs/readme.md +++ b/docs/readme.md @@ -1,3 +1,11 @@ # Readme -There is some RFCs of dubbo-rust design. \ No newline at end of file +There is some RFCs of dubbo-rust design. + +## 关于配置的一些约定(暂时) + +所有的服务只能注册到一个或多个注册中心 +所有的服务只能使用Triple进行通信 +Triple只能对外暴露一个端口 + +一个协议上的server注册到 \ No newline at end of file diff --git a/dubbo.yaml b/dubbo.yaml index f6e7aa95..e14b5301 100644 --- a/dubbo.yaml +++ b/dubbo.yaml @@ -11,6 +11,7 @@ service: ip: 0.0.0.0 port: '8888' name: triple + listener: tcp # helloworld.Greeter: # version: 1.0.0 # group: test @@ -22,3 +23,4 @@ protocols: ip: 0.0.0.0 port: '8888' name: triple + listener: tcp diff --git a/dubbo/readme.md b/dubbo/readme.md index 0adef956..8dd713d4 100644 --- a/dubbo/readme.md +++ b/dubbo/readme.md @@ -1 +1,23 @@ -# Introduce \ No newline at end of file +# Introduce + +## Filter实现 + +客户端Filter和服务端Filter +将TripleClient扩展为通用的InvokerClient + ++ 测试客户端filter ++ 服务端filter接口设计,以及测试 ++ 服务注册接口 ++ 服务发现接口设计 + +EchoClient -> TripleClient -> FilterService -> Connection -> hyper::Connect + + +memory registry clone实现 +将服务注册接入到framework中 +url模型如何用于多个场景 +protocol模块实现 + +registry config(yaml) -> registry config(memory) -> + +Init函数初始化配置即:根据RootConfig来初始化对应的ServiceConfig \ No newline at end of file diff --git a/dubbo/src/codegen.rs b/dubbo/src/codegen.rs index 16c8c1ee..ef656469 100644 --- a/dubbo/src/codegen.rs +++ b/dubbo/src/codegen.rs @@ -39,4 +39,5 @@ pub use super::{empty_body, BoxBody, BoxFuture, StdError}; pub use crate::filter::service::FilterService; pub use crate::filter::Filter; pub use crate::triple::client::builder::{ClientBoxService, ClientBuilder}; +pub use crate::triple::server::builder::ServerBuilder; pub use crate::triple::transport::connection::Connection; diff --git a/dubbo/src/common/url.rs b/dubbo/src/common/url.rs index 4f79b300..29f9e2c0 100644 --- a/dubbo/src/common/url.rs +++ b/dubbo/src/common/url.rs @@ -41,7 +41,7 @@ impl Url { tracing::error!("fail to parse url({}), err: {:?}", url, err); }) .unwrap(); - Some(Self { + let mut u = Self { uri: uri.to_string(), protocol: uri.scheme_str()?.to_string(), ip: uri.authority()?.host().to_string(), @@ -54,7 +54,18 @@ impl Url { .map(|x| x.to_string()) .collect::>(), params: HashMap::new(), - }) + }; + if uri.query().is_some() { + u.decode(uri.query().unwrap().to_string()); + u.service_key = u + .get_param("service_names".to_string()) + .unwrap() + .split(',') + .map(|x| x.to_string()) + .collect::>(); + } + + Some(u) } pub fn get_service_name(&self) -> Vec { diff --git a/dubbo/src/framework.rs b/dubbo/src/framework.rs index 412ff36b..354b65a0 100644 --- a/dubbo/src/framework.rs +++ b/dubbo/src/framework.rs @@ -77,12 +77,17 @@ impl Dubbo { protocol_url } }; - let protocol_url = format!( - "{}/{}/{}", - &protocol_url.to_url(), - service_config.name, - service_name - ); + // let protocol_url = format!( + // "{}/{}/{}", + // &protocol_url.to_url(), + // service_config.name, + // service_name + // ); + // service_names may be multiple + let protocol_url = protocol_url + .to_owned() + .add_param("service_names".to_string(), service_name.to_string()); + let protocol_url = protocol_url.to_url(); tracing::info!("url: {}", protocol_url); let protocol_url = Url::from_url(&protocol_url) diff --git a/dubbo/src/protocol/triple/triple_protocol.rs b/dubbo/src/protocol/triple/triple_protocol.rs index 5b713ada..037bfb7a 100644 --- a/dubbo/src/protocol/triple/triple_protocol.rs +++ b/dubbo/src/protocol/triple/triple_protocol.rs @@ -60,10 +60,10 @@ impl Protocol for TripleProtocol { } async fn export(mut self, url: Url) -> BoxExporter { - let server = TripleServer::new(url.service_key.clone()); + let server = TripleServer::new(); self.servers .insert(url.service_key.join(","), server.clone()); - server.serve(url.to_url()).await; + server.serve(url).await; Box::new(TripleExporter::new()) } diff --git a/dubbo/src/protocol/triple/triple_server.rs b/dubbo/src/protocol/triple/triple_server.rs index b85dbe17..d297a881 100644 --- a/dubbo/src/protocol/triple/triple_server.rs +++ b/dubbo/src/protocol/triple/triple_server.rs @@ -15,65 +15,22 @@ * limitations under the License. */ -use std::{net::ToSocketAddrs, str::FromStr}; - -use crate::triple::transport::DubboServer; +use crate::{common::url::Url, triple::server::builder::ServerBuilder}; #[derive(Default, Clone)] pub struct TripleServer { - s: DubboServer, - service_names: Vec, + builder: ServerBuilder, } impl TripleServer { - pub fn new(names: Vec) -> TripleServer { + pub fn new() -> TripleServer { Self { - service_names: names, - s: DubboServer::new(), + builder: ServerBuilder::new(), } } - pub async fn serve(mut self, url: String) { - { - let lock = super::TRIPLE_SERVICES.read().unwrap(); - for name in self.service_names.iter() { - if lock.get(name).is_none() { - tracing::warn!("service ({}) not register", name); - continue; - } - let svc = lock.get(name).unwrap(); - - self.s = self.s.add_service(name.clone(), svc.clone()); - } - } - - let uri = match http::Uri::from_str(&url) { - Ok(v) => v, - Err(err) => { - tracing::error!("http uri parse error: {}, url: {:?}", err, &url); - return; - } - }; - - let authority = match uri.authority() { - Some(v) => v.to_owned(), - None => { - tracing::error!("http authority is none"); - return; - } - }; - - self.s - .with_listener("tcp".to_string()) - .serve( - authority - .to_string() - .to_socket_addrs() - .unwrap() - .next() - .unwrap(), - ) - .await - .unwrap(); + pub async fn serve(mut self, url: Url) { + self.builder = ServerBuilder::from(url); + self.builder.build().serve().await.unwrap() } } diff --git a/dubbo/src/triple/server/builder.rs b/dubbo/src/triple/server/builder.rs new file mode 100644 index 00000000..ce494db9 --- /dev/null +++ b/dubbo/src/triple/server/builder.rs @@ -0,0 +1,119 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use std::{ + net::{SocketAddr, ToSocketAddrs}, + str::FromStr, +}; + +use http::Uri; +use http::{Request, Response}; +use hyper::body::Body; +use tower_service::Service; + +use crate::BoxBody; +use crate::{common::url::Url, triple::transport::DubboServer}; + +#[derive(Clone, Default, Debug)] +pub struct ServerBuilder { + pub listener: String, + pub addr: Option, + pub service_names: Vec, + server: DubboServer, +} + +impl ServerBuilder { + pub fn new() -> Self { + ServerBuilder::default() + } + + pub fn with_listener(self, listener: String) -> ServerBuilder { + Self { listener, ..self } + } + + pub fn with_addr(self, addr: &'static str) -> ServerBuilder { + Self { + addr: addr.to_socket_addrs().unwrap().next(), + ..self + } + } + + pub fn with_service_names(self, service_names: Vec) -> ServerBuilder { + Self { + service_names, + ..self + } + } + + pub fn build(self) -> Self { + let mut server = self.server.with_listener(self.listener.clone()); + { + let lock = crate::protocol::triple::TRIPLE_SERVICES.read().unwrap(); + for name in self.service_names.iter() { + if lock.get(name).is_none() { + tracing::warn!("service ({}) not register", name); + continue; + } + let svc = lock.get(name).unwrap(); + + server = server.add_service(name.clone(), svc.clone()); + } + } + Self { server, ..self } + } + + pub fn add_service(self, name: String, service: S) -> Self + where + S: Service, Response = Response, Error = std::convert::Infallible> + + Clone + + Send + + 'static, + S::Future: Send + 'static, + S::Error: Into + Send + 'static, + { + Self { + server: self.server.add_service(name, service), + ..self + } + } + + pub async fn serve(self) -> Result<(), crate::Error> { + tracing::info!("server starting. addr: {:?}", self.addr); + self.server.serve(self.addr.unwrap()).await + } +} + +impl From for ServerBuilder { + fn from(u: Url) -> Self { + let uri = match http::Uri::from_str(&u.to_url()) { + Ok(v) => v, + Err(err) => { + tracing::error!("http uri parse error: {}, url: {:?}", err, &u); + Uri::default() + } + }; + + let authority = uri.authority().unwrap(); + + Self { + listener: u.get_param("listener".to_string()).unwrap(), + addr: authority.to_string().to_socket_addrs().unwrap().next(), + service_names: u.service_key, + server: DubboServer::default(), + } + } +} diff --git a/dubbo/src/triple/server/mod.rs b/dubbo/src/triple/server/mod.rs index 93172876..b36f7693 100644 --- a/dubbo/src/triple/server/mod.rs +++ b/dubbo/src/triple/server/mod.rs @@ -15,6 +15,7 @@ * limitations under the License. */ +pub mod builder; pub mod service; pub mod triple; diff --git a/dubbo/src/triple/transport/service.rs b/dubbo/src/triple/transport/service.rs index f8abc699..a049c1d6 100644 --- a/dubbo/src/triple/transport/service.rs +++ b/dubbo/src/triple/transport/service.rs @@ -27,7 +27,7 @@ use super::listener::get_listener; use super::router::DubboRouter; use crate::BoxBody; -#[derive(Default, Clone)] +#[derive(Default, Clone, Debug)] pub struct DubboServer { accept_http2: bool, init_stream_window_size: Option, diff --git a/examples/echo/src/echo/server.rs b/examples/echo/src/echo/server.rs index 6f2d610f..c14a67fa 100644 --- a/examples/echo/src/echo/server.rs +++ b/examples/echo/src/echo/server.rs @@ -27,8 +27,8 @@ use tokio::sync::mpsc; use tokio_stream::wrappers::ReceiverStream; use dubbo::codegen::*; -use dubbo::Dubbo; -use dubbo_config::RootConfig; +// use dubbo::Dubbo; +// use dubbo_config::RootConfig; use example_echo::protos::hello_echo::{ echo_server::{register_server, Echo, EchoServer}, EchoRequest, EchoResponse, @@ -65,15 +65,25 @@ async fn main() { dubbo::utils::boxed_clone::BoxCloneService::new(context_filter), ); + // 1. 通过读取配置文件来初始化 // Dubbo::new().start().await; - Dubbo::new() - .with_config({ - let mut r = RootConfig::new(); - r.test_config(); - r - }) - .start() - .await; + + // 2. 通过自定义配置来初始化 + // Dubbo::new() + // .with_config({ + // let mut r = RootConfig::new(); + // r.test_config(); + // r + // }) + // .start() + // .await; + + // 3. 通过serverbuilder来初始化Server + let builder = ServerBuilder::new() + .with_listener("tcp".to_string()) + .with_service_names(vec!["grpc.examples.echo.Echo".to_string()]) + .with_addr("0.0.0.0:8888"); + builder.build().serve().await.unwrap(); } #[allow(dead_code)] From bd4be4e36721c9bb230602b43fa6dd321bfd85c5 Mon Sep 17 00:00:00 2001 From: yangyang <962032265@qq.com> Date: Tue, 27 Dec 2022 22:10:03 +0800 Subject: [PATCH 09/48] Rft(examples): update yaml --- examples/greeter/dubbo.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/greeter/dubbo.yaml b/examples/greeter/dubbo.yaml index fa578d4c..b6552f80 100644 --- a/examples/greeter/dubbo.yaml +++ b/examples/greeter/dubbo.yaml @@ -14,3 +14,4 @@ protocols: ip: 0.0.0.0 port: '8888' name: triple + listener: tcp From 80f35028d49e8eedb860b74a9a9270ee07fe66be Mon Sep 17 00:00:00 2001 From: yangyang <962032265@qq.com> Date: Mon, 16 Jan 2023 22:27:37 +0800 Subject: [PATCH 10/48] refactor(dubbo): update invoker trait --- dubbo/src/protocol/mod.rs | 2 ++ dubbo/src/protocol/triple/triple_invoker.rs | 24 ++++++++++----------- dubbo/src/triple/client/builder.rs | 2 +- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/dubbo/src/protocol/mod.rs b/dubbo/src/protocol/mod.rs index 20042cb5..f58212d3 100644 --- a/dubbo/src/protocol/mod.rs +++ b/dubbo/src/protocol/mod.rs @@ -48,6 +48,8 @@ pub trait Invoker { fn get_url(&self) -> Url; + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll>; + fn call(&mut self, req: ReqBody) -> Self::Future; } diff --git a/dubbo/src/protocol/triple/triple_invoker.rs b/dubbo/src/protocol/triple/triple_invoker.rs index 01938513..1cbe2ed7 100644 --- a/dubbo/src/protocol/triple/triple_invoker.rs +++ b/dubbo/src/protocol/triple/triple_invoker.rs @@ -21,13 +21,11 @@ use tower_service::Service; use crate::common::url::Url; use crate::protocol::Invoker; -use crate::triple::transport::connection::Connection; +use crate::triple::client::builder::{ClientBoxService, ClientBuilder}; -#[allow(dead_code)] -#[derive(Clone, Default)] pub struct TripleInvoker { url: Url, - conn: Connection, + conn: ClientBoxService, } impl TripleInvoker { @@ -35,17 +33,12 @@ impl TripleInvoker { let uri = http::Uri::from_str(&url.to_url()).unwrap(); Self { url, - conn: Connection::new().with_host(uri), + conn: ClientBuilder::from(uri).connect(), } } } -impl Invoker> for TripleInvoker -where - ReqBody: http_body::Body + Unpin + Send + 'static, - ReqBody::Error: Into, - ReqBody::Data: Send + Unpin, -{ +impl Invoker> for TripleInvoker { type Response = http::Response; type Error = crate::Error; @@ -56,7 +49,14 @@ where self.url.clone() } - fn call(&mut self, req: http::Request) -> Self::Future { + fn call(&mut self, req: http::Request) -> Self::Future { self.conn.call(req) } + + fn poll_ready( + &mut self, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + self.conn.poll_ready(cx) + } } diff --git a/dubbo/src/triple/client/builder.rs b/dubbo/src/triple/client/builder.rs index 087f2a25..67d635a3 100644 --- a/dubbo/src/triple/client/builder.rs +++ b/dubbo/src/triple/client/builder.rs @@ -81,7 +81,7 @@ impl From for ClientBuilder { Self { uri: u, timeout: None, - connector: "", + connector: "tcp", } } } From 0fcb29fde2a79334b8a169f33a55092087f732b5 Mon Sep 17 00:00:00 2001 From: yangyang <962032265@qq.com> Date: Thu, 11 May 2023 22:54:28 +0800 Subject: [PATCH 11/48] refactor(cluster): add Cluster MockImpl --- dubbo/Cargo.toml | 1 + dubbo/src/cluster/mod.rs | 82 ++++++++++++++++++--- dubbo/src/protocol/mod.rs | 10 ++- dubbo/src/protocol/triple/triple_invoker.rs | 19 +++-- dubbo/src/triple/client/builder.rs | 4 +- 5 files changed, 94 insertions(+), 22 deletions(-) diff --git a/dubbo/Cargo.toml b/dubbo/Cargo.toml index 94a2bd86..91b19d6f 100644 --- a/dubbo/Cargo.toml +++ b/dubbo/Cargo.toml @@ -34,6 +34,7 @@ axum = "0.5.9" async-stream = "0.3" flate2 = "1.0" aws-smithy-http = "0.54.1" +dyn-clone = "1.0.11" itertools.workspace = true urlencoding.workspace = true lazy_static.workspace = true diff --git a/dubbo/src/cluster/mod.rs b/dubbo/src/cluster/mod.rs index b6d8a7c2..e7b0a03a 100644 --- a/dubbo/src/cluster/mod.rs +++ b/dubbo/src/cluster/mod.rs @@ -15,24 +15,38 @@ * limitations under the License. */ -use std::{sync::Arc, task::Poll}; +use std::{sync::Arc, task::Poll, fmt::Debug, collections::HashMap}; use aws_smithy_http::body::SdkBody; +use dubbo_base::Url; use tower_service::Service; -use crate::{empty_body, protocol::BoxInvoker}; +use crate::{empty_body, protocol::{BoxInvoker, Invoker}, invocation::RpcInvocation}; pub mod directory; pub mod loadbalance; pub mod support; -pub trait Directory { - fn list(&self, meta: String) -> Vec; +pub trait Directory: Debug { + fn list(&self, invocation: Arc) -> Vec; fn is_empty(&self) -> bool; } -type BoxDirectory = Box; +type BoxDirectory = Box; +pub trait Cluster { + fn join(&self, dir: BoxDirectory) -> BoxInvoker; +} + +#[derive(Debug)] +pub struct MockCluster {} + +impl Cluster for MockCluster { + fn join(&self, dir: BoxDirectory) -> BoxInvoker { + Box::new(FailoverCluster::new(dir)) + } +} +#[derive(Clone, Debug)] pub struct FailoverCluster { dir: Arc, } @@ -43,7 +57,7 @@ impl FailoverCluster { } } -impl Service> for FailoverCluster { +impl Invoker> for FailoverCluster { type Response = http::Response; type Error = crate::Error; @@ -66,7 +80,9 @@ impl Service> for FailoverCluster { .method(req.method().clone()); *clone_req.headers_mut().unwrap() = req.headers().clone(); let r = clone_req.body(clone_body).unwrap(); - let invokers = self.dir.list("service_name".to_string()); + let invokers = self.dir.list( + RpcInvocation::default().with_service_unique_name("hello".to_string()).into() + ); for mut invoker in invokers { let fut = async move { let res = invoker.call(r).await; @@ -83,19 +99,63 @@ impl Service> for FailoverCluster { .unwrap()) }) } + + fn get_url(&self) -> dubbo_base::Url { + Url::from_url("triple://127.0.0.1:8888/helloworld.Greeter").unwrap() + } +} + +#[derive(Debug, Default)] +pub struct MockDirectory { + router_chain: RouterChain, + invokers: Vec } -pub struct MockDirectory {} +impl MockDirectory { + pub fn new(invokers: Vec) -> MockDirectory { + Self { router_chain: RouterChain::default(), invokers } + } +} impl Directory for MockDirectory { - fn list(&self, _meta: String) -> Vec { + fn list(&self, invo: Arc) -> Vec { // tracing::info!("MockDirectory: {}", meta); - // let u = Url::from_url("triple://127.0.0.1:8888/helloworld.Greeter").unwrap(); + let u = Url::from_url("triple://127.0.0.1:8888/helloworld.Greeter").unwrap(); // vec![Box::new(TripleInvoker::new(u))] - todo!() + self.router_chain.route(u, invo); + self.invokers.clone() } fn is_empty(&self) -> bool { false } } + +#[derive(Debug, Default)] +pub struct RouterChain { + router: HashMap, + invokers: Vec +} + +impl RouterChain { + pub fn route(&self, url: Url, invo: Arc) -> Vec { + let r = self.router.get("mock").unwrap(); + r.route(self.invokers.clone(), url, invo) + } +} + + +pub trait Router: Debug{ + fn route(&self, invokers: Vec, url: Url, invo: Arc) -> Vec; +} + +pub type BoxRouter = Box; + +#[derive(Debug, Default)] +pub struct MockRouter {} + +impl Router for MockRouter { + fn route(&self, invokers: Vec, url: Url, invo: Arc) -> Vec { + invokers + } +} \ No newline at end of file diff --git a/dubbo/src/protocol/mod.rs b/dubbo/src/protocol/mod.rs index 58dca5fd..629513e6 100644 --- a/dubbo/src/protocol/mod.rs +++ b/dubbo/src/protocol/mod.rs @@ -23,6 +23,7 @@ use std::{ use async_trait::async_trait; use aws_smithy_http::body::SdkBody; +use dyn_clone::DynClone; use tower_service::Service; use dubbo_base::Url; @@ -43,7 +44,7 @@ pub trait Exporter { fn unexport(&self); } -pub trait Invoker: Debug { +pub trait Invoker: Debug + DynClone { type Response; type Error; @@ -68,6 +69,13 @@ pub type BoxInvoker = Box< + Sync, >; +dyn_clone::clone_trait_object!(Invoker< + http::Request, + Response = http::Response, + Error = crate::Error, + Future = crate::BoxFuture, crate::Error>, +>); + pub struct WrapperInvoker(T); impl Service> for WrapperInvoker diff --git a/dubbo/src/protocol/triple/triple_invoker.rs b/dubbo/src/protocol/triple/triple_invoker.rs index 6139cc91..fb47bbed 100644 --- a/dubbo/src/protocol/triple/triple_invoker.rs +++ b/dubbo/src/protocol/triple/triple_invoker.rs @@ -18,23 +18,26 @@ use aws_smithy_http::body::SdkBody; use dubbo_base::Url; use std::fmt::{Debug, Formatter}; +use std::str::FromStr; use tower_service::Service; -use crate::{protocol::Invoker, triple::client::builder::ClientBoxService}; +use crate::{protocol::Invoker, triple::client::builder::ClientBoxService, utils::boxed_clone::BoxCloneService}; +use crate::triple::transport::connection::Connection; +#[derive(Clone)] pub struct TripleInvoker { url: Url, conn: ClientBoxService, } impl TripleInvoker { - // pub fn new(url: Url) -> TripleInvoker { - // let uri = http::Uri::from_str(&url.to_url()).unwrap(); - // Self { - // url, - // conn: ClientBuilder::from_uri(&uri).build()connect(), - // } - // } + pub fn new(url: Url) -> TripleInvoker { + let uri = http::Uri::from_str(&url.to_url()).unwrap(); + Self { + url, + conn: BoxCloneService::new(Connection::new().with_host(uri)), + } + } } impl Debug for TripleInvoker { diff --git a/dubbo/src/triple/client/builder.rs b/dubbo/src/triple/client/builder.rs index cf667ccd..ea1fd26d 100644 --- a/dubbo/src/triple/client/builder.rs +++ b/dubbo/src/triple/client/builder.rs @@ -19,7 +19,7 @@ use crate::{ cluster::directory::StaticDirectory, codegen::{ClusterInvoker, Directory, RegistryDirectory}, triple::compression::CompressionEncoding, - utils::boxed::BoxService, + utils::{boxed::BoxService, boxed_clone::BoxCloneService}, }; use aws_smithy_http::body::SdkBody; @@ -27,7 +27,7 @@ use aws_smithy_http::body::SdkBody; use super::TripleClient; pub type ClientBoxService = - BoxService, http::Response, crate::Error>; + BoxCloneService, http::Response, crate::Error>; #[derive(Clone, Debug, Default)] pub struct ClientBuilder { From 2cd76b66caeaf168b4f218d2138b55c84df65b1a Mon Sep 17 00:00:00 2001 From: yangyang <962032265@qq.com> Date: Thu, 11 May 2023 23:46:44 +0800 Subject: [PATCH 12/48] refactor(triple): use ClientBuilder to init Cluster ability --- dubbo/src/cluster/mod.rs | 43 ++++++++++++++------- dubbo/src/protocol/mod.rs | 14 ++++--- dubbo/src/protocol/triple/triple_invoker.rs | 13 +++++-- dubbo/src/triple/client/builder.rs | 39 +++++++++++++++++-- dubbo/src/triple/client/triple.rs | 31 +++++---------- 5 files changed, 92 insertions(+), 48 deletions(-) diff --git a/dubbo/src/cluster/mod.rs b/dubbo/src/cluster/mod.rs index e7b0a03a..d2c2adb7 100644 --- a/dubbo/src/cluster/mod.rs +++ b/dubbo/src/cluster/mod.rs @@ -15,13 +15,16 @@ * limitations under the License. */ -use std::{sync::Arc, task::Poll, fmt::Debug, collections::HashMap}; +use std::{collections::HashMap, fmt::Debug, sync::Arc, task::Poll}; use aws_smithy_http::body::SdkBody; use dubbo_base::Url; -use tower_service::Service; -use crate::{empty_body, protocol::{BoxInvoker, Invoker}, invocation::RpcInvocation}; +use crate::{ + empty_body, + invocation::RpcInvocation, + protocol::{BoxInvoker, Invoker}, +}; pub mod directory; pub mod loadbalance; @@ -38,7 +41,7 @@ pub trait Cluster { fn join(&self, dir: BoxDirectory) -> BoxInvoker; } -#[derive(Debug)] +#[derive(Debug, Default)] pub struct MockCluster {} impl Cluster for MockCluster { @@ -81,7 +84,9 @@ impl Invoker> for FailoverCluster { *clone_req.headers_mut().unwrap() = req.headers().clone(); let r = clone_req.body(clone_body).unwrap(); let invokers = self.dir.list( - RpcInvocation::default().with_service_unique_name("hello".to_string()).into() + RpcInvocation::default() + .with_service_unique_name("hello".to_string()) + .into(), ); for mut invoker in invokers { let fut = async move { @@ -108,12 +113,15 @@ impl Invoker> for FailoverCluster { #[derive(Debug, Default)] pub struct MockDirectory { router_chain: RouterChain, - invokers: Vec + invokers: Vec, } impl MockDirectory { pub fn new(invokers: Vec) -> MockDirectory { - Self { router_chain: RouterChain::default(), invokers } + Self { + router_chain: RouterChain::default(), + invokers, + } } } @@ -134,7 +142,7 @@ impl Directory for MockDirectory { #[derive(Debug, Default)] pub struct RouterChain { router: HashMap, - invokers: Vec + invokers: Vec, } impl RouterChain { @@ -144,9 +152,13 @@ impl RouterChain { } } - -pub trait Router: Debug{ - fn route(&self, invokers: Vec, url: Url, invo: Arc) -> Vec; +pub trait Router: Debug { + fn route( + &self, + invokers: Vec, + url: Url, + invo: Arc, + ) -> Vec; } pub type BoxRouter = Box; @@ -155,7 +167,12 @@ pub type BoxRouter = Box; pub struct MockRouter {} impl Router for MockRouter { - fn route(&self, invokers: Vec, url: Url, invo: Arc) -> Vec { + fn route( + &self, + invokers: Vec, + _url: Url, + _invo: Arc, + ) -> Vec { invokers } -} \ No newline at end of file +} diff --git a/dubbo/src/protocol/mod.rs b/dubbo/src/protocol/mod.rs index 629513e6..145bcc8e 100644 --- a/dubbo/src/protocol/mod.rs +++ b/dubbo/src/protocol/mod.rs @@ -69,12 +69,14 @@ pub type BoxInvoker = Box< + Sync, >; -dyn_clone::clone_trait_object!(Invoker< - http::Request, - Response = http::Response, - Error = crate::Error, - Future = crate::BoxFuture, crate::Error>, ->); +dyn_clone::clone_trait_object!( + Invoker< + http::Request, + Response = http::Response, + Error = crate::Error, + Future = crate::BoxFuture, crate::Error>, + > +); pub struct WrapperInvoker(T); diff --git a/dubbo/src/protocol/triple/triple_invoker.rs b/dubbo/src/protocol/triple/triple_invoker.rs index fb47bbed..fb661f9e 100644 --- a/dubbo/src/protocol/triple/triple_invoker.rs +++ b/dubbo/src/protocol/triple/triple_invoker.rs @@ -17,12 +17,17 @@ use aws_smithy_http::body::SdkBody; use dubbo_base::Url; -use std::fmt::{Debug, Formatter}; -use std::str::FromStr; +use std::{ + fmt::{Debug, Formatter}, + str::FromStr, +}; use tower_service::Service; -use crate::{protocol::Invoker, triple::client::builder::ClientBoxService, utils::boxed_clone::BoxCloneService}; -use crate::triple::transport::connection::Connection; +use crate::{ + protocol::Invoker, + triple::{client::builder::ClientBoxService, transport::connection::Connection}, + utils::boxed_clone::BoxCloneService, +}; #[derive(Clone)] pub struct TripleInvoker { diff --git a/dubbo/src/triple/client/builder.rs b/dubbo/src/triple/client/builder.rs index ea1fd26d..26ab9e9f 100644 --- a/dubbo/src/triple/client/builder.rs +++ b/dubbo/src/triple/client/builder.rs @@ -16,13 +16,14 @@ */ use crate::{ - cluster::directory::StaticDirectory, - codegen::{ClusterInvoker, Directory, RegistryDirectory}, + cluster::{directory::StaticDirectory, Cluster, MockCluster, MockDirectory}, + codegen::{ClusterInvoker, Directory, RegistryDirectory, TripleInvoker}, triple::compression::CompressionEncoding, - utils::{boxed::BoxService, boxed_clone::BoxCloneService}, + utils::boxed_clone::BoxCloneService, }; use aws_smithy_http::body::SdkBody; +use dubbo_base::Url; use super::TripleClient; @@ -35,6 +36,9 @@ pub struct ClientBuilder { pub connector: &'static str, directory: Option>, cluster_invoker: Option, + pub direct: bool, + host: String, + uri: Option, } impl ClientBuilder { @@ -44,6 +48,9 @@ impl ClientBuilder { connector: "", directory: None, cluster_invoker: None, + direct: true, + uri: None, + host: "".to_string(), } } @@ -53,6 +60,9 @@ impl ClientBuilder { connector: "", directory: Some(Box::new(StaticDirectory::new(&host))), cluster_invoker: None, + direct: true, + uri: None, + host: host.clone().to_string(), } } @@ -62,6 +72,9 @@ impl ClientBuilder { connector: "", directory: Some(Box::new(StaticDirectory::from_uri(&uri))), cluster_invoker: None, + direct: true, + uri: Some(uri.clone()), + host: "".to_string(), } } @@ -104,11 +117,29 @@ impl ClientBuilder { } } + pub fn with_direct(self, direct: bool) -> Self { + Self { direct, ..self } + } + pub fn build(self) -> TripleClient { - TripleClient { + let mut cli = TripleClient { send_compression_encoding: Some(CompressionEncoding::Gzip), directory: self.directory, cluster_invoker: self.cluster_invoker, + invoker: None, + }; + if self.direct { + cli.invoker = Some(Box::new(TripleInvoker::new( + Url::from_url(&self.host).unwrap(), + ))); + return cli; } + + let cluster = MockCluster::default().join(Box::new(MockDirectory::new(vec![Box::new( + TripleInvoker::new(Url::from_url("http://127.0.0.1:8888").unwrap()), + )]))); + + cli.invoker = Some(cluster); + cli } } diff --git a/dubbo/src/triple/client/triple.rs b/dubbo/src/triple/client/triple.rs index 56edb96b..3585b486 100644 --- a/dubbo/src/triple/client/triple.rs +++ b/dubbo/src/triple/client/triple.rs @@ -30,6 +30,7 @@ use crate::codegen::{ClusterInvoker, Directory, RpcInvocation}; use crate::{ cluster::support::cluster_invoker::ClusterRequestBuilder, invocation::{IntoStreamingRequest, Metadata, Request, Response}, + protocol::BoxInvoker, triple::{codec::Codec, compression::CompressionEncoding, decode::Decoding, encode::encode}, }; @@ -38,11 +39,12 @@ pub struct TripleClient { pub(crate) send_compression_encoding: Option, pub(crate) directory: Option>, pub(crate) cluster_invoker: Option, + pub invoker: Option, } impl TripleClient { pub fn connect(host: String) -> Self { - let builder = ClientBuilder::from_static(&host); + let builder = ClientBuilder::from_static(&host).with_direct(true); builder.build() } @@ -150,27 +152,14 @@ impl TripleClient { ) .into_stream(); let body = hyper::Body::wrap_stream(body_stream); - let sdk_body = SdkBody::from(body); - let arc_invocation = Arc::new(invocation); - let req; - let http_uri; - if self.cluster_invoker.is_some() { - let cluster_invoker = self.cluster_invoker.as_ref().unwrap().clone(); - req = cluster_invoker.build_req(self, path, arc_invocation.clone(), sdk_body); - http_uri = req.uri().clone(); - } else { - let url_list = self - .directory - .as_ref() - .expect("msg") - .list(arc_invocation.clone()); - let real_url = url_list.choose(&mut rand::thread_rng()).expect("msg"); - http_uri = - http::Uri::from_str(&format!("http://{}:{}/", real_url.ip, real_url.port)).unwrap(); - req = self.map_request(http_uri.clone(), path, sdk_body); - } + let bytes = hyper::body::to_bytes(body).await.unwrap(); + let sdk_body = SdkBody::from(bytes); + + // let mut conn = Connection::new().with_host(http_uri); + let mut conn = self.invoker.clone().unwrap(); + let http_uri = http::Uri::from_str(&conn.get_url().to_url()).unwrap(); + let req = self.map_request(http_uri.clone(), path, sdk_body); - let mut conn = Connection::new().with_host(http_uri); let response = conn .call(req) .await From 3cba7dc269a9ea0c171b06a1dfe9a1963bff7527 Mon Sep 17 00:00:00 2001 From: Yang Yang <962032265q@gmail.com> Date: Tue, 16 May 2023 10:51:59 +0800 Subject: [PATCH 13/48] Update builder.rs update default direct value --- dubbo/src/triple/client/builder.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dubbo/src/triple/client/builder.rs b/dubbo/src/triple/client/builder.rs index 26ab9e9f..5ec8bda4 100644 --- a/dubbo/src/triple/client/builder.rs +++ b/dubbo/src/triple/client/builder.rs @@ -48,7 +48,7 @@ impl ClientBuilder { connector: "", directory: None, cluster_invoker: None, - direct: true, + direct: false, uri: None, host: "".to_string(), } From 1e7285e799e13e0fd569cb1c81f2f01d5ee72ca1 Mon Sep 17 00:00:00 2001 From: Yang Yang <962032265q@gmail.com> Date: Tue, 16 May 2023 10:57:51 +0800 Subject: [PATCH 14/48] Update triple.rs handle unused var --- dubbo/src/triple/client/triple.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dubbo/src/triple/client/triple.rs b/dubbo/src/triple/client/triple.rs index 3585b486..eb7934dd 100644 --- a/dubbo/src/triple/client/triple.rs +++ b/dubbo/src/triple/client/triple.rs @@ -137,7 +137,7 @@ impl TripleClient { req: Request, mut codec: C, path: http::uri::PathAndQuery, - invocation: RpcInvocation, + _invocation: RpcInvocation, ) -> Result, crate::status::Status> where C: Codec, From 2c567a73c08c14b3e9507f1557f07797d7d055ec Mon Sep 17 00:00:00 2001 From: Yang Yang <962032265q@gmail.com> Date: Tue, 16 May 2023 11:17:10 +0800 Subject: [PATCH 15/48] Update mod.rs comment some codes --- dubbo/src/cluster/mod.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/dubbo/src/cluster/mod.rs b/dubbo/src/cluster/mod.rs index d2c2adb7..4f73d2f5 100644 --- a/dubbo/src/cluster/mod.rs +++ b/dubbo/src/cluster/mod.rs @@ -112,25 +112,25 @@ impl Invoker> for FailoverCluster { #[derive(Debug, Default)] pub struct MockDirectory { - router_chain: RouterChain, + // router_chain: RouterChain, invokers: Vec, } impl MockDirectory { pub fn new(invokers: Vec) -> MockDirectory { Self { - router_chain: RouterChain::default(), + // router_chain: RouterChain::default(), invokers, } } } impl Directory for MockDirectory { - fn list(&self, invo: Arc) -> Vec { + fn list(&self, _invo: Arc) -> Vec { // tracing::info!("MockDirectory: {}", meta); - let u = Url::from_url("triple://127.0.0.1:8888/helloworld.Greeter").unwrap(); + let _u = Url::from_url("triple://127.0.0.1:8888/helloworld.Greeter").unwrap(); // vec![Box::new(TripleInvoker::new(u))] - self.router_chain.route(u, invo); + // self.router_chain.route(u, invo); self.invokers.clone() } From 695a880f65c75296f15feedafb64d6c4a99eff6a Mon Sep 17 00:00:00 2001 From: yangyang <962032265@qq.com> Date: Tue, 16 May 2023 21:22:42 +0800 Subject: [PATCH 16/48] refactor(triple): rm unused var in clientBuilder --- dubbo/src/triple/client/builder.rs | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/dubbo/src/triple/client/builder.rs b/dubbo/src/triple/client/builder.rs index 5ec8bda4..29957a6f 100644 --- a/dubbo/src/triple/client/builder.rs +++ b/dubbo/src/triple/client/builder.rs @@ -38,7 +38,6 @@ pub struct ClientBuilder { cluster_invoker: Option, pub direct: bool, host: String, - uri: Option, } impl ClientBuilder { @@ -49,7 +48,6 @@ impl ClientBuilder { directory: None, cluster_invoker: None, direct: false, - uri: None, host: "".to_string(), } } @@ -61,23 +59,10 @@ impl ClientBuilder { directory: Some(Box::new(StaticDirectory::new(&host))), cluster_invoker: None, direct: true, - uri: None, host: host.clone().to_string(), } } - pub fn from_uri(uri: &http::Uri) -> ClientBuilder { - Self { - timeout: None, - connector: "", - directory: Some(Box::new(StaticDirectory::from_uri(&uri))), - cluster_invoker: None, - direct: true, - uri: Some(uri.clone()), - host: "".to_string(), - } - } - pub fn with_timeout(self, timeout: u64) -> Self { Self { timeout: Some(timeout), From e9a9935e4421ceebc8833e0df56a46dac6609880 Mon Sep 17 00:00:00 2001 From: yangyang <962032265@qq.com> Date: Tue, 16 May 2023 22:03:39 +0800 Subject: [PATCH 17/48] refactor(dubbo): delete some codes --- dubbo-build/src/client.rs | 5 - dubbo/src/cluster/mod.rs | 2 +- dubbo/src/cluster/support/cluster_invoker.rs | 147 ------------------- dubbo/src/cluster/support/mod.rs | 20 --- dubbo/src/codegen.rs | 5 +- dubbo/src/registry/integration.rs | 7 - dubbo/src/triple/client/builder.rs | 9 +- dubbo/src/triple/client/triple.rs | 81 ++-------- registry/zookeeper/src/lib.rs | 11 +- 9 files changed, 20 insertions(+), 267 deletions(-) delete mode 100644 dubbo/src/cluster/support/cluster_invoker.rs delete mode 100644 dubbo/src/cluster/support/mod.rs diff --git a/dubbo-build/src/client.rs b/dubbo-build/src/client.rs index 1e1c9fbd..bfcfe45b 100644 --- a/dubbo-build/src/client.rs +++ b/dubbo-build/src/client.rs @@ -90,11 +90,6 @@ pub fn generate( } } - pub fn with_cluster(mut self, invoker: ClusterInvoker) -> Self { - self.inner = self.inner.with_cluster(invoker); - self - } - #methods } diff --git a/dubbo/src/cluster/mod.rs b/dubbo/src/cluster/mod.rs index 4f73d2f5..f60838e1 100644 --- a/dubbo/src/cluster/mod.rs +++ b/dubbo/src/cluster/mod.rs @@ -28,7 +28,7 @@ use crate::{ pub mod directory; pub mod loadbalance; -pub mod support; +// pub mod support; pub trait Directory: Debug { fn list(&self, invocation: Arc) -> Vec; diff --git a/dubbo/src/cluster/support/cluster_invoker.rs b/dubbo/src/cluster/support/cluster_invoker.rs deleted file mode 100644 index 0ccca488..00000000 --- a/dubbo/src/cluster/support/cluster_invoker.rs +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -use aws_smithy_http::body::SdkBody; -use std::{str::FromStr, sync::Arc}; - -use dubbo_base::Url; -use http::{uri::PathAndQuery, Request}; - -use crate::{ - cluster::{ - loadbalance::{types::BoxLoadBalance, LOAD_BALANCE_EXTENSIONS}, - support::DEFAULT_LOADBALANCE, - }, - codegen::{Directory, RegistryDirectory, TripleClient}, - invocation::RpcInvocation, -}; - -#[derive(Debug, Clone)] -pub struct ClusterInvoker { - directory: Arc, - destroyed: bool, -} - -pub trait ClusterInvokerSelector { - /// Select a invoker using loadbalance policy. - fn select( - &self, - invocation: Arc, - invokers: Arc>, - excluded: Arc>, - ) -> Option; - - fn do_select( - &self, - loadbalance_key: Option<&str>, - invocation: Arc, - invokers: Arc>, - ) -> Option; -} - -pub trait ClusterRequestBuilder { - fn build_req( - &self, - triple_client: &mut TripleClient, - path: http::uri::PathAndQuery, - invocation: Arc, - body: SdkBody, - ) -> http::Request; -} - -impl ClusterInvoker { - pub fn with_directory(registry_directory: RegistryDirectory) -> Self { - ClusterInvoker { - directory: Arc::new(registry_directory), - destroyed: false, - } - } - - pub fn directory(&self) -> Arc { - self.directory.clone() - } - - pub fn init_loadbalance(&self, loadbalance_key: &str) -> &BoxLoadBalance { - if LOAD_BALANCE_EXTENSIONS.contains_key(loadbalance_key) { - LOAD_BALANCE_EXTENSIONS.get(loadbalance_key).unwrap() - } else { - println!( - "loadbalance {} not found, use default loadbalance {}", - loadbalance_key, DEFAULT_LOADBALANCE - ); - LOAD_BALANCE_EXTENSIONS.get(DEFAULT_LOADBALANCE).unwrap() - } - } - - pub fn is_available(&self, invocation: Arc) -> bool { - !self.destroyed() && !self.directory.list(invocation).is_empty() - } - - pub fn destroyed(&self) -> bool { - self.destroyed - } -} - -impl ClusterInvokerSelector for ClusterInvoker { - fn select( - &self, - invocation: Arc, - invokers: Arc>, - _excluded: Arc>, - ) -> Option { - if invokers.is_empty() { - return None; - } - let instance_count = invokers.len(); - return if instance_count == 1 { - Some(invokers.as_ref().first()?.clone()) - } else { - let loadbalance = Some(DEFAULT_LOADBALANCE); - self.do_select(loadbalance, invocation, invokers) - }; - } - - /// picking instance invoker url from registry directory - fn do_select( - &self, - loadbalance_key: Option<&str>, - invocation: Arc, - invokers: Arc>, - ) -> Option { - let loadbalance = self.init_loadbalance(loadbalance_key.unwrap_or(DEFAULT_LOADBALANCE)); - loadbalance.select(invokers, None, invocation) - } -} - -impl ClusterRequestBuilder for ClusterInvoker { - fn build_req( - &self, - triple_client: &mut TripleClient, - path: PathAndQuery, - invocation: Arc, - body: SdkBody, - ) -> Request { - let invokers = self.directory.list(invocation.clone()); - let invoker_url = self - .select(invocation, Arc::new(invokers), Arc::new(Vec::new())) - .expect("no valid provider"); - let http_uri = - http::Uri::from_str(&format!("http://{}:{}/", invoker_url.ip, invoker_url.port)) - .unwrap(); - triple_client.map_request(http_uri, path, body) - } -} diff --git a/dubbo/src/cluster/support/mod.rs b/dubbo/src/cluster/support/mod.rs deleted file mode 100644 index ae42cc28..00000000 --- a/dubbo/src/cluster/support/mod.rs +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -pub mod cluster_invoker; - -pub const DEFAULT_LOADBALANCE: &str = "random"; diff --git a/dubbo/src/codegen.rs b/dubbo/src/codegen.rs index 98d784fc..5d0a2732 100644 --- a/dubbo/src/codegen.rs +++ b/dubbo/src/codegen.rs @@ -27,10 +27,7 @@ pub use hyper::Body as hyperBody; pub use tower_service::Service; pub use super::{ - cluster::{ - directory::{Directory, RegistryDirectory}, - support::cluster_invoker::ClusterInvoker, - }, + cluster::directory::{Directory, RegistryDirectory}, empty_body, invocation::{IntoStreamingRequest, Request, Response, RpcInvocation}, protocol::{triple::triple_invoker::TripleInvoker, Invoker}, diff --git a/dubbo/src/registry/integration.rs b/dubbo/src/registry/integration.rs index 15b82d01..2944f981 100644 --- a/dubbo/src/registry/integration.rs +++ b/dubbo/src/registry/integration.rs @@ -14,10 +14,3 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -use crate::{cluster::support::cluster_invoker::ClusterInvoker, registry::BoxRegistry}; -use std::sync::Arc; - -pub trait ClusterRegistryIntegration { - /// get cluster invoker struct - fn get_invoker(registry: BoxRegistry) -> Option>; -} diff --git a/dubbo/src/triple/client/builder.rs b/dubbo/src/triple/client/builder.rs index 29957a6f..ce86641a 100644 --- a/dubbo/src/triple/client/builder.rs +++ b/dubbo/src/triple/client/builder.rs @@ -17,7 +17,7 @@ use crate::{ cluster::{directory::StaticDirectory, Cluster, MockCluster, MockDirectory}, - codegen::{ClusterInvoker, Directory, RegistryDirectory, TripleInvoker}, + codegen::{Directory, RegistryDirectory, TripleInvoker}, triple::compression::CompressionEncoding, utils::boxed_clone::BoxCloneService, }; @@ -35,7 +35,6 @@ pub struct ClientBuilder { pub timeout: Option, pub connector: &'static str, directory: Option>, - cluster_invoker: Option, pub direct: bool, host: String, } @@ -46,7 +45,6 @@ impl ClientBuilder { timeout: None, connector: "", directory: None, - cluster_invoker: None, direct: false, host: "".to_string(), } @@ -57,7 +55,6 @@ impl ClientBuilder { timeout: None, connector: "", directory: Some(Box::new(StaticDirectory::new(&host))), - cluster_invoker: None, direct: true, host: host.clone().to_string(), } @@ -74,7 +71,6 @@ impl ClientBuilder { pub fn with_directory(self, directory: Box) -> Self { Self { directory: Some(directory), - cluster_invoker: None, ..self } } @@ -82,7 +78,6 @@ impl ClientBuilder { pub fn with_registry_directory(self, registry: RegistryDirectory) -> Self { Self { directory: None, - cluster_invoker: Some(ClusterInvoker::with_directory(registry)), ..self } } @@ -97,7 +92,6 @@ impl ClientBuilder { pub fn with_connector(self, connector: &'static str) -> Self { Self { connector: connector, - cluster_invoker: None, ..self } } @@ -110,7 +104,6 @@ impl ClientBuilder { let mut cli = TripleClient { send_compression_encoding: Some(CompressionEncoding::Gzip), directory: self.directory, - cluster_invoker: self.cluster_invoker, invoker: None, }; if self.direct { diff --git a/dubbo/src/triple/client/triple.rs b/dubbo/src/triple/client/triple.rs index eb7934dd..64c8ffbc 100644 --- a/dubbo/src/triple/client/triple.rs +++ b/dubbo/src/triple/client/triple.rs @@ -25,10 +25,9 @@ use rand::prelude::SliceRandom; use tower_service::Service; use super::{super::transport::connection::Connection, builder::ClientBuilder}; -use crate::codegen::{ClusterInvoker, Directory, RpcInvocation}; +use crate::codegen::{Directory, RpcInvocation}; use crate::{ - cluster::support::cluster_invoker::ClusterRequestBuilder, invocation::{IntoStreamingRequest, Metadata, Request, Response}, protocol::BoxInvoker, triple::{codec::Codec, compression::CompressionEncoding, decode::Decoding, encode::encode}, @@ -38,7 +37,6 @@ use crate::{ pub struct TripleClient { pub(crate) send_compression_encoding: Option, pub(crate) directory: Option>, - pub(crate) cluster_invoker: Option, pub invoker: Option, } @@ -53,13 +51,6 @@ impl TripleClient { builder.build() } - pub fn with_cluster(self, invoker: ClusterInvoker) -> Self { - TripleClient { - cluster_invoker: Some(invoker), - ..self - } - } - pub fn map_request( &self, uri: http::Uri, @@ -214,25 +205,11 @@ impl TripleClient { .into_stream(); let body = hyper::Body::wrap_stream(en); let sdk_body = SdkBody::from(body); - let arc_invocation = Arc::new(invocation); - let req; - let http_uri; - if self.cluster_invoker.is_some() { - let cluster_invoker = self.cluster_invoker.as_ref().unwrap().clone(); - req = cluster_invoker.build_req(self, path, arc_invocation.clone(), sdk_body); - http_uri = req.uri().clone(); - } else { - let url_list = self - .directory - .as_ref() - .expect("msg") - .list(arc_invocation.clone()); - let real_url = url_list.choose(&mut rand::thread_rng()).expect("msg"); - http_uri = - http::Uri::from_str(&format!("http://{}:{}/", real_url.ip, real_url.port)).unwrap(); - req = self.map_request(http_uri.clone(), path, sdk_body); - } - let mut conn = Connection::new().with_host(http_uri); + + let mut conn = self.invoker.clone().unwrap(); + let http_uri = http::Uri::from_str(&conn.get_url().to_url()).unwrap(); + let req = self.map_request(http_uri.clone(), path, sdk_body); + let response = conn .call(req) .await @@ -271,24 +248,11 @@ impl TripleClient { .into_stream(); let body = hyper::Body::wrap_stream(en); let sdk_body = SdkBody::from(body); - let arc_invocation = Arc::new(invocation); - let req; - let http_uri; - if self.cluster_invoker.is_some() { - let cluster_invoker = self.cluster_invoker.as_ref().unwrap().clone(); - req = cluster_invoker.build_req(self, path, arc_invocation.clone(), sdk_body); - http_uri = req.uri().clone(); - } else { - let url_list = self - .directory - .as_ref() - .expect("msg") - .list(arc_invocation.clone()); - let real_url = url_list.choose(&mut rand::thread_rng()).expect("msg"); - http_uri = - http::Uri::from_str(&format!("http://{}:{}/", real_url.ip, real_url.port)).unwrap(); - req = self.map_request(http_uri.clone(), path, sdk_body); - } + + let mut conn = self.invoker.clone().unwrap(); + let http_uri = http::Uri::from_str(&conn.get_url().to_url()).unwrap(); + let req = self.map_request(http_uri.clone(), path, sdk_body); + let mut conn = Connection::new().with_host(http_uri); let response = conn .call(req) @@ -344,26 +308,11 @@ impl TripleClient { .into_stream(); let body = hyper::Body::wrap_stream(en); let sdk_body = SdkBody::from(body); - let arc_invocation = Arc::new(invocation); - let req; - let http_uri; - if self.cluster_invoker.is_some() { - let cluster_invoker = self.cluster_invoker.as_ref().unwrap().clone(); - req = cluster_invoker.build_req(self, path, arc_invocation.clone(), sdk_body); - http_uri = req.uri().clone(); - } else { - let url_list = self - .directory - .as_ref() - .expect("msg") - .list(arc_invocation.clone()); - let real_url = url_list.choose(&mut rand::thread_rng()).expect("msg"); - http_uri = - http::Uri::from_str(&format!("http://{}:{}/", real_url.ip, real_url.port)).unwrap(); - req = self.map_request(http_uri.clone(), path, sdk_body); - } - let mut conn = Connection::new().with_host(http_uri); + let mut conn = self.invoker.clone().unwrap(); + let http_uri = http::Uri::from_str(&conn.get_url().to_url()).unwrap(); + let req = self.map_request(http_uri.clone(), path, sdk_body); + let response = conn .call(req) .await diff --git a/registry/zookeeper/src/lib.rs b/registry/zookeeper/src/lib.rs index 5debc0ab..2da3a53e 100644 --- a/registry/zookeeper/src/lib.rs +++ b/registry/zookeeper/src/lib.rs @@ -34,11 +34,10 @@ use serde::{Deserialize, Serialize}; use zookeeper::{Acl, CreateMode, WatchedEvent, WatchedEventType, Watcher, ZooKeeper}; use dubbo::{ - cluster::support::cluster_invoker::ClusterInvoker, codegen::BoxRegistry, registry::{ - integration::ClusterRegistryIntegration, memory_registry::MemoryRegistry, NotifyListener, - Registry, RegistryNotifyListener, ServiceEvent, + memory_registry::MemoryRegistry, NotifyListener, Registry, RegistryNotifyListener, + ServiceEvent, }, StdError, }; @@ -371,12 +370,6 @@ impl NotifyListener for ServiceInstancesChangedListener { } } -impl ClusterRegistryIntegration for ZookeeperRegistry { - fn get_invoker(registry: BoxRegistry) -> Option> { - todo!() - } -} - #[cfg(test)] mod tests { use std::sync::Arc; From 1c79aee5246db4b2ed6465936b1d94bba738025f Mon Sep 17 00:00:00 2001 From: yangyang <962032265@qq.com> Date: Thu, 18 May 2023 20:29:29 +0800 Subject: [PATCH 18/48] refactor(cluster): rm some duplicate codes --- dubbo/src/cluster/directory.rs | 58 ++++++++++------------------------ dubbo/src/cluster/mod.rs | 16 ++++++---- dubbo/src/codegen.rs | 2 +- 3 files changed, 26 insertions(+), 50 deletions(-) diff --git a/dubbo/src/cluster/directory.rs b/dubbo/src/cluster/directory.rs index e74fc6d4..e8ce5644 100644 --- a/dubbo/src/cluster/directory.rs +++ b/dubbo/src/cluster/directory.rs @@ -23,39 +23,21 @@ use std::{ }; use crate::{ + codegen::TripleInvoker, invocation::{Invocation, RpcInvocation}, + protocol::BoxInvoker, registry::{memory_registry::MemoryNotifyListener, BoxRegistry, RegistryWrapper}, }; use dubbo_base::Url; use dubbo_logger::tracing; +use crate::cluster::Directory; + /// Directory. /// /// [Directory Service](http://en.wikipedia.org/wiki/Directory_service) -pub trait Directory: Debug + DirectoryClone { - fn list(&self, invocation: Arc) -> Vec; -} -pub trait DirectoryClone { - fn clone_box(&self) -> Box; -} - -impl DirectoryClone for T -where - T: 'static + Directory + Clone, -{ - fn clone_box(&self) -> Box { - Box::new(self.clone()) - } -} - -impl Clone for Box { - fn clone(&self) -> Box { - self.clone_box() - } -} - -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct StaticDirectory { uri: http::Uri, } @@ -78,7 +60,7 @@ impl StaticDirectory { } impl Directory for StaticDirectory { - fn list(&self, invocation: Arc) -> Vec { + fn list(&self, invocation: Arc) -> Vec { let url = Url::from_url(&format!( "tri://{}:{}/{}", self.uri.host().unwrap(), @@ -86,19 +68,12 @@ impl Directory for StaticDirectory { invocation.get_target_service_unique_name(), )) .unwrap(); - vec![url] - } -} - -impl DirectoryClone for StaticDirectory { - fn clone_box(&self) -> Box { - Box::new(StaticDirectory { - uri: self.uri.clone(), - }) + let invoker = Box::new(TripleInvoker::new(url)); + vec![invoker] } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct RegistryDirectory { registry: RegistryWrapper, service_instances: Arc>>>, @@ -115,14 +90,8 @@ impl RegistryDirectory { } } -impl DirectoryClone for RegistryDirectory { - fn clone_box(&self) -> Box { - todo!() - } -} - impl Directory for RegistryDirectory { - fn list(&self, invocation: Arc) -> Vec { + fn list(&self, invocation: Arc) -> Vec { let service_name = invocation.get_target_service_unique_name(); let url = Url::from_url(&format!( @@ -149,6 +118,11 @@ impl Directory for RegistryDirectory { .expect("service_instances.read"); let binding = Vec::new(); let url_vec = map.get(&service_name).unwrap_or(&binding); - url_vec.to_vec() + // url_vec.to_vec() + let mut invokers: Vec = vec![]; + for item in url_vec.iter() { + invokers.push(Box::new(TripleInvoker::new(item.clone()))); + } + invokers } } diff --git a/dubbo/src/cluster/mod.rs b/dubbo/src/cluster/mod.rs index f60838e1..3e6c4f30 100644 --- a/dubbo/src/cluster/mod.rs +++ b/dubbo/src/cluster/mod.rs @@ -19,6 +19,7 @@ use std::{collections::HashMap, fmt::Debug, sync::Arc, task::Poll}; use aws_smithy_http::body::SdkBody; use dubbo_base::Url; +use dyn_clone::DynClone; use crate::{ empty_body, @@ -28,13 +29,14 @@ use crate::{ pub mod directory; pub mod loadbalance; -// pub mod support; -pub trait Directory: Debug { +pub trait Directory: Debug + DynClone { fn list(&self, invocation: Arc) -> Vec; - fn is_empty(&self) -> bool; + // fn is_empty(&self) -> bool; } +dyn_clone::clone_trait_object!(Directory); + type BoxDirectory = Box; pub trait Cluster { @@ -110,7 +112,7 @@ impl Invoker> for FailoverCluster { } } -#[derive(Debug, Default)] +#[derive(Debug, Default, Clone)] pub struct MockDirectory { // router_chain: RouterChain, invokers: Vec, @@ -134,9 +136,9 @@ impl Directory for MockDirectory { self.invokers.clone() } - fn is_empty(&self) -> bool { - false - } + // fn is_empty(&self) -> bool { + // false + // } } #[derive(Debug, Default)] diff --git a/dubbo/src/codegen.rs b/dubbo/src/codegen.rs index 5d0a2732..24b73fd3 100644 --- a/dubbo/src/codegen.rs +++ b/dubbo/src/codegen.rs @@ -27,7 +27,7 @@ pub use hyper::Body as hyperBody; pub use tower_service::Service; pub use super::{ - cluster::directory::{Directory, RegistryDirectory}, + cluster::directory::RegistryDirectory, empty_body, invocation::{IntoStreamingRequest, Request, Response, RpcInvocation}, protocol::{triple::triple_invoker::TripleInvoker, Invoker}, From 06cb96950b32d536c811b9191416a2b8d1811bf6 Mon Sep 17 00:00:00 2001 From: yangyang <962032265@qq.com> Date: Thu, 18 May 2023 20:44:28 +0800 Subject: [PATCH 19/48] refactor(registry): rm unused import --- registry/zookeeper/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/registry/zookeeper/src/lib.rs b/registry/zookeeper/src/lib.rs index 2da3a53e..e8c2c5ce 100644 --- a/registry/zookeeper/src/lib.rs +++ b/registry/zookeeper/src/lib.rs @@ -34,7 +34,6 @@ use serde::{Deserialize, Serialize}; use zookeeper::{Acl, CreateMode, WatchedEvent, WatchedEventType, Watcher, ZooKeeper}; use dubbo::{ - codegen::BoxRegistry, registry::{ memory_registry::MemoryRegistry, NotifyListener, Registry, RegistryNotifyListener, ServiceEvent, From 18a61403a9b79482be6a974f125b68388800a8ef Mon Sep 17 00:00:00 2001 From: yangyang <962032265@qq.com> Date: Thu, 18 May 2023 20:46:17 +0800 Subject: [PATCH 20/48] refactor(triple): use two build func for different usage --- dubbo/src/triple/client/builder.rs | 34 +++++++++------ dubbo/src/triple/client/triple.rs | 66 +++++++++++++++++++++++------- examples/echo/src/echo/client.rs | 4 +- 3 files changed, 76 insertions(+), 28 deletions(-) diff --git a/dubbo/src/triple/client/builder.rs b/dubbo/src/triple/client/builder.rs index ce86641a..06ecd627 100644 --- a/dubbo/src/triple/client/builder.rs +++ b/dubbo/src/triple/client/builder.rs @@ -15,9 +15,12 @@ * limitations under the License. */ +use std::sync::Arc; + use crate::{ - cluster::{directory::StaticDirectory, Cluster, MockCluster, MockDirectory}, - codegen::{Directory, RegistryDirectory, TripleInvoker}, + cluster::{directory::StaticDirectory, Cluster, Directory, MockCluster, MockDirectory}, + codegen::{RegistryDirectory, RpcInvocation, TripleInvoker}, + protocol::BoxInvoker, triple::compression::CompressionEncoding, utils::boxed_clone::BoxCloneService, }; @@ -77,7 +80,7 @@ impl ClientBuilder { pub fn with_registry_directory(self, registry: RegistryDirectory) -> Self { Self { - directory: None, + directory: Some(Box::new(registry)), ..self } } @@ -100,24 +103,31 @@ impl ClientBuilder { Self { direct, ..self } } - pub fn build(self) -> TripleClient { + pub(crate) fn direct_build(self) -> TripleClient { let mut cli = TripleClient { send_compression_encoding: Some(CompressionEncoding::Gzip), - directory: self.directory, + builder: Some(self.clone()), invoker: None, }; + cli.invoker = Some(Box::new(TripleInvoker::new( + Url::from_url(&self.host).unwrap(), + ))); + return cli; + } + + pub fn build(self, invocation: Arc) -> Option { if self.direct { - cli.invoker = Some(Box::new(TripleInvoker::new( + return Some(Box::new(TripleInvoker::new( Url::from_url(&self.host).unwrap(), ))); - return cli; } + let invokers = match self.directory { + Some(v) => v.list(invocation), + None => panic!("use direct connection"), + }; - let cluster = MockCluster::default().join(Box::new(MockDirectory::new(vec![Box::new( - TripleInvoker::new(Url::from_url("http://127.0.0.1:8888").unwrap()), - )]))); + let cluster = MockCluster::default().join(Box::new(MockDirectory::new(invokers))); - cli.invoker = Some(cluster); - cli + return Some(cluster); } } diff --git a/dubbo/src/triple/client/triple.rs b/dubbo/src/triple/client/triple.rs index 64c8ffbc..124cfcfd 100644 --- a/dubbo/src/triple/client/triple.rs +++ b/dubbo/src/triple/client/triple.rs @@ -15,17 +15,15 @@ * limitations under the License. */ -use std::{str::FromStr, sync::Arc}; +use std::str::FromStr; use futures_util::{future, stream, StreamExt, TryStreamExt}; use aws_smithy_http::body::SdkBody; use http::HeaderValue; -use rand::prelude::SliceRandom; -use tower_service::Service; -use super::{super::transport::connection::Connection, builder::ClientBuilder}; -use crate::codegen::{Directory, RpcInvocation}; +use super::builder::ClientBuilder; +use crate::codegen::RpcInvocation; use crate::{ invocation::{IntoStreamingRequest, Metadata, Request, Response}, @@ -36,7 +34,7 @@ use crate::{ #[derive(Debug, Clone, Default)] pub struct TripleClient { pub(crate) send_compression_encoding: Option, - pub(crate) directory: Option>, + pub(crate) builder: Option, pub invoker: Option, } @@ -44,11 +42,15 @@ impl TripleClient { pub fn connect(host: String) -> Self { let builder = ClientBuilder::from_static(&host).with_direct(true); - builder.build() + builder.direct_build() } pub fn new(builder: ClientBuilder) -> Self { - builder.build() + TripleClient { + send_compression_encoding: Some(CompressionEncoding::Gzip), + builder: Some(builder), + invoker: None, + } } pub fn map_request( @@ -128,7 +130,7 @@ impl TripleClient { req: Request, mut codec: C, path: http::uri::PathAndQuery, - _invocation: RpcInvocation, + invocation: RpcInvocation, ) -> Result, crate::status::Status> where C: Codec, @@ -146,8 +148,16 @@ impl TripleClient { let bytes = hyper::body::to_bytes(body).await.unwrap(); let sdk_body = SdkBody::from(bytes); - // let mut conn = Connection::new().with_host(http_uri); - let mut conn = self.invoker.clone().unwrap(); + let mut conn = match self.invoker.clone() { + Some(v) => v, + None => self + .builder + .clone() + .unwrap() + .build(invocation.into()) + .unwrap(), + }; + let http_uri = http::Uri::from_str(&conn.get_url().to_url()).unwrap(); let req = self.map_request(http_uri.clone(), path, sdk_body); @@ -206,7 +216,16 @@ impl TripleClient { let body = hyper::Body::wrap_stream(en); let sdk_body = SdkBody::from(body); - let mut conn = self.invoker.clone().unwrap(); + let mut conn = match self.invoker.clone() { + Some(v) => v, + None => self + .builder + .clone() + .unwrap() + .build(invocation.into()) + .unwrap(), + }; + let http_uri = http::Uri::from_str(&conn.get_url().to_url()).unwrap(); let req = self.map_request(http_uri.clone(), path, sdk_body); @@ -249,11 +268,20 @@ impl TripleClient { let body = hyper::Body::wrap_stream(en); let sdk_body = SdkBody::from(body); - let mut conn = self.invoker.clone().unwrap(); + let mut conn = match self.invoker.clone() { + Some(v) => v, + None => self + .builder + .clone() + .unwrap() + .build(invocation.into()) + .unwrap(), + }; + let http_uri = http::Uri::from_str(&conn.get_url().to_url()).unwrap(); let req = self.map_request(http_uri.clone(), path, sdk_body); - let mut conn = Connection::new().with_host(http_uri); + // let mut conn = Connection::new().with_host(http_uri); let response = conn .call(req) .await @@ -309,7 +337,15 @@ impl TripleClient { let body = hyper::Body::wrap_stream(en); let sdk_body = SdkBody::from(body); - let mut conn = self.invoker.clone().unwrap(); + let mut conn = match self.invoker.clone() { + Some(v) => v, + None => self + .builder + .clone() + .unwrap() + .build(invocation.into()) + .unwrap(), + }; let http_uri = http::Uri::from_str(&conn.get_url().to_url()).unwrap(); let req = self.map_request(http_uri.clone(), path, sdk_body); diff --git a/examples/echo/src/echo/client.rs b/examples/echo/src/echo/client.rs index db46958c..0a2f150b 100644 --- a/examples/echo/src/echo/client.rs +++ b/examples/echo/src/echo/client.rs @@ -34,7 +34,9 @@ async fn main() { // let builder = ClientBuilder::new() // .with_connector("unix") // .with_host("unix://127.0.0.1:8888"); - let builder = ClientBuilder::from_static(&"http://127.0.0.1:8888").with_timeout(1000000); + let builder = ClientBuilder::from_static(&"http://127.0.0.1:8888") + .with_timeout(1000000) + .with_direct(true); let mut cli = EchoClient::new(builder); // let mut unary_cli = cli.clone().with_filter(FakeFilter {}); // let mut cli = EchoClient::build(ClientBuilder::from_static("http://127.0.0.1:8888")); From 7dbe5f346ef6302f8e7f7f3c803388ed6df52fbb Mon Sep 17 00:00:00 2001 From: yangyang <962032265@qq.com> Date: Thu, 18 May 2023 23:51:24 +0800 Subject: [PATCH 21/48] style: cargo fmt --all --- dubbo/src/cluster/mod.rs | 1 - dubbo/src/triple/client/triple.rs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/dubbo/src/cluster/mod.rs b/dubbo/src/cluster/mod.rs index 7447ffe0..3e6c4f30 100644 --- a/dubbo/src/cluster/mod.rs +++ b/dubbo/src/cluster/mod.rs @@ -21,7 +21,6 @@ use aws_smithy_http::body::SdkBody; use dubbo_base::Url; use dyn_clone::DynClone; - use crate::{ empty_body, invocation::RpcInvocation, diff --git a/dubbo/src/triple/client/triple.rs b/dubbo/src/triple/client/triple.rs index e7d26084..124cfcfd 100644 --- a/dubbo/src/triple/client/triple.rs +++ b/dubbo/src/triple/client/triple.rs @@ -130,7 +130,7 @@ impl TripleClient { req: Request, mut codec: C, path: http::uri::PathAndQuery, - _invocation: RpcInvocation, + invocation: RpcInvocation, ) -> Result, crate::status::Status> where C: Codec, From 58c6f7d41d9a8f7b08111ca4854b6fc57f7d7aae Mon Sep 17 00:00:00 2001 From: yangyang <962032265@qq.com> Date: Fri, 19 May 2023 21:57:26 +0800 Subject: [PATCH 22/48] refactor(cluster): rm registryWrapper --- dubbo/src/cluster/directory.rs | 11 +++-------- dubbo/src/codegen.rs | 2 +- dubbo/src/registry/mod.rs | 17 ----------------- 3 files changed, 4 insertions(+), 26 deletions(-) diff --git a/dubbo/src/cluster/directory.rs b/dubbo/src/cluster/directory.rs index e8ce5644..afe9657b 100644 --- a/dubbo/src/cluster/directory.rs +++ b/dubbo/src/cluster/directory.rs @@ -26,7 +26,7 @@ use crate::{ codegen::TripleInvoker, invocation::{Invocation, RpcInvocation}, protocol::BoxInvoker, - registry::{memory_registry::MemoryNotifyListener, BoxRegistry, RegistryWrapper}, + registry::{memory_registry::MemoryNotifyListener, BoxRegistry}, }; use dubbo_base::Url; use dubbo_logger::tracing; @@ -75,16 +75,14 @@ impl Directory for StaticDirectory { #[derive(Debug, Clone)] pub struct RegistryDirectory { - registry: RegistryWrapper, + registry: Arc, service_instances: Arc>>>, } impl RegistryDirectory { pub fn new(registry: BoxRegistry) -> RegistryDirectory { RegistryDirectory { - registry: RegistryWrapper { - registry: Some(registry), - }, + registry: Arc::new(registry), service_instances: Arc::new(RwLock::new(HashMap::new())), } } @@ -101,9 +99,6 @@ impl Directory for RegistryDirectory { .unwrap(); self.registry - .registry - .as_ref() - .expect("msg") .subscribe( url, Arc::new(MemoryNotifyListener { diff --git a/dubbo/src/codegen.rs b/dubbo/src/codegen.rs index 24b73fd3..412feb91 100644 --- a/dubbo/src/codegen.rs +++ b/dubbo/src/codegen.rs @@ -31,7 +31,7 @@ pub use super::{ empty_body, invocation::{IntoStreamingRequest, Request, Response, RpcInvocation}, protocol::{triple::triple_invoker::TripleInvoker, Invoker}, - registry::{BoxRegistry, Registry, RegistryWrapper}, + registry::{BoxRegistry, Registry}, triple::{ client::TripleClient, codec::{prost::ProstCodec, Codec}, diff --git a/dubbo/src/registry/mod.rs b/dubbo/src/registry/mod.rs index 31106f0f..2a95452a 100644 --- a/dubbo/src/registry/mod.rs +++ b/dubbo/src/registry/mod.rs @@ -60,20 +60,3 @@ impl Debug for BoxRegistry { f.write_str("BoxRegistry") } } - -#[derive(Default)] -pub struct RegistryWrapper { - pub registry: Option>, -} - -impl Clone for RegistryWrapper { - fn clone(&self) -> Self { - Self { registry: None } - } -} - -impl Debug for RegistryWrapper { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("RegistryWrapper").finish() - } -} From 157b6c4f4de1943233df9c1de8f0e4568e7e10da Mon Sep 17 00:00:00 2001 From: yangyang <962032265@qq.com> Date: Fri, 19 May 2023 22:18:27 +0800 Subject: [PATCH 23/48] refactor(cluster): delete print --- dubbo/src/cluster/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/dubbo/src/cluster/mod.rs b/dubbo/src/cluster/mod.rs index 3e6c4f30..1f949b77 100644 --- a/dubbo/src/cluster/mod.rs +++ b/dubbo/src/cluster/mod.rs @@ -78,7 +78,6 @@ impl Invoker> for FailoverCluster { } fn call(&mut self, req: http::Request) -> Self::Future { - println!("req: {}", req.body().content_length().unwrap()); let clone_body = req.body().try_clone().unwrap(); let mut clone_req = http::Request::builder() .uri(req.uri().clone()) From aa554fee8e0e609854d1a5fd3868254607a6f204 Mon Sep 17 00:00:00 2001 From: yangyang <962032265@qq.com> Date: Sun, 21 May 2023 00:29:48 +0800 Subject: [PATCH 24/48] chore(dubbo): upgrade hyper version in cargo.toml --- dubbo/Cargo.toml | 4 ++-- examples/echo/Cargo.toml | 4 +--- examples/greeter/Cargo.toml | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/dubbo/Cargo.toml b/dubbo/Cargo.toml index 91b19d6f..d0ab2a4d 100644 --- a/dubbo/Cargo.toml +++ b/dubbo/Cargo.toml @@ -10,7 +10,7 @@ repository = "https://github.com/apache/dubbo-rust.git" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -hyper = { version = "0.14.19", features = ["full"] } +hyper = { version = "0.14.26", features = ["full"] } http = "0.2" tower-service.workspace = true http-body = "0.4.4" @@ -33,7 +33,7 @@ futures.workspace = true axum = "0.5.9" async-stream = "0.3" flate2 = "1.0" -aws-smithy-http = "0.54.1" +aws-smithy-http = "0.55.2" dyn-clone = "1.0.11" itertools.workspace = true urlencoding.workspace = true diff --git a/examples/echo/Cargo.toml b/examples/echo/Cargo.toml index 319c17b3..bc6638b8 100644 --- a/examples/echo/Cargo.toml +++ b/examples/echo/Cargo.toml @@ -30,9 +30,7 @@ async-trait = "0.1.56" tokio-stream = "0.1" dubbo-logger.workspace=true -hyper = { version = "0.14.19", features = ["full"]} - -dubbo = {path = "../../dubbo", version = "0.3.0" } +dubbo = {path = "../../dubbo"} dubbo-config = {path = "../../config", version = "0.3.0" } registry-zookeeper.workspace=true diff --git a/examples/greeter/Cargo.toml b/examples/greeter/Cargo.toml index 6ad3b1b5..d68cab7b 100644 --- a/examples/greeter/Cargo.toml +++ b/examples/greeter/Cargo.toml @@ -29,7 +29,7 @@ prost = "0.10.4" async-trait = "0.1.56" tokio-stream = "0.1" dubbo-logger = { path = "../../common/logger" } -dubbo = { path = "../../dubbo", version = "0.3.0" } +dubbo = { path = "../../dubbo"} dubbo-config = { path = "../../config", version = "0.3.0" } registry-zookeeper.workspace = true registry-nacos.workspace = true From 254fe5917f6ce460171411e1fb0f8b89745ce1ae Mon Sep 17 00:00:00 2001 From: yangyang <962032265@qq.com> Date: Mon, 22 May 2023 22:17:49 +0800 Subject: [PATCH 25/48] refactor(cluster): comment the logic of clone body --- dubbo/src/cluster/mod.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/dubbo/src/cluster/mod.rs b/dubbo/src/cluster/mod.rs index 1f949b77..d1f96f95 100644 --- a/dubbo/src/cluster/mod.rs +++ b/dubbo/src/cluster/mod.rs @@ -78,12 +78,12 @@ impl Invoker> for FailoverCluster { } fn call(&mut self, req: http::Request) -> Self::Future { - let clone_body = req.body().try_clone().unwrap(); - let mut clone_req = http::Request::builder() - .uri(req.uri().clone()) - .method(req.method().clone()); - *clone_req.headers_mut().unwrap() = req.headers().clone(); - let r = clone_req.body(clone_body).unwrap(); + // let clone_body = req.body().try_clone().unwrap(); + // let mut clone_req = http::Request::builder() + // .uri(req.uri().clone()) + // .method(req.method().clone()); + // *clone_req.headers_mut().unwrap() = req.headers().clone(); + // let r = clone_req.body(clone_body).unwrap(); let invokers = self.dir.list( RpcInvocation::default() .with_service_unique_name("hello".to_string()) @@ -91,7 +91,7 @@ impl Invoker> for FailoverCluster { ); for mut invoker in invokers { let fut = async move { - let res = invoker.call(r).await; + let res = invoker.call(req).await; return res; }; return Box::pin(fut); From b24e7307f403bafec790a2d042bce49f8b348692 Mon Sep 17 00:00:00 2001 From: yangyang <962032265@qq.com> Date: Sat, 29 Jul 2023 14:40:52 +0800 Subject: [PATCH 26/48] Rft(triple): remove Clone of Invoker --- dubbo/src/protocol/mod.rs | 26 ++++++----- dubbo/src/triple/client/builder.rs | 33 +++----------- dubbo/src/triple/client/triple.rs | 71 +++++++++++++----------------- 3 files changed, 53 insertions(+), 77 deletions(-) diff --git a/dubbo/src/protocol/mod.rs b/dubbo/src/protocol/mod.rs index 145bcc8e..f4de85b5 100644 --- a/dubbo/src/protocol/mod.rs +++ b/dubbo/src/protocol/mod.rs @@ -23,7 +23,6 @@ use std::{ use async_trait::async_trait; use aws_smithy_http::body::SdkBody; -use dyn_clone::DynClone; use tower_service::Service; use dubbo_base::Url; @@ -44,7 +43,7 @@ pub trait Exporter { fn unexport(&self); } -pub trait Invoker: Debug + DynClone { +pub trait Invoker: Debug { type Response; type Error; @@ -69,14 +68,21 @@ pub type BoxInvoker = Box< + Sync, >; -dyn_clone::clone_trait_object!( - Invoker< - http::Request, - Response = http::Response, - Error = crate::Error, - Future = crate::BoxFuture, crate::Error>, - > -); +impl Service> for BoxInvoker { + type Response = http::Response; + + type Error = crate::Error; + + type Future = crate::BoxFuture, crate::Error>; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + self.poll_ready(cx) + } + + fn call(&mut self, req: http::Request) -> Self::Future { + self.call(req) + } +} pub struct WrapperInvoker(T); diff --git a/dubbo/src/triple/client/builder.rs b/dubbo/src/triple/client/builder.rs index 06ecd627..d68067a8 100644 --- a/dubbo/src/triple/client/builder.rs +++ b/dubbo/src/triple/client/builder.rs @@ -21,15 +21,12 @@ use crate::{ cluster::{directory::StaticDirectory, Cluster, Directory, MockCluster, MockDirectory}, codegen::{RegistryDirectory, RpcInvocation, TripleInvoker}, protocol::BoxInvoker, - triple::compression::CompressionEncoding, utils::boxed_clone::BoxCloneService, }; use aws_smithy_http::body::SdkBody; use dubbo_base::Url; -use super::TripleClient; - pub type ClientBoxService = BoxCloneService, http::Response, crate::Error>; @@ -37,7 +34,7 @@ pub type ClientBoxService = pub struct ClientBuilder { pub timeout: Option, pub connector: &'static str, - directory: Option>, + directory: Option>>, pub direct: bool, host: String, } @@ -57,7 +54,7 @@ impl ClientBuilder { Self { timeout: None, connector: "", - directory: Some(Box::new(StaticDirectory::new(&host))), + directory: Some(Arc::new(Box::new(StaticDirectory::new(&host)))), direct: true, host: host.clone().to_string(), } @@ -73,21 +70,21 @@ impl ClientBuilder { /// host: http://0.0.0.0:8888 pub fn with_directory(self, directory: Box) -> Self { Self { - directory: Some(directory), + directory: Some(Arc::new(directory)), ..self } } pub fn with_registry_directory(self, registry: RegistryDirectory) -> Self { Self { - directory: Some(Box::new(registry)), + directory: Some(Arc::new(Box::new(registry))), ..self } } pub fn with_host(self, host: &'static str) -> Self { Self { - directory: Some(Box::new(StaticDirectory::new(&host))), + directory: Some(Arc::new(Box::new(StaticDirectory::new(&host)))), ..self } } @@ -103,30 +100,14 @@ impl ClientBuilder { Self { direct, ..self } } - pub(crate) fn direct_build(self) -> TripleClient { - let mut cli = TripleClient { - send_compression_encoding: Some(CompressionEncoding::Gzip), - builder: Some(self.clone()), - invoker: None, - }; - cli.invoker = Some(Box::new(TripleInvoker::new( - Url::from_url(&self.host).unwrap(), - ))); - return cli; - } - - pub fn build(self, invocation: Arc) -> Option { + pub fn build(self, _invocation: Arc) -> Option { if self.direct { return Some(Box::new(TripleInvoker::new( Url::from_url(&self.host).unwrap(), ))); } - let invokers = match self.directory { - Some(v) => v.list(invocation), - None => panic!("use direct connection"), - }; - let cluster = MockCluster::default().join(Box::new(MockDirectory::new(invokers))); + let cluster = MockCluster::default().join(Box::new(MockDirectory::new())); return Some(cluster); } diff --git a/dubbo/src/triple/client/triple.rs b/dubbo/src/triple/client/triple.rs index 124cfcfd..b81661cd 100644 --- a/dubbo/src/triple/client/triple.rs +++ b/dubbo/src/triple/client/triple.rs @@ -27,29 +27,29 @@ use crate::codegen::RpcInvocation; use crate::{ invocation::{IntoStreamingRequest, Metadata, Request, Response}, - protocol::BoxInvoker, triple::{codec::Codec, compression::CompressionEncoding, decode::Decoding, encode::encode}, }; -#[derive(Debug, Clone, Default)] +#[derive(Debug, Default, Clone)] pub struct TripleClient { pub(crate) send_compression_encoding: Option, pub(crate) builder: Option, - pub invoker: Option, } impl TripleClient { pub fn connect(host: String) -> Self { let builder = ClientBuilder::from_static(&host).with_direct(true); - builder.direct_build() + TripleClient { + send_compression_encoding: Some(CompressionEncoding::Gzip), + builder: Some(builder), + } } pub fn new(builder: ClientBuilder) -> Self { TripleClient { send_compression_encoding: Some(CompressionEncoding::Gzip), builder: Some(builder), - invoker: None, } } @@ -148,15 +148,12 @@ impl TripleClient { let bytes = hyper::body::to_bytes(body).await.unwrap(); let sdk_body = SdkBody::from(bytes); - let mut conn = match self.invoker.clone() { - Some(v) => v, - None => self - .builder - .clone() - .unwrap() - .build(invocation.into()) - .unwrap(), - }; + let mut conn = self + .builder + .clone() + .unwrap() + .build(invocation.into()) + .unwrap(); let http_uri = http::Uri::from_str(&conn.get_url().to_url()).unwrap(); let req = self.map_request(http_uri.clone(), path, sdk_body); @@ -216,15 +213,12 @@ impl TripleClient { let body = hyper::Body::wrap_stream(en); let sdk_body = SdkBody::from(body); - let mut conn = match self.invoker.clone() { - Some(v) => v, - None => self - .builder - .clone() - .unwrap() - .build(invocation.into()) - .unwrap(), - }; + let mut conn = self + .builder + .clone() + .unwrap() + .build(invocation.into()) + .unwrap(); let http_uri = http::Uri::from_str(&conn.get_url().to_url()).unwrap(); let req = self.map_request(http_uri.clone(), path, sdk_body); @@ -268,15 +262,12 @@ impl TripleClient { let body = hyper::Body::wrap_stream(en); let sdk_body = SdkBody::from(body); - let mut conn = match self.invoker.clone() { - Some(v) => v, - None => self - .builder - .clone() - .unwrap() - .build(invocation.into()) - .unwrap(), - }; + let mut conn = self + .builder + .clone() + .unwrap() + .build(invocation.into()) + .unwrap(); let http_uri = http::Uri::from_str(&conn.get_url().to_url()).unwrap(); let req = self.map_request(http_uri.clone(), path, sdk_body); @@ -337,15 +328,13 @@ impl TripleClient { let body = hyper::Body::wrap_stream(en); let sdk_body = SdkBody::from(body); - let mut conn = match self.invoker.clone() { - Some(v) => v, - None => self - .builder - .clone() - .unwrap() - .build(invocation.into()) - .unwrap(), - }; + let mut conn = self + .builder + .clone() + .unwrap() + .build(invocation.into()) + .unwrap(); + let http_uri = http::Uri::from_str(&conn.get_url().to_url()).unwrap(); let req = self.map_request(http_uri.clone(), path, sdk_body); From 6999bbb272ae49dce136b96bf1911c2145a9aa41 Mon Sep 17 00:00:00 2001 From: yangyang <962032265@qq.com> Date: Sat, 29 Jul 2023 14:46:06 +0800 Subject: [PATCH 27/48] Rft(cluster): use ready_cache to manage Invokers, add ready_cache in FailoverCluster --- dubbo/Cargo.toml | 2 +- dubbo/src/cluster/mod.rs | 134 ++++++++++++++++++--------------------- 2 files changed, 63 insertions(+), 73 deletions(-) diff --git a/dubbo/Cargo.toml b/dubbo/Cargo.toml index d0ab2a4d..51ccc193 100644 --- a/dubbo/Cargo.toml +++ b/dubbo/Cargo.toml @@ -14,7 +14,7 @@ hyper = { version = "0.14.26", features = ["full"] } http = "0.2" tower-service.workspace = true http-body = "0.4.4" -tower = { workspace = true, features = ["timeout"] } +tower = { workspace = true, features = ["timeout", "ready-cache"] } futures-util = "0.3.23" futures-core ="0.3.23" argh = "0.1" diff --git a/dubbo/src/cluster/mod.rs b/dubbo/src/cluster/mod.rs index d1f96f95..a60ab90f 100644 --- a/dubbo/src/cluster/mod.rs +++ b/dubbo/src/cluster/mod.rs @@ -15,28 +15,25 @@ * limitations under the License. */ -use std::{collections::HashMap, fmt::Debug, sync::Arc, task::Poll}; +use std::{fmt::Debug, sync::Arc, task::Poll}; use aws_smithy_http::body::SdkBody; use dubbo_base::Url; -use dyn_clone::DynClone; +use futures_util::TryFutureExt; +use tower::ready_cache::ReadyCache; use crate::{ - empty_body, invocation::RpcInvocation, - protocol::{BoxInvoker, Invoker}, + protocol::{triple::triple_invoker::TripleInvoker, BoxInvoker, Invoker}, }; pub mod directory; pub mod loadbalance; -pub trait Directory: Debug + DynClone { +pub trait Directory: Debug { fn list(&self, invocation: Arc) -> Vec; - // fn is_empty(&self) -> bool; } -dyn_clone::clone_trait_object!(Directory); - type BoxDirectory = Box; pub trait Cluster { @@ -51,14 +48,20 @@ impl Cluster for MockCluster { Box::new(FailoverCluster::new(dir)) } } -#[derive(Clone, Debug)] + +// 在Cluster上进行缓存Service +#[derive(Debug)] pub struct FailoverCluster { dir: Arc, + caches: ReadyCache>, } impl FailoverCluster { pub fn new(dir: BoxDirectory) -> FailoverCluster { - Self { dir: Arc::new(dir) } + Self { + dir: Arc::new(dir), + caches: ReadyCache::default(), + } } } @@ -89,21 +92,29 @@ impl Invoker> for FailoverCluster { .with_service_unique_name("hello".to_string()) .into(), ); - for mut invoker in invokers { - let fut = async move { - let res = invoker.call(req).await; - return res; - }; - return Box::pin(fut); + let mut i: usize = 0; + for invoker in invokers { + self.caches.push(i, invoker); + i += 1; } - Box::pin(async move { - Ok(http::Response::builder() - .status(200) - .header("grpc-status", "12") - .header("content-type", "application/grpc") - .body(empty_body()) - .unwrap()) - }) + + Box::pin(self.caches.call_ready_index(0, req).map_err(Into::into)) + + // let fut = async move { + // let (_, invoker) = self.caches.get_ready_index_mut(i).unwrap(); + // let res = invoker.call(req).await; + // return res; + // }; + // return Box::pin(fut); + + // Box::pin(async move { + // Ok(http::Response::builder() + // .status(200) + // .header("grpc-status", "12") + // .header("content-type", "application/grpc") + // .body(empty_body()) + // .unwrap()) + // }) } fn get_url(&self) -> dubbo_base::Url { @@ -111,17 +122,15 @@ impl Invoker> for FailoverCluster { } } -#[derive(Debug, Default, Clone)] +#[derive(Debug, Default)] pub struct MockDirectory { // router_chain: RouterChain, - invokers: Vec, } impl MockDirectory { - pub fn new(invokers: Vec) -> MockDirectory { + pub fn new() -> MockDirectory { Self { // router_chain: RouterChain::default(), - invokers, } } } @@ -129,51 +138,32 @@ impl MockDirectory { impl Directory for MockDirectory { fn list(&self, _invo: Arc) -> Vec { // tracing::info!("MockDirectory: {}", meta); - let _u = Url::from_url("triple://127.0.0.1:8888/helloworld.Greeter").unwrap(); - // vec![Box::new(TripleInvoker::new(u))] + let u = Url::from_url("triple://127.0.0.1:8888/helloworld.Greeter").unwrap(); + vec![Box::new(TripleInvoker::new(u))] // self.router_chain.route(u, invo); - self.invokers.clone() } - - // fn is_empty(&self) -> bool { - // false - // } -} - -#[derive(Debug, Default)] -pub struct RouterChain { - router: HashMap, - invokers: Vec, } -impl RouterChain { - pub fn route(&self, url: Url, invo: Arc) -> Vec { - let r = self.router.get("mock").unwrap(); - r.route(self.invokers.clone(), url, invo) - } -} - -pub trait Router: Debug { - fn route( - &self, - invokers: Vec, - url: Url, - invo: Arc, - ) -> Vec; -} - -pub type BoxRouter = Box; - -#[derive(Debug, Default)] -pub struct MockRouter {} - -impl Router for MockRouter { - fn route( - &self, - invokers: Vec, - _url: Url, - _invo: Arc, - ) -> Vec { - invokers - } -} +// #[derive(Debug, Default)] +// pub struct RouterChain { +// router: HashMap, +// invokers: Arc>, +// } + +// impl RouterChain { +// pub fn route(&mut self, url: Url, invo: Arc) -> Arc> { +// let r = self.router.get("mock").unwrap(); +// r.route(self.invokers.clone(), url, invo) +// } +// } + +// pub trait Router: Debug { +// fn route( +// &self, +// invokers: Arc>, +// url: Url, +// invo: Arc, +// ) -> Arc>; +// } + +// pub type BoxRouter = Box; From 0a5ee224d014ee94de61e1faf5a298b1667d6379 Mon Sep 17 00:00:00 2001 From: yangyang <962032265@qq.com> Date: Sat, 29 Jul 2023 19:00:37 +0800 Subject: [PATCH 28/48] Rft(protocol): use interface Inheritance to redesign Invoker --- dubbo/src/cluster/mod.rs | 21 +++------------ dubbo/src/protocol/mod.rs | 29 +-------------------- dubbo/src/protocol/triple/triple_invoker.rs | 12 +++++---- 3 files changed, 12 insertions(+), 50 deletions(-) diff --git a/dubbo/src/cluster/mod.rs b/dubbo/src/cluster/mod.rs index a60ab90f..afc3cc91 100644 --- a/dubbo/src/cluster/mod.rs +++ b/dubbo/src/cluster/mod.rs @@ -21,6 +21,7 @@ use aws_smithy_http::body::SdkBody; use dubbo_base::Url; use futures_util::TryFutureExt; use tower::ready_cache::ReadyCache; +use tower_service::Service; use crate::{ invocation::RpcInvocation, @@ -65,7 +66,7 @@ impl FailoverCluster { } } -impl Invoker> for FailoverCluster { +impl Service> for FailoverCluster { type Response = http::Response; type Error = crate::Error; @@ -99,24 +100,10 @@ impl Invoker> for FailoverCluster { } Box::pin(self.caches.call_ready_index(0, req).map_err(Into::into)) - - // let fut = async move { - // let (_, invoker) = self.caches.get_ready_index_mut(i).unwrap(); - // let res = invoker.call(req).await; - // return res; - // }; - // return Box::pin(fut); - - // Box::pin(async move { - // Ok(http::Response::builder() - // .status(200) - // .header("grpc-status", "12") - // .header("content-type", "application/grpc") - // .body(empty_body()) - // .unwrap()) - // }) } +} +impl Invoker> for FailoverCluster { fn get_url(&self) -> dubbo_base::Url { Url::from_url("triple://127.0.0.1:8888/helloworld.Greeter").unwrap() } diff --git a/dubbo/src/protocol/mod.rs b/dubbo/src/protocol/mod.rs index f4de85b5..4dceb45e 100644 --- a/dubbo/src/protocol/mod.rs +++ b/dubbo/src/protocol/mod.rs @@ -17,7 +17,6 @@ use std::{ fmt::Debug, - future::Future, task::{Context, Poll}, }; @@ -43,18 +42,8 @@ pub trait Exporter { fn unexport(&self); } -pub trait Invoker: Debug { - type Response; - - type Error; - - type Future: Future>; - +pub trait Invoker: Debug + Service { fn get_url(&self) -> Url; - - fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll>; - - fn call(&mut self, req: ReqBody) -> Self::Future; } pub type BoxExporter = Box; @@ -68,22 +57,6 @@ pub type BoxInvoker = Box< + Sync, >; -impl Service> for BoxInvoker { - type Response = http::Response; - - type Error = crate::Error; - - type Future = crate::BoxFuture, crate::Error>; - - fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { - self.poll_ready(cx) - } - - fn call(&mut self, req: http::Request) -> Self::Future { - self.call(req) - } -} - pub struct WrapperInvoker(T); impl Service> for WrapperInvoker diff --git a/dubbo/src/protocol/triple/triple_invoker.rs b/dubbo/src/protocol/triple/triple_invoker.rs index fb661f9e..db18f5f1 100644 --- a/dubbo/src/protocol/triple/triple_invoker.rs +++ b/dubbo/src/protocol/triple/triple_invoker.rs @@ -51,17 +51,13 @@ impl Debug for TripleInvoker { } } -impl Invoker> for TripleInvoker { +impl Service> for TripleInvoker { type Response = http::Response; type Error = crate::Error; type Future = crate::BoxFuture; - fn get_url(&self) -> Url { - self.url.clone() - } - fn call(&mut self, req: http::Request) -> Self::Future { self.conn.call(req) } @@ -73,3 +69,9 @@ impl Invoker> for TripleInvoker { self.conn.poll_ready(cx) } } + +impl Invoker> for TripleInvoker { + fn get_url(&self) -> Url { + self.url.clone() + } +} From b2e829cc397679a28a729ca7de7b59c391f00641 Mon Sep 17 00:00:00 2001 From: Yang Yang <962032265@qq.com> Date: Tue, 1 Aug 2023 10:40:30 +0800 Subject: [PATCH 29/48] Feat(cluster): Cluster Policy Impl (#146) * refactor(cluster): comment the logic of clone body * Rft(triple): remove Clone of Invoker * Rft(cluster): use ready_cache to manage Invokers, add ready_cache in FailoverCluster * Rft(protocol): use interface Inheritance to redesign Invoker --------- Co-authored-by: G-XD <38717659+G-XD@users.noreply.github.com> Co-authored-by: GXD --- config/src/protocol.rs | 20 ++- dubbo-build/src/client.rs | 5 - dubbo/Cargo.toml | 7 +- dubbo/src/cluster/directory.rs | 69 +++------ dubbo/src/cluster/mod.rs | 133 +++++++++++----- dubbo/src/cluster/support/cluster_invoker.rs | 147 ------------------ dubbo/src/cluster/support/mod.rs | 20 --- dubbo/src/codegen.rs | 7 +- dubbo/src/protocol/mod.rs | 13 +- dubbo/src/protocol/triple/triple_invoker.rs | 38 +++-- dubbo/src/registry/integration.rs | 7 - dubbo/src/registry/mod.rs | 17 --- dubbo/src/triple/client/builder.rs | 64 ++++---- dubbo/src/triple/client/triple.rs | 151 +++++++------------ examples/echo/Cargo.toml | 4 +- examples/echo/src/echo/client.rs | 4 +- examples/greeter/Cargo.toml | 2 +- registry/zookeeper/src/lib.rs | 12 +- 18 files changed, 258 insertions(+), 462 deletions(-) delete mode 100644 dubbo/src/cluster/support/cluster_invoker.rs delete mode 100644 dubbo/src/cluster/support/mod.rs diff --git a/config/src/protocol.rs b/config/src/protocol.rs index 86ff0531..e2340c17 100644 --- a/config/src/protocol.rs +++ b/config/src/protocol.rs @@ -77,10 +77,26 @@ impl ProtocolRetrieve for ProtocolConfig { } else { let result = self.get_protocol(protocol_key); if let Some(..) = result { - panic!("default triple base dose not defined.") - } else { result.unwrap() + } else { + panic!("default triple base dose not defined.") } } } } + +#[cfg(test)] +mod tests { + + use super::{ProtocolConfig, ProtocolRetrieve}; + + #[test] + #[should_panic(expected = "default triple base dose not defined")] + pub fn test_get_invalid_protocol() { + let config = ProtocolConfig::default(); + + let _ = config.get_protocol_or_default(""); + + () + } +} diff --git a/dubbo-build/src/client.rs b/dubbo-build/src/client.rs index 1e1c9fbd..bfcfe45b 100644 --- a/dubbo-build/src/client.rs +++ b/dubbo-build/src/client.rs @@ -90,11 +90,6 @@ pub fn generate( } } - pub fn with_cluster(mut self, invoker: ClusterInvoker) -> Self { - self.inner = self.inner.with_cluster(invoker); - self - } - #methods } diff --git a/dubbo/Cargo.toml b/dubbo/Cargo.toml index 94a2bd86..51ccc193 100644 --- a/dubbo/Cargo.toml +++ b/dubbo/Cargo.toml @@ -10,11 +10,11 @@ repository = "https://github.com/apache/dubbo-rust.git" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -hyper = { version = "0.14.19", features = ["full"] } +hyper = { version = "0.14.26", features = ["full"] } http = "0.2" tower-service.workspace = true http-body = "0.4.4" -tower = { workspace = true, features = ["timeout"] } +tower = { workspace = true, features = ["timeout", "ready-cache"] } futures-util = "0.3.23" futures-core ="0.3.23" argh = "0.1" @@ -33,7 +33,8 @@ futures.workspace = true axum = "0.5.9" async-stream = "0.3" flate2 = "1.0" -aws-smithy-http = "0.54.1" +aws-smithy-http = "0.55.2" +dyn-clone = "1.0.11" itertools.workspace = true urlencoding.workspace = true lazy_static.workspace = true diff --git a/dubbo/src/cluster/directory.rs b/dubbo/src/cluster/directory.rs index e74fc6d4..afe9657b 100644 --- a/dubbo/src/cluster/directory.rs +++ b/dubbo/src/cluster/directory.rs @@ -23,39 +23,21 @@ use std::{ }; use crate::{ + codegen::TripleInvoker, invocation::{Invocation, RpcInvocation}, - registry::{memory_registry::MemoryNotifyListener, BoxRegistry, RegistryWrapper}, + protocol::BoxInvoker, + registry::{memory_registry::MemoryNotifyListener, BoxRegistry}, }; use dubbo_base::Url; use dubbo_logger::tracing; +use crate::cluster::Directory; + /// Directory. /// /// [Directory Service](http://en.wikipedia.org/wiki/Directory_service) -pub trait Directory: Debug + DirectoryClone { - fn list(&self, invocation: Arc) -> Vec; -} -pub trait DirectoryClone { - fn clone_box(&self) -> Box; -} - -impl DirectoryClone for T -where - T: 'static + Directory + Clone, -{ - fn clone_box(&self) -> Box { - Box::new(self.clone()) - } -} - -impl Clone for Box { - fn clone(&self) -> Box { - self.clone_box() - } -} - -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct StaticDirectory { uri: http::Uri, } @@ -78,7 +60,7 @@ impl StaticDirectory { } impl Directory for StaticDirectory { - fn list(&self, invocation: Arc) -> Vec { + fn list(&self, invocation: Arc) -> Vec { let url = Url::from_url(&format!( "tri://{}:{}/{}", self.uri.host().unwrap(), @@ -86,43 +68,28 @@ impl Directory for StaticDirectory { invocation.get_target_service_unique_name(), )) .unwrap(); - vec![url] - } -} - -impl DirectoryClone for StaticDirectory { - fn clone_box(&self) -> Box { - Box::new(StaticDirectory { - uri: self.uri.clone(), - }) + let invoker = Box::new(TripleInvoker::new(url)); + vec![invoker] } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct RegistryDirectory { - registry: RegistryWrapper, + registry: Arc, service_instances: Arc>>>, } impl RegistryDirectory { pub fn new(registry: BoxRegistry) -> RegistryDirectory { RegistryDirectory { - registry: RegistryWrapper { - registry: Some(registry), - }, + registry: Arc::new(registry), service_instances: Arc::new(RwLock::new(HashMap::new())), } } } -impl DirectoryClone for RegistryDirectory { - fn clone_box(&self) -> Box { - todo!() - } -} - impl Directory for RegistryDirectory { - fn list(&self, invocation: Arc) -> Vec { + fn list(&self, invocation: Arc) -> Vec { let service_name = invocation.get_target_service_unique_name(); let url = Url::from_url(&format!( @@ -132,9 +99,6 @@ impl Directory for RegistryDirectory { .unwrap(); self.registry - .registry - .as_ref() - .expect("msg") .subscribe( url, Arc::new(MemoryNotifyListener { @@ -149,6 +113,11 @@ impl Directory for RegistryDirectory { .expect("service_instances.read"); let binding = Vec::new(); let url_vec = map.get(&service_name).unwrap_or(&binding); - url_vec.to_vec() + // url_vec.to_vec() + let mut invokers: Vec = vec![]; + for item in url_vec.iter() { + invokers.push(Box::new(TripleInvoker::new(item.clone()))); + } + invokers } } diff --git a/dubbo/src/cluster/mod.rs b/dubbo/src/cluster/mod.rs index b6d8a7c2..afc3cc91 100644 --- a/dubbo/src/cluster/mod.rs +++ b/dubbo/src/cluster/mod.rs @@ -15,31 +15,54 @@ * limitations under the License. */ -use std::{sync::Arc, task::Poll}; +use std::{fmt::Debug, sync::Arc, task::Poll}; use aws_smithy_http::body::SdkBody; +use dubbo_base::Url; +use futures_util::TryFutureExt; +use tower::ready_cache::ReadyCache; use tower_service::Service; -use crate::{empty_body, protocol::BoxInvoker}; +use crate::{ + invocation::RpcInvocation, + protocol::{triple::triple_invoker::TripleInvoker, BoxInvoker, Invoker}, +}; pub mod directory; pub mod loadbalance; -pub mod support; -pub trait Directory { - fn list(&self, meta: String) -> Vec; - fn is_empty(&self) -> bool; +pub trait Directory: Debug { + fn list(&self, invocation: Arc) -> Vec; } -type BoxDirectory = Box; +type BoxDirectory = Box; +pub trait Cluster { + fn join(&self, dir: BoxDirectory) -> BoxInvoker; +} + +#[derive(Debug, Default)] +pub struct MockCluster {} + +impl Cluster for MockCluster { + fn join(&self, dir: BoxDirectory) -> BoxInvoker { + Box::new(FailoverCluster::new(dir)) + } +} + +// 在Cluster上进行缓存Service +#[derive(Debug)] pub struct FailoverCluster { dir: Arc, + caches: ReadyCache>, } impl FailoverCluster { pub fn new(dir: BoxDirectory) -> FailoverCluster { - Self { dir: Arc::new(dir) } + Self { + dir: Arc::new(dir), + caches: ReadyCache::default(), + } } } @@ -59,43 +82,75 @@ impl Service> for FailoverCluster { } fn call(&mut self, req: http::Request) -> Self::Future { - println!("req: {}", req.body().content_length().unwrap()); - let clone_body = req.body().try_clone().unwrap(); - let mut clone_req = http::Request::builder() - .uri(req.uri().clone()) - .method(req.method().clone()); - *clone_req.headers_mut().unwrap() = req.headers().clone(); - let r = clone_req.body(clone_body).unwrap(); - let invokers = self.dir.list("service_name".to_string()); - for mut invoker in invokers { - let fut = async move { - let res = invoker.call(r).await; - return res; - }; - return Box::pin(fut); + // let clone_body = req.body().try_clone().unwrap(); + // let mut clone_req = http::Request::builder() + // .uri(req.uri().clone()) + // .method(req.method().clone()); + // *clone_req.headers_mut().unwrap() = req.headers().clone(); + // let r = clone_req.body(clone_body).unwrap(); + let invokers = self.dir.list( + RpcInvocation::default() + .with_service_unique_name("hello".to_string()) + .into(), + ); + let mut i: usize = 0; + for invoker in invokers { + self.caches.push(i, invoker); + i += 1; } - Box::pin(async move { - Ok(http::Response::builder() - .status(200) - .header("grpc-status", "12") - .header("content-type", "application/grpc") - .body(empty_body()) - .unwrap()) - }) + + Box::pin(self.caches.call_ready_index(0, req).map_err(Into::into)) } } -pub struct MockDirectory {} +impl Invoker> for FailoverCluster { + fn get_url(&self) -> dubbo_base::Url { + Url::from_url("triple://127.0.0.1:8888/helloworld.Greeter").unwrap() + } +} -impl Directory for MockDirectory { - fn list(&self, _meta: String) -> Vec { - // tracing::info!("MockDirectory: {}", meta); - // let u = Url::from_url("triple://127.0.0.1:8888/helloworld.Greeter").unwrap(); - // vec![Box::new(TripleInvoker::new(u))] - todo!() +#[derive(Debug, Default)] +pub struct MockDirectory { + // router_chain: RouterChain, +} + +impl MockDirectory { + pub fn new() -> MockDirectory { + Self { + // router_chain: RouterChain::default(), + } } +} - fn is_empty(&self) -> bool { - false +impl Directory for MockDirectory { + fn list(&self, _invo: Arc) -> Vec { + // tracing::info!("MockDirectory: {}", meta); + let u = Url::from_url("triple://127.0.0.1:8888/helloworld.Greeter").unwrap(); + vec![Box::new(TripleInvoker::new(u))] + // self.router_chain.route(u, invo); } } + +// #[derive(Debug, Default)] +// pub struct RouterChain { +// router: HashMap, +// invokers: Arc>, +// } + +// impl RouterChain { +// pub fn route(&mut self, url: Url, invo: Arc) -> Arc> { +// let r = self.router.get("mock").unwrap(); +// r.route(self.invokers.clone(), url, invo) +// } +// } + +// pub trait Router: Debug { +// fn route( +// &self, +// invokers: Arc>, +// url: Url, +// invo: Arc, +// ) -> Arc>; +// } + +// pub type BoxRouter = Box; diff --git a/dubbo/src/cluster/support/cluster_invoker.rs b/dubbo/src/cluster/support/cluster_invoker.rs deleted file mode 100644 index 0ccca488..00000000 --- a/dubbo/src/cluster/support/cluster_invoker.rs +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -use aws_smithy_http::body::SdkBody; -use std::{str::FromStr, sync::Arc}; - -use dubbo_base::Url; -use http::{uri::PathAndQuery, Request}; - -use crate::{ - cluster::{ - loadbalance::{types::BoxLoadBalance, LOAD_BALANCE_EXTENSIONS}, - support::DEFAULT_LOADBALANCE, - }, - codegen::{Directory, RegistryDirectory, TripleClient}, - invocation::RpcInvocation, -}; - -#[derive(Debug, Clone)] -pub struct ClusterInvoker { - directory: Arc, - destroyed: bool, -} - -pub trait ClusterInvokerSelector { - /// Select a invoker using loadbalance policy. - fn select( - &self, - invocation: Arc, - invokers: Arc>, - excluded: Arc>, - ) -> Option; - - fn do_select( - &self, - loadbalance_key: Option<&str>, - invocation: Arc, - invokers: Arc>, - ) -> Option; -} - -pub trait ClusterRequestBuilder { - fn build_req( - &self, - triple_client: &mut TripleClient, - path: http::uri::PathAndQuery, - invocation: Arc, - body: SdkBody, - ) -> http::Request; -} - -impl ClusterInvoker { - pub fn with_directory(registry_directory: RegistryDirectory) -> Self { - ClusterInvoker { - directory: Arc::new(registry_directory), - destroyed: false, - } - } - - pub fn directory(&self) -> Arc { - self.directory.clone() - } - - pub fn init_loadbalance(&self, loadbalance_key: &str) -> &BoxLoadBalance { - if LOAD_BALANCE_EXTENSIONS.contains_key(loadbalance_key) { - LOAD_BALANCE_EXTENSIONS.get(loadbalance_key).unwrap() - } else { - println!( - "loadbalance {} not found, use default loadbalance {}", - loadbalance_key, DEFAULT_LOADBALANCE - ); - LOAD_BALANCE_EXTENSIONS.get(DEFAULT_LOADBALANCE).unwrap() - } - } - - pub fn is_available(&self, invocation: Arc) -> bool { - !self.destroyed() && !self.directory.list(invocation).is_empty() - } - - pub fn destroyed(&self) -> bool { - self.destroyed - } -} - -impl ClusterInvokerSelector for ClusterInvoker { - fn select( - &self, - invocation: Arc, - invokers: Arc>, - _excluded: Arc>, - ) -> Option { - if invokers.is_empty() { - return None; - } - let instance_count = invokers.len(); - return if instance_count == 1 { - Some(invokers.as_ref().first()?.clone()) - } else { - let loadbalance = Some(DEFAULT_LOADBALANCE); - self.do_select(loadbalance, invocation, invokers) - }; - } - - /// picking instance invoker url from registry directory - fn do_select( - &self, - loadbalance_key: Option<&str>, - invocation: Arc, - invokers: Arc>, - ) -> Option { - let loadbalance = self.init_loadbalance(loadbalance_key.unwrap_or(DEFAULT_LOADBALANCE)); - loadbalance.select(invokers, None, invocation) - } -} - -impl ClusterRequestBuilder for ClusterInvoker { - fn build_req( - &self, - triple_client: &mut TripleClient, - path: PathAndQuery, - invocation: Arc, - body: SdkBody, - ) -> Request { - let invokers = self.directory.list(invocation.clone()); - let invoker_url = self - .select(invocation, Arc::new(invokers), Arc::new(Vec::new())) - .expect("no valid provider"); - let http_uri = - http::Uri::from_str(&format!("http://{}:{}/", invoker_url.ip, invoker_url.port)) - .unwrap(); - triple_client.map_request(http_uri, path, body) - } -} diff --git a/dubbo/src/cluster/support/mod.rs b/dubbo/src/cluster/support/mod.rs deleted file mode 100644 index ae42cc28..00000000 --- a/dubbo/src/cluster/support/mod.rs +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -pub mod cluster_invoker; - -pub const DEFAULT_LOADBALANCE: &str = "random"; diff --git a/dubbo/src/codegen.rs b/dubbo/src/codegen.rs index 98d784fc..412feb91 100644 --- a/dubbo/src/codegen.rs +++ b/dubbo/src/codegen.rs @@ -27,14 +27,11 @@ pub use hyper::Body as hyperBody; pub use tower_service::Service; pub use super::{ - cluster::{ - directory::{Directory, RegistryDirectory}, - support::cluster_invoker::ClusterInvoker, - }, + cluster::directory::RegistryDirectory, empty_body, invocation::{IntoStreamingRequest, Request, Response, RpcInvocation}, protocol::{triple::triple_invoker::TripleInvoker, Invoker}, - registry::{BoxRegistry, Registry, RegistryWrapper}, + registry::{BoxRegistry, Registry}, triple::{ client::TripleClient, codec::{prost::ProstCodec, Codec}, diff --git a/dubbo/src/protocol/mod.rs b/dubbo/src/protocol/mod.rs index 58dca5fd..4dceb45e 100644 --- a/dubbo/src/protocol/mod.rs +++ b/dubbo/src/protocol/mod.rs @@ -17,7 +17,6 @@ use std::{ fmt::Debug, - future::Future, task::{Context, Poll}, }; @@ -43,18 +42,8 @@ pub trait Exporter { fn unexport(&self); } -pub trait Invoker: Debug { - type Response; - - type Error; - - type Future: Future>; - +pub trait Invoker: Debug + Service { fn get_url(&self) -> Url; - - fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll>; - - fn call(&mut self, req: ReqBody) -> Self::Future; } pub type BoxExporter = Box; diff --git a/dubbo/src/protocol/triple/triple_invoker.rs b/dubbo/src/protocol/triple/triple_invoker.rs index 6139cc91..db18f5f1 100644 --- a/dubbo/src/protocol/triple/triple_invoker.rs +++ b/dubbo/src/protocol/triple/triple_invoker.rs @@ -17,24 +17,32 @@ use aws_smithy_http::body::SdkBody; use dubbo_base::Url; -use std::fmt::{Debug, Formatter}; +use std::{ + fmt::{Debug, Formatter}, + str::FromStr, +}; use tower_service::Service; -use crate::{protocol::Invoker, triple::client::builder::ClientBoxService}; +use crate::{ + protocol::Invoker, + triple::{client::builder::ClientBoxService, transport::connection::Connection}, + utils::boxed_clone::BoxCloneService, +}; +#[derive(Clone)] pub struct TripleInvoker { url: Url, conn: ClientBoxService, } impl TripleInvoker { - // pub fn new(url: Url) -> TripleInvoker { - // let uri = http::Uri::from_str(&url.to_url()).unwrap(); - // Self { - // url, - // conn: ClientBuilder::from_uri(&uri).build()connect(), - // } - // } + pub fn new(url: Url) -> TripleInvoker { + let uri = http::Uri::from_str(&url.to_url()).unwrap(); + Self { + url, + conn: BoxCloneService::new(Connection::new().with_host(uri)), + } + } } impl Debug for TripleInvoker { @@ -43,17 +51,13 @@ impl Debug for TripleInvoker { } } -impl Invoker> for TripleInvoker { +impl Service> for TripleInvoker { type Response = http::Response; type Error = crate::Error; type Future = crate::BoxFuture; - fn get_url(&self) -> Url { - self.url.clone() - } - fn call(&mut self, req: http::Request) -> Self::Future { self.conn.call(req) } @@ -65,3 +69,9 @@ impl Invoker> for TripleInvoker { self.conn.poll_ready(cx) } } + +impl Invoker> for TripleInvoker { + fn get_url(&self) -> Url { + self.url.clone() + } +} diff --git a/dubbo/src/registry/integration.rs b/dubbo/src/registry/integration.rs index 15b82d01..2944f981 100644 --- a/dubbo/src/registry/integration.rs +++ b/dubbo/src/registry/integration.rs @@ -14,10 +14,3 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -use crate::{cluster::support::cluster_invoker::ClusterInvoker, registry::BoxRegistry}; -use std::sync::Arc; - -pub trait ClusterRegistryIntegration { - /// get cluster invoker struct - fn get_invoker(registry: BoxRegistry) -> Option>; -} diff --git a/dubbo/src/registry/mod.rs b/dubbo/src/registry/mod.rs index 31106f0f..2a95452a 100644 --- a/dubbo/src/registry/mod.rs +++ b/dubbo/src/registry/mod.rs @@ -60,20 +60,3 @@ impl Debug for BoxRegistry { f.write_str("BoxRegistry") } } - -#[derive(Default)] -pub struct RegistryWrapper { - pub registry: Option>, -} - -impl Clone for RegistryWrapper { - fn clone(&self) -> Self { - Self { registry: None } - } -} - -impl Debug for RegistryWrapper { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("RegistryWrapper").finish() - } -} diff --git a/dubbo/src/triple/client/builder.rs b/dubbo/src/triple/client/builder.rs index cf667ccd..d68067a8 100644 --- a/dubbo/src/triple/client/builder.rs +++ b/dubbo/src/triple/client/builder.rs @@ -15,26 +15,28 @@ * limitations under the License. */ +use std::sync::Arc; + use crate::{ - cluster::directory::StaticDirectory, - codegen::{ClusterInvoker, Directory, RegistryDirectory}, - triple::compression::CompressionEncoding, - utils::boxed::BoxService, + cluster::{directory::StaticDirectory, Cluster, Directory, MockCluster, MockDirectory}, + codegen::{RegistryDirectory, RpcInvocation, TripleInvoker}, + protocol::BoxInvoker, + utils::boxed_clone::BoxCloneService, }; use aws_smithy_http::body::SdkBody; - -use super::TripleClient; +use dubbo_base::Url; pub type ClientBoxService = - BoxService, http::Response, crate::Error>; + BoxCloneService, http::Response, crate::Error>; #[derive(Clone, Debug, Default)] pub struct ClientBuilder { pub timeout: Option, pub connector: &'static str, - directory: Option>, - cluster_invoker: Option, + directory: Option>>, + pub direct: bool, + host: String, } impl ClientBuilder { @@ -43,7 +45,8 @@ impl ClientBuilder { timeout: None, connector: "", directory: None, - cluster_invoker: None, + direct: false, + host: "".to_string(), } } @@ -51,17 +54,9 @@ impl ClientBuilder { Self { timeout: None, connector: "", - directory: Some(Box::new(StaticDirectory::new(&host))), - cluster_invoker: None, - } - } - - pub fn from_uri(uri: &http::Uri) -> ClientBuilder { - Self { - timeout: None, - connector: "", - directory: Some(Box::new(StaticDirectory::from_uri(&uri))), - cluster_invoker: None, + directory: Some(Arc::new(Box::new(StaticDirectory::new(&host)))), + direct: true, + host: host.clone().to_string(), } } @@ -75,23 +70,21 @@ impl ClientBuilder { /// host: http://0.0.0.0:8888 pub fn with_directory(self, directory: Box) -> Self { Self { - directory: Some(directory), - cluster_invoker: None, + directory: Some(Arc::new(directory)), ..self } } pub fn with_registry_directory(self, registry: RegistryDirectory) -> Self { Self { - directory: None, - cluster_invoker: Some(ClusterInvoker::with_directory(registry)), + directory: Some(Arc::new(Box::new(registry))), ..self } } pub fn with_host(self, host: &'static str) -> Self { Self { - directory: Some(Box::new(StaticDirectory::new(&host))), + directory: Some(Arc::new(Box::new(StaticDirectory::new(&host)))), ..self } } @@ -99,16 +92,23 @@ impl ClientBuilder { pub fn with_connector(self, connector: &'static str) -> Self { Self { connector: connector, - cluster_invoker: None, ..self } } - pub fn build(self) -> TripleClient { - TripleClient { - send_compression_encoding: Some(CompressionEncoding::Gzip), - directory: self.directory, - cluster_invoker: self.cluster_invoker, + pub fn with_direct(self, direct: bool) -> Self { + Self { direct, ..self } + } + + pub fn build(self, _invocation: Arc) -> Option { + if self.direct { + return Some(Box::new(TripleInvoker::new( + Url::from_url(&self.host).unwrap(), + ))); } + + let cluster = MockCluster::default().join(Box::new(MockDirectory::new())); + + return Some(cluster); } } diff --git a/dubbo/src/triple/client/triple.rs b/dubbo/src/triple/client/triple.rs index 56edb96b..b81661cd 100644 --- a/dubbo/src/triple/client/triple.rs +++ b/dubbo/src/triple/client/triple.rs @@ -15,46 +15,41 @@ * limitations under the License. */ -use std::{str::FromStr, sync::Arc}; +use std::str::FromStr; use futures_util::{future, stream, StreamExt, TryStreamExt}; use aws_smithy_http::body::SdkBody; use http::HeaderValue; -use rand::prelude::SliceRandom; -use tower_service::Service; -use super::{super::transport::connection::Connection, builder::ClientBuilder}; -use crate::codegen::{ClusterInvoker, Directory, RpcInvocation}; +use super::builder::ClientBuilder; +use crate::codegen::RpcInvocation; use crate::{ - cluster::support::cluster_invoker::ClusterRequestBuilder, invocation::{IntoStreamingRequest, Metadata, Request, Response}, triple::{codec::Codec, compression::CompressionEncoding, decode::Decoding, encode::encode}, }; -#[derive(Debug, Clone, Default)] +#[derive(Debug, Default, Clone)] pub struct TripleClient { pub(crate) send_compression_encoding: Option, - pub(crate) directory: Option>, - pub(crate) cluster_invoker: Option, + pub(crate) builder: Option, } impl TripleClient { pub fn connect(host: String) -> Self { - let builder = ClientBuilder::from_static(&host); + let builder = ClientBuilder::from_static(&host).with_direct(true); - builder.build() + TripleClient { + send_compression_encoding: Some(CompressionEncoding::Gzip), + builder: Some(builder), + } } pub fn new(builder: ClientBuilder) -> Self { - builder.build() - } - - pub fn with_cluster(self, invoker: ClusterInvoker) -> Self { TripleClient { - cluster_invoker: Some(invoker), - ..self + send_compression_encoding: Some(CompressionEncoding::Gzip), + builder: Some(builder), } } @@ -150,27 +145,19 @@ impl TripleClient { ) .into_stream(); let body = hyper::Body::wrap_stream(body_stream); - let sdk_body = SdkBody::from(body); - let arc_invocation = Arc::new(invocation); - let req; - let http_uri; - if self.cluster_invoker.is_some() { - let cluster_invoker = self.cluster_invoker.as_ref().unwrap().clone(); - req = cluster_invoker.build_req(self, path, arc_invocation.clone(), sdk_body); - http_uri = req.uri().clone(); - } else { - let url_list = self - .directory - .as_ref() - .expect("msg") - .list(arc_invocation.clone()); - let real_url = url_list.choose(&mut rand::thread_rng()).expect("msg"); - http_uri = - http::Uri::from_str(&format!("http://{}:{}/", real_url.ip, real_url.port)).unwrap(); - req = self.map_request(http_uri.clone(), path, sdk_body); - } + let bytes = hyper::body::to_bytes(body).await.unwrap(); + let sdk_body = SdkBody::from(bytes); + + let mut conn = self + .builder + .clone() + .unwrap() + .build(invocation.into()) + .unwrap(); + + let http_uri = http::Uri::from_str(&conn.get_url().to_url()).unwrap(); + let req = self.map_request(http_uri.clone(), path, sdk_body); - let mut conn = Connection::new().with_host(http_uri); let response = conn .call(req) .await @@ -225,25 +212,17 @@ impl TripleClient { .into_stream(); let body = hyper::Body::wrap_stream(en); let sdk_body = SdkBody::from(body); - let arc_invocation = Arc::new(invocation); - let req; - let http_uri; - if self.cluster_invoker.is_some() { - let cluster_invoker = self.cluster_invoker.as_ref().unwrap().clone(); - req = cluster_invoker.build_req(self, path, arc_invocation.clone(), sdk_body); - http_uri = req.uri().clone(); - } else { - let url_list = self - .directory - .as_ref() - .expect("msg") - .list(arc_invocation.clone()); - let real_url = url_list.choose(&mut rand::thread_rng()).expect("msg"); - http_uri = - http::Uri::from_str(&format!("http://{}:{}/", real_url.ip, real_url.port)).unwrap(); - req = self.map_request(http_uri.clone(), path, sdk_body); - } - let mut conn = Connection::new().with_host(http_uri); + + let mut conn = self + .builder + .clone() + .unwrap() + .build(invocation.into()) + .unwrap(); + + let http_uri = http::Uri::from_str(&conn.get_url().to_url()).unwrap(); + let req = self.map_request(http_uri.clone(), path, sdk_body); + let response = conn .call(req) .await @@ -282,25 +261,18 @@ impl TripleClient { .into_stream(); let body = hyper::Body::wrap_stream(en); let sdk_body = SdkBody::from(body); - let arc_invocation = Arc::new(invocation); - let req; - let http_uri; - if self.cluster_invoker.is_some() { - let cluster_invoker = self.cluster_invoker.as_ref().unwrap().clone(); - req = cluster_invoker.build_req(self, path, arc_invocation.clone(), sdk_body); - http_uri = req.uri().clone(); - } else { - let url_list = self - .directory - .as_ref() - .expect("msg") - .list(arc_invocation.clone()); - let real_url = url_list.choose(&mut rand::thread_rng()).expect("msg"); - http_uri = - http::Uri::from_str(&format!("http://{}:{}/", real_url.ip, real_url.port)).unwrap(); - req = self.map_request(http_uri.clone(), path, sdk_body); - } - let mut conn = Connection::new().with_host(http_uri); + + let mut conn = self + .builder + .clone() + .unwrap() + .build(invocation.into()) + .unwrap(); + + let http_uri = http::Uri::from_str(&conn.get_url().to_url()).unwrap(); + let req = self.map_request(http_uri.clone(), path, sdk_body); + + // let mut conn = Connection::new().with_host(http_uri); let response = conn .call(req) .await @@ -355,26 +327,17 @@ impl TripleClient { .into_stream(); let body = hyper::Body::wrap_stream(en); let sdk_body = SdkBody::from(body); - let arc_invocation = Arc::new(invocation); - let req; - let http_uri; - if self.cluster_invoker.is_some() { - let cluster_invoker = self.cluster_invoker.as_ref().unwrap().clone(); - req = cluster_invoker.build_req(self, path, arc_invocation.clone(), sdk_body); - http_uri = req.uri().clone(); - } else { - let url_list = self - .directory - .as_ref() - .expect("msg") - .list(arc_invocation.clone()); - let real_url = url_list.choose(&mut rand::thread_rng()).expect("msg"); - http_uri = - http::Uri::from_str(&format!("http://{}:{}/", real_url.ip, real_url.port)).unwrap(); - req = self.map_request(http_uri.clone(), path, sdk_body); - } - let mut conn = Connection::new().with_host(http_uri); + let mut conn = self + .builder + .clone() + .unwrap() + .build(invocation.into()) + .unwrap(); + + let http_uri = http::Uri::from_str(&conn.get_url().to_url()).unwrap(); + let req = self.map_request(http_uri.clone(), path, sdk_body); + let response = conn .call(req) .await diff --git a/examples/echo/Cargo.toml b/examples/echo/Cargo.toml index 319c17b3..bc6638b8 100644 --- a/examples/echo/Cargo.toml +++ b/examples/echo/Cargo.toml @@ -30,9 +30,7 @@ async-trait = "0.1.56" tokio-stream = "0.1" dubbo-logger.workspace=true -hyper = { version = "0.14.19", features = ["full"]} - -dubbo = {path = "../../dubbo", version = "0.3.0" } +dubbo = {path = "../../dubbo"} dubbo-config = {path = "../../config", version = "0.3.0" } registry-zookeeper.workspace=true diff --git a/examples/echo/src/echo/client.rs b/examples/echo/src/echo/client.rs index db46958c..0a2f150b 100644 --- a/examples/echo/src/echo/client.rs +++ b/examples/echo/src/echo/client.rs @@ -34,7 +34,9 @@ async fn main() { // let builder = ClientBuilder::new() // .with_connector("unix") // .with_host("unix://127.0.0.1:8888"); - let builder = ClientBuilder::from_static(&"http://127.0.0.1:8888").with_timeout(1000000); + let builder = ClientBuilder::from_static(&"http://127.0.0.1:8888") + .with_timeout(1000000) + .with_direct(true); let mut cli = EchoClient::new(builder); // let mut unary_cli = cli.clone().with_filter(FakeFilter {}); // let mut cli = EchoClient::build(ClientBuilder::from_static("http://127.0.0.1:8888")); diff --git a/examples/greeter/Cargo.toml b/examples/greeter/Cargo.toml index 6ad3b1b5..d68cab7b 100644 --- a/examples/greeter/Cargo.toml +++ b/examples/greeter/Cargo.toml @@ -29,7 +29,7 @@ prost = "0.10.4" async-trait = "0.1.56" tokio-stream = "0.1" dubbo-logger = { path = "../../common/logger" } -dubbo = { path = "../../dubbo", version = "0.3.0" } +dubbo = { path = "../../dubbo"} dubbo-config = { path = "../../config", version = "0.3.0" } registry-zookeeper.workspace = true registry-nacos.workspace = true diff --git a/registry/zookeeper/src/lib.rs b/registry/zookeeper/src/lib.rs index 5debc0ab..e8c2c5ce 100644 --- a/registry/zookeeper/src/lib.rs +++ b/registry/zookeeper/src/lib.rs @@ -34,11 +34,9 @@ use serde::{Deserialize, Serialize}; use zookeeper::{Acl, CreateMode, WatchedEvent, WatchedEventType, Watcher, ZooKeeper}; use dubbo::{ - cluster::support::cluster_invoker::ClusterInvoker, - codegen::BoxRegistry, registry::{ - integration::ClusterRegistryIntegration, memory_registry::MemoryRegistry, NotifyListener, - Registry, RegistryNotifyListener, ServiceEvent, + memory_registry::MemoryRegistry, NotifyListener, Registry, RegistryNotifyListener, + ServiceEvent, }, StdError, }; @@ -371,12 +369,6 @@ impl NotifyListener for ServiceInstancesChangedListener { } } -impl ClusterRegistryIntegration for ZookeeperRegistry { - fn get_invoker(registry: BoxRegistry) -> Option> { - todo!() - } -} - #[cfg(test)] mod tests { use std::sync::Arc; From df0e0e5845a4d40440f1aa255654fac848156ac4 Mon Sep 17 00:00:00 2001 From: Yang Yang <962032265@qq.com> Date: Thu, 17 Aug 2023 00:15:05 +0800 Subject: [PATCH 30/48] chore(github): rm workflow_dispatch in workflow (#149) --- .github/workflows/github-actions.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/github-actions.yml b/.github/workflows/github-actions.yml index b02b3bc9..19d8c4d0 100644 --- a/.github/workflows/github-actions.yml +++ b/.github/workflows/github-actions.yml @@ -8,7 +8,6 @@ on: pull_request: branches: ["*"] - workflow_dispatch: jobs: check: From c07b96d4ca8300699657b97d18ba788b81417d5c Mon Sep 17 00:00:00 2001 From: Urara <95117705+AdachiAndShimamura@users.noreply.github.com> Date: Fri, 1 Sep 2023 16:17:36 +0800 Subject: [PATCH 31/48] feat: Add Router Module(#144) (#153) add condition router, add tag router, use nacos as router config center --- application.yaml | 35 ++- config/src/config.rs | 6 +- config/src/lib.rs | 1 + config/src/router.rs | 67 ++++++ dubbo/Cargo.toml | 5 + dubbo/src/cluster/mod.rs | 28 +-- .../router/condition/condition_router.rs | 60 +++++ dubbo/src/cluster/router/condition/matcher.rs | 94 ++++++++ dubbo/src/cluster/router/condition/mod.rs | 3 + .../cluster/router/condition/single_router.rs | 225 ++++++++++++++++++ .../router/manager/condition_manager.rs | 75 ++++++ dubbo/src/cluster/router/manager/mod.rs | 3 + .../cluster/router/manager/router_manager.rs | 161 +++++++++++++ .../src/cluster/router/manager/tag_manager.rs | 27 +++ dubbo/src/cluster/router/mod.rs | 25 ++ .../cluster/router/nacos_config_center/mod.rs | 1 + .../nacos_config_center/nacos_client.rs | 132 ++++++++++ dubbo/src/cluster/router/router_chain.rs | 74 ++++++ dubbo/src/cluster/router/tag/mod.rs | 1 + dubbo/src/cluster/router/tag/tag_router.rs | 70 ++++++ dubbo/src/cluster/router/utils.rs | 16 ++ dubbo/src/triple/client/builder.rs | 3 +- 22 files changed, 1084 insertions(+), 28 deletions(-) create mode 100644 config/src/router.rs create mode 100644 dubbo/src/cluster/router/condition/condition_router.rs create mode 100644 dubbo/src/cluster/router/condition/matcher.rs create mode 100644 dubbo/src/cluster/router/condition/mod.rs create mode 100644 dubbo/src/cluster/router/condition/single_router.rs create mode 100644 dubbo/src/cluster/router/manager/condition_manager.rs create mode 100644 dubbo/src/cluster/router/manager/mod.rs create mode 100644 dubbo/src/cluster/router/manager/router_manager.rs create mode 100644 dubbo/src/cluster/router/manager/tag_manager.rs create mode 100644 dubbo/src/cluster/router/mod.rs create mode 100644 dubbo/src/cluster/router/nacos_config_center/mod.rs create mode 100644 dubbo/src/cluster/router/nacos_config_center/nacos_client.rs create mode 100644 dubbo/src/cluster/router/router_chain.rs create mode 100644 dubbo/src/cluster/router/tag/mod.rs create mode 100644 dubbo/src/cluster/router/tag/tag_router.rs create mode 100644 dubbo/src/cluster/router/utils.rs diff --git a/application.yaml b/application.yaml index d357db14..902b0efd 100644 --- a/application.yaml +++ b/application.yaml @@ -21,4 +21,37 @@ dubbo: references: GreeterClientImpl: url: tri://localhost:20000 - protocol: tri \ No newline at end of file + protocol: tri + routers: + consumer: + - service: "org.apache.dubbo.sample.tri.Greeter" + url: triple://localhost:20000 + protocol: triple + nacos: + addr: "127.0.0.1:8848" + namespace: "" + app: "" + conditions: + - scope: "service" + force: false + runtime: true + enabled: true + key: "org.apache.dubbo.sample.tri.Greeter" + conditions: + - method=greet => port=8889 + - scope: "service" + force: true + runtime: true + enabled: true + key: "user.UserService" + conditions: + - method=get_s => port=2003 + tags: + force: true + enabled: true + key: shop-detail + tags: + - name: gray + matches: + - key: env + value: gray \ No newline at end of file diff --git a/config/src/config.rs b/config/src/config.rs index 646873d1..cf4e4415 100644 --- a/config/src/config.rs +++ b/config/src/config.rs @@ -17,7 +17,7 @@ use std::{collections::HashMap, env, path::PathBuf}; -use crate::{protocol::Protocol, registry::RegistryConfig}; +use crate::{protocol::Protocol, registry::RegistryConfig, router::RouterConfig}; use dubbo_logger::tracing; use dubbo_utils::yaml_util::yaml_file_parser; use once_cell::sync::OnceCell; @@ -44,6 +44,9 @@ pub struct RootConfig { #[serde(default)] pub registries: HashMap, + #[serde(default)] + pub routers: RouterConfig, + #[serde(default)] pub data: HashMap, } @@ -63,6 +66,7 @@ impl RootConfig { protocols: HashMap::new(), registries: HashMap::new(), provider: ProviderConfig::new(), + routers: RouterConfig::default(), data: HashMap::new(), } } diff --git a/config/src/lib.rs b/config/src/lib.rs index 0748c667..6fa38801 100644 --- a/config/src/lib.rs +++ b/config/src/lib.rs @@ -21,4 +21,5 @@ pub mod config; pub mod protocol; pub mod provider; pub mod registry; +pub mod router; pub mod service; diff --git a/config/src/router.rs b/config/src/router.rs new file mode 100644 index 00000000..98aa14a6 --- /dev/null +++ b/config/src/router.rs @@ -0,0 +1,67 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Default)] +pub struct ConditionRouterConfig { + pub scope: String, + pub force: bool, + pub runtime: bool, + pub enabled: bool, + pub key: String, + pub conditions: Vec, +} + +#[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq)] +pub struct TagRouterConfig { + pub force: bool, + pub enabled: bool, + pub key: String, + pub tags: Vec, +} + +#[derive(Serialize, Deserialize, Clone, PartialEq, Default, Debug)] +pub struct ConsumerConfig { + pub service: String, + pub url: String, + pub protocol: String, +} + +#[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq)] +pub struct Tag { + pub name: String, + pub matches: Vec, +} + +#[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq)] +pub struct TagMatchRule { + pub key: String, + pub value: String, +} + +impl ConditionRouterConfig { + pub fn new(config: &String) -> Self { + serde_yaml::from_str(config).expect("parse error") + } +} + +#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)] +pub struct EnableAuth { + pub auth_username: String, + pub auth_password: String, +} + +#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)] +pub struct NacosConfig { + pub addr: String, + pub namespace: String, + pub app: String, + pub enable_auth: Option, + pub enable_auth_plugin_http: Option, +} + +#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Default)] +pub struct RouterConfig { + pub consumer: Option>, + pub nacos: Option, + pub conditions: Option>, + pub tags: Option, +} diff --git a/dubbo/Cargo.toml b/dubbo/Cargo.toml index 51ccc193..736f0e8d 100644 --- a/dubbo/Cargo.toml +++ b/dubbo/Cargo.toml @@ -40,8 +40,13 @@ urlencoding.workspace = true lazy_static.workspace = true dubbo-base.workspace = true dubbo-logger.workspace = true +once_cell.workspace = true dubbo-config = { path = "../config", version = "0.3.0" } #对象存储 state = { version = "0.5", features = ["tls"] } + +regex = "1.9.1" +nacos-sdk = { version = "0.3.0", features = ["default"] } +serde_yaml = "0.9.22" \ No newline at end of file diff --git a/dubbo/src/cluster/mod.rs b/dubbo/src/cluster/mod.rs index afc3cc91..e6a5ffaa 100644 --- a/dubbo/src/cluster/mod.rs +++ b/dubbo/src/cluster/mod.rs @@ -30,6 +30,7 @@ use crate::{ pub mod directory; pub mod loadbalance; +pub mod router; pub trait Directory: Debug { fn list(&self, invocation: Arc) -> Vec; @@ -116,8 +117,9 @@ pub struct MockDirectory { impl MockDirectory { pub fn new() -> MockDirectory { + // let router_chain = get_global_router_manager().read().unwrap().get_router_chain(invocation); Self { - // router_chain: RouterChain::default(), + // router_chain } } } @@ -130,27 +132,3 @@ impl Directory for MockDirectory { // self.router_chain.route(u, invo); } } - -// #[derive(Debug, Default)] -// pub struct RouterChain { -// router: HashMap, -// invokers: Arc>, -// } - -// impl RouterChain { -// pub fn route(&mut self, url: Url, invo: Arc) -> Arc> { -// let r = self.router.get("mock").unwrap(); -// r.route(self.invokers.clone(), url, invo) -// } -// } - -// pub trait Router: Debug { -// fn route( -// &self, -// invokers: Arc>, -// url: Url, -// invo: Arc, -// ) -> Arc>; -// } - -// pub type BoxRouter = Box; diff --git a/dubbo/src/cluster/router/condition/condition_router.rs b/dubbo/src/cluster/router/condition/condition_router.rs new file mode 100644 index 00000000..f39cd1c7 --- /dev/null +++ b/dubbo/src/cluster/router/condition/condition_router.rs @@ -0,0 +1,60 @@ +use crate::{ + cluster::router::{condition::single_router::ConditionSingleRouter, Router}, + codegen::RpcInvocation, +}; +use dubbo_base::Url; +use std::{ + fmt::Debug, + sync::{Arc, RwLock}, +}; + +#[derive(Default, Debug, Clone)] +pub struct ConditionRouter { + //condition router for service scope + pub service_routers: Option>>, + //condition router for application scope + pub application_routers: Option>>, +} + +impl Router for ConditionRouter { + fn route(&self, invokers: Vec, url: Url, invo: Arc) -> Vec { + let mut invokers_result = invokers.clone(); + if let Some(routers) = self.application_routers.clone() { + for router in &routers.read().unwrap().routers { + invokers_result = router.route(invokers_result, url.clone(), invo.clone()) + } + } + if let Some(routers) = self.service_routers.clone() { + for router in &routers.read().unwrap().routers { + invokers_result = router.route(invokers_result, url.clone(), invo.clone()) + } + } + invokers_result + } +} + +impl ConditionRouter { + pub fn new( + service_routers: Option>>, + application_routers: Option>>, + ) -> Self { + Self { + service_routers, + application_routers, + } + } +} + +#[derive(Debug, Clone, Default)] +pub struct ConditionSingleRouters { + pub routers: Vec, +} + +impl ConditionSingleRouters { + pub fn new(routers: Vec) -> Self { + Self { routers } + } + pub fn is_null(&self) -> bool { + self.routers.is_empty() + } +} diff --git a/dubbo/src/cluster/router/condition/matcher.rs b/dubbo/src/cluster/router/condition/matcher.rs new file mode 100644 index 00000000..8c9177e8 --- /dev/null +++ b/dubbo/src/cluster/router/condition/matcher.rs @@ -0,0 +1,94 @@ +use crate::codegen::RpcInvocation; +use regex::Regex; +use std::{collections::HashSet, error::Error, option::Option, sync::Arc}; + +#[derive(Clone, Debug, Default)] +pub struct ConditionMatcher { + _key: String, + matches: HashSet, + mismatches: HashSet, +} + +impl ConditionMatcher { + pub fn new(_key: String) -> Self { + ConditionMatcher { + _key, + matches: HashSet::new(), + mismatches: HashSet::new(), + } + } + + pub fn is_match( + &self, + value: Option, + invocation: Arc, + _is_when: bool, + ) -> Result> { + match value { + None => { + // if key does not present in whichever of url, invocation or attachment based on the matcher type, then return false. + Ok(false) + } + Some(val) => { + if !self.matches.is_empty() && self.mismatches.is_empty() { + for match_ in self.matches.iter() { + if self.do_pattern_match(match_, &val, invocation.clone())? { + return Ok(true); + } + } + Ok(false) + } else if !self.mismatches.is_empty() && self.matches.is_empty() { + for mismatch in self.mismatches.iter() { + if self.do_pattern_match(mismatch, &val, invocation.clone())? { + return Ok(false); + } + } + Ok(true) + } else if !self.matches.is_empty() && !self.mismatches.is_empty() { + for mismatch in self.mismatches.iter() { + if self.do_pattern_match(mismatch, &val, invocation.clone())? { + return Ok(false); + } + } + for match_ in self.matches.iter() { + if self.do_pattern_match(match_, &val, invocation.clone())? { + return Ok(true); + } + } + Ok(false) + } else { + Ok(false) + } + } + } + } + pub fn get_matches(&mut self) -> &mut HashSet { + &mut self.matches + } + pub fn get_mismatches(&mut self) -> &mut HashSet { + &mut self.mismatches + } + + fn do_pattern_match( + &self, + pattern: &String, + value: &String, + _invocation: Arc, + ) -> Result> { + if pattern.contains("*") { + return Ok(star_matcher(pattern, value)); + } + Ok(pattern.eq(value)) + } +} + +pub fn star_matcher(pattern: &str, input: &str) -> bool { + // 将*替换为任意字符的正则表达式 + let pattern = pattern.replace("*", ".*"); + let regex = Regex::new(&pattern).unwrap(); + regex.is_match(input) +} + +pub fn _range_matcher(val: i32, min: i32, max: i32) -> bool { + min <= val && val <= max +} diff --git a/dubbo/src/cluster/router/condition/mod.rs b/dubbo/src/cluster/router/condition/mod.rs new file mode 100644 index 00000000..7285b88f --- /dev/null +++ b/dubbo/src/cluster/router/condition/mod.rs @@ -0,0 +1,3 @@ +pub mod condition_router; +pub mod matcher; +pub mod single_router; diff --git a/dubbo/src/cluster/router/condition/single_router.rs b/dubbo/src/cluster/router/condition/single_router.rs new file mode 100644 index 00000000..e290b150 --- /dev/null +++ b/dubbo/src/cluster/router/condition/single_router.rs @@ -0,0 +1,225 @@ +use dubbo_base::Url; +use dubbo_logger::tracing::info; +use regex::Regex; +use std::{ + collections::HashMap, + sync::{Arc, RwLock}, +}; + +use crate::{ + cluster::router::{condition::matcher::ConditionMatcher, utils::to_original_map, Router}, + codegen::RpcInvocation, + invocation::Invocation, +}; + +#[derive(Debug, Clone, Default)] +pub struct ConditionSingleRouter { + pub name: String, + pub when_condition: HashMap>>, + pub then_condition: HashMap>>, + pub enabled: bool, + pub force: bool, +} + +impl Router for ConditionSingleRouter { + fn route(&self, invokers: Vec, url: Url, invocation: Arc) -> Vec { + if !self.enabled { + return invokers; + }; + let mut result = Vec::new(); + if self.match_when(url.clone(), invocation.clone()) { + for invoker in &invokers.clone() { + if self.match_then(invoker.clone(), invocation.clone()) { + result.push(invoker.clone()); + } + } + if result.is_empty() && self.force == false { + invokers + } else { + result + } + } else { + invokers + } + } +} + +impl ConditionSingleRouter { + pub fn new(rule: String, force: bool, enabled: bool) -> Self { + let mut router = Self { + name: "condition".to_string(), + when_condition: HashMap::new(), + then_condition: HashMap::new(), + enabled, + force, + }; + if enabled { + router.init(rule).expect("parse rule error"); + } + router + } + + fn init(&mut self, rule: String) -> Result<(), Box> { + match rule.trim().is_empty() { + true => Err("Illegal route rule!".into()), + false => { + let r = rule.replace("consumer.", "").replace("provider.", ""); + let i = r.find("=>").unwrap_or_else(|| r.len()); + let when_rule = r[..i].trim().to_string(); + let then_rule = r[(i + 2)..].trim().to_string(); + let when = if when_rule.is_empty() || when_rule == "true" { + HashMap::new() + } else { + self.parse_rule(&when_rule)? + }; + let then = if then_rule.is_empty() || then_rule == "false" { + HashMap::new() + } else { + self.parse_rule(&then_rule)? + }; + self.when_condition = when; + self.then_condition = then; + Ok(()) + } + } + } + + fn parse_rule( + &mut self, + rule: &str, + ) -> Result>>, Box> { + let mut conditions: HashMap>> = HashMap::new(); + let mut current_matcher: Option>> = None; + let regex = Regex::new(r"([&!=,]*)\s*([^&!=,\s]+)").unwrap(); + for cap in regex.captures_iter(rule) { + let separator = &cap[1]; + let content = &cap[2]; + + match separator { + "" => { + let current_key = content.to_string(); + current_matcher = + Some(Arc::new(RwLock::new(self.get_matcher(current_key.clone())))); + conditions.insert( + current_key.clone(), + current_matcher.as_ref().unwrap().clone(), + ); + } + "&" => { + let current_key = content.to_string(); + current_matcher = conditions.get(¤t_key).cloned(); + if current_matcher.is_none() { + let matcher = Arc::new(RwLock::new(self.get_matcher(current_key.clone()))); + conditions.insert(current_key.clone(), matcher.clone()); + current_matcher = Some(matcher); + } + } + "=" => { + if let Some(matcher) = ¤t_matcher { + let mut matcher_borrowed = matcher.write().unwrap(); + matcher_borrowed + .get_matches() + .insert(content.to_string().clone()); + } else { + return Err("Error: ...".into()); + } + } + "!=" => { + if let Some(matcher) = ¤t_matcher { + let mut matcher_borrowed = matcher.write().unwrap(); + matcher_borrowed + .get_mismatches() + .insert(content.to_string().clone()); + } else { + return Err("Error: ...".into()); + } + } + "," => { + if let Some(matcher) = ¤t_matcher { + let mut matcher_borrowed = matcher.write().unwrap(); + if matcher_borrowed.get_matches().is_empty() + && matcher_borrowed.get_mismatches().is_empty() + { + return Err("Error: ...".into()); + } + drop(matcher_borrowed); + let mut matcher_borrowed_mut = matcher.write().unwrap(); + matcher_borrowed_mut + .get_matches() + .insert(content.to_string().clone()); + } else { + return Err("Error: ...".into()); + } + } + _ => { + return Err("Error: ...".into()); + } + } + } + Ok(conditions) + } + + // pub fn is_runtime(&self) -> bool { + // // same as the Java version + // } + + pub fn get_matcher(&self, key: String) -> ConditionMatcher { + ConditionMatcher::new(key) + } + + pub fn match_when(&self, url: Url, invocation: Arc) -> bool { + if self.when_condition.is_empty() { + true + } else { + false + }; + self.do_match(url, &self.when_condition, invocation, true) + } + + pub fn match_then(&self, url: Url, invocation: Arc) -> bool { + if self.when_condition.is_empty() { + true + } else { + false + }; + self.do_match(url, &self.then_condition, invocation, false) + } + + pub fn do_match( + &self, + url: Url, + conditions: &HashMap>>, + invocation: Arc, + is_when: bool, + ) -> bool { + let sample: HashMap = to_original_map(url); + for (key, condition_matcher) in conditions { + let matcher = condition_matcher.read().unwrap(); + let value = get_value(key, &sample, invocation.clone()); + match matcher.is_match(value, invocation.clone(), is_when) { + Ok(result) => { + if !result { + return false; + } + } + Err(error) => { + info!("Error occurred: {:?}", error); + return false; + } + } + } + true + } +} + +fn get_value( + key: &String, + sample: &HashMap, + invocation: Arc, +) -> Option { + if key == "method" { + let method_param = invocation.get_method_name(); + return Some(method_param); + } + sample.get(key).cloned() +} diff --git a/dubbo/src/cluster/router/manager/condition_manager.rs b/dubbo/src/cluster/router/manager/condition_manager.rs new file mode 100644 index 00000000..4207b79b --- /dev/null +++ b/dubbo/src/cluster/router/manager/condition_manager.rs @@ -0,0 +1,75 @@ +use crate::cluster::router::condition::{ + condition_router::{ConditionRouter, ConditionSingleRouters}, + single_router::ConditionSingleRouter, +}; +use dubbo_config::router::ConditionRouterConfig; +use std::{ + collections::HashMap, + sync::{Arc, RwLock}, +}; + +#[derive(Debug, Clone, Default)] +pub struct ConditionRouterManager { + //Application-level routing applies globally, while service-level routing only affects a specific service. + pub routers_service: HashMap>>, + pub routers_application: Arc>, +} + +impl ConditionRouterManager { + pub fn get_router(&self, service_name: String) -> Option { + let routers_service = self.routers_service.get(&service_name); + match routers_service { + Some(routers_service) => { + if self.routers_application.read().unwrap().is_null() { + return Some(ConditionRouter::new(Some(routers_service.clone()), None)); + } + Some(ConditionRouter::new( + Some(routers_service.clone()), + Some(self.routers_application.clone()), + )) + } + None => { + if self.routers_application.read().unwrap().is_null() { + return None; + } + Some(ConditionRouter::new( + None, + Some(self.routers_application.clone()), + )) + } + } + } + + pub fn update(&mut self, config: ConditionRouterConfig) { + let force = config.force; + let scope = config.scope; + let key = config.key; + let enable = config.enabled; + let mut routers = Vec::new(); + for condition in config.conditions { + routers.push(ConditionSingleRouter::new(condition, force, enable)); + } + match scope.as_str() { + "application" => { + self.routers_application.write().unwrap().routers = routers; + } + "service" => { + if let Some(x) = self.routers_service.get(&key) { + x.write().unwrap().routers = routers + } else { + self.routers_service.insert( + key, + Arc::new(RwLock::new(ConditionSingleRouters::new(routers))), + ); + } + } + _ => {} + } + } + + pub fn _parse_rules(&mut self, configs: Vec) { + for config in configs { + self.update(config) + } + } +} diff --git a/dubbo/src/cluster/router/manager/mod.rs b/dubbo/src/cluster/router/manager/mod.rs new file mode 100644 index 00000000..025f6c16 --- /dev/null +++ b/dubbo/src/cluster/router/manager/mod.rs @@ -0,0 +1,3 @@ +mod condition_manager; +pub mod router_manager; +mod tag_manager; diff --git a/dubbo/src/cluster/router/manager/router_manager.rs b/dubbo/src/cluster/router/manager/router_manager.rs new file mode 100644 index 00000000..a2a66586 --- /dev/null +++ b/dubbo/src/cluster/router/manager/router_manager.rs @@ -0,0 +1,161 @@ +use crate::{ + cluster::router::{ + manager::{condition_manager::ConditionRouterManager, tag_manager::TagRouterManager}, + nacos_config_center::nacos_client::NacosClient, + router_chain::RouterChain, + }, + invocation::{Invocation, RpcInvocation}, +}; +use dubbo_base::Url; +use dubbo_config::{ + get_global_config, + router::{ConditionRouterConfig, NacosConfig, TagRouterConfig}, +}; +use dubbo_logger::tracing::{info, trace}; +use once_cell::sync::OnceCell; +use std::{ + collections::HashMap, + sync::{Arc, RwLock}, +}; + +pub static GLOBAL_ROUTER_MANAGER: OnceCell>> = OnceCell::new(); + +pub struct RouterManager { + pub condition_router_manager: ConditionRouterManager, + pub tag_router_manager: TagRouterManager, + pub nacos: Option, + pub consumer: HashMap, +} + +impl RouterManager { + pub fn get_router_chain(&self, invocation: Arc) -> RouterChain { + let service = invocation.get_target_service_unique_name().clone(); + let condition_router = self.condition_router_manager.get_router(service.clone()); + let tag_router = self.tag_router_manager.get_router(); + let mut chain = RouterChain::new(); + match self.consumer.get(service.as_str()) { + None => {} + Some(url) => { + chain.set_condition_router(condition_router); + chain.set_tag_router(tag_router); + chain.self_url = url.clone(); + } + } + chain + } + + pub fn notify(&mut self, event: RouterConfigChangeEvent) { + match event.router_kind.as_str() { + "condition" => { + let config: ConditionRouterConfig = + serde_yaml::from_str(event.content.as_str()).unwrap(); + self.condition_router_manager.update(config) + } + "tag" => { + let config: TagRouterConfig = serde_yaml::from_str(event.content.as_str()).unwrap(); + self.tag_router_manager.update(config) + } + _ => { + info!("other router change event") + } + } + } + + pub fn init_nacos(&mut self, config: NacosConfig) { + self.nacos = Some(NacosClient::new_init_client(config)); + self.init_router_managers_for_nacos(); + } + + pub fn init_router_managers_for_nacos(&mut self) { + let config = self + .nacos + .as_ref() + .unwrap() + .get_tag_config("application".to_string()); + match config { + None => {} + Some(tag_config) => { + self.tag_router_manager.init(); + self.tag_router_manager.update(tag_config) + } + } + for (service_name, _) in &self.consumer { + let config = self + .nacos + .as_ref() + .unwrap() + .get_condition_config(service_name.clone()); + match config { + None => {} + Some(condition_config) => self.condition_router_manager.update(condition_config), + } + } + } + + pub fn init(&mut self) { + let config = get_global_config().routers.clone(); + let consumer_configs = get_global_config() + .routers + .consumer + .clone() + .unwrap_or(Vec::new()); + for consumer_config in consumer_configs { + self.consumer.insert( + consumer_config.service.clone(), + Url::from_url( + format!("{}/{}", consumer_config.url, consumer_config.service).as_str(), + ) + .expect("consumer配置出错!Url生成错误"), + ); + } + match &config.nacos { + None => { + trace!("Nacos not configured, using local YAML configuration for routing"); + let condition = config.conditions.clone(); + match condition { + None => { + info!("Unconfigured Condition Router") + } + Some(cons) => { + for con in cons { + self.condition_router_manager.update(con) + } + } + } + let tag = config.tags.clone(); + match tag { + None => { + info!("Unconfigured Tag Router") + } + Some(ta) => { + self.tag_router_manager.init(); + self.tag_router_manager.update(ta) + } + } + } + Some(config) => { + self.init_nacos(config.clone()); + } + } + } +} + +pub fn get_global_router_manager() -> &'static Arc> { + GLOBAL_ROUTER_MANAGER.get_or_init(|| { + let mut router_manager = RouterManager { + condition_router_manager: ConditionRouterManager::default(), + tag_router_manager: TagRouterManager::default(), + nacos: None, + consumer: HashMap::new(), + }; + router_manager.init(); + return Arc::new(RwLock::new(router_manager)); + }) +} + +#[derive(Debug, Default, Clone)] +pub struct RouterConfigChangeEvent { + pub service_name: String, + pub router_kind: String, + pub content: String, +} diff --git a/dubbo/src/cluster/router/manager/tag_manager.rs b/dubbo/src/cluster/router/manager/tag_manager.rs new file mode 100644 index 00000000..ce30c92e --- /dev/null +++ b/dubbo/src/cluster/router/manager/tag_manager.rs @@ -0,0 +1,27 @@ +use crate::cluster::router::tag::tag_router::TagRouter; +use dubbo_config::router::TagRouterConfig; +use std::sync::{Arc, RwLock}; + +#[derive(Debug, Clone, Default)] +pub struct TagRouterManager { + pub tag_router: Option>>, +} + +impl TagRouterManager { + pub fn init(&mut self) { + self.tag_router = Some(Arc::new(RwLock::new(TagRouter::default()))) + } + + pub fn get_router(&self) -> Option>> { + self.tag_router.clone() + } + + pub fn update(&mut self, config: TagRouterConfig) { + self.tag_router + .as_ref() + .unwrap() + .write() + .unwrap() + .parse_config(config) + } +} diff --git a/dubbo/src/cluster/router/mod.rs b/dubbo/src/cluster/router/mod.rs new file mode 100644 index 00000000..84ceae15 --- /dev/null +++ b/dubbo/src/cluster/router/mod.rs @@ -0,0 +1,25 @@ +pub mod condition; +pub mod manager; +pub mod nacos_config_center; +pub mod router_chain; +pub mod tag; +pub mod utils; + +use crate::invocation::RpcInvocation; +use dubbo_base::Url; +use std::{fmt::Debug, sync::Arc}; + +pub trait Router: Debug + Clone { + fn route(&self, invokers: Vec, url: Url, invo: Arc) -> Vec; +} + +// pub type BoxRouter = Box; + +#[derive(Debug, Default, Clone)] +pub struct MockRouter {} + +impl Router for MockRouter { + fn route(&self, invokers: Vec, _url: Url, _invo: Arc) -> Vec { + invokers + } +} diff --git a/dubbo/src/cluster/router/nacos_config_center/mod.rs b/dubbo/src/cluster/router/nacos_config_center/mod.rs new file mode 100644 index 00000000..7878fa9f --- /dev/null +++ b/dubbo/src/cluster/router/nacos_config_center/mod.rs @@ -0,0 +1 @@ +pub mod nacos_client; diff --git a/dubbo/src/cluster/router/nacos_config_center/nacos_client.rs b/dubbo/src/cluster/router/nacos_config_center/nacos_client.rs new file mode 100644 index 00000000..1600e9b0 --- /dev/null +++ b/dubbo/src/cluster/router/nacos_config_center/nacos_client.rs @@ -0,0 +1,132 @@ +use crate::cluster::router::manager::router_manager::{ + get_global_router_manager, RouterConfigChangeEvent, +}; +use dubbo_config::router::{ConditionRouterConfig, NacosConfig, TagRouterConfig}; +use dubbo_logger::{tracing, tracing::info}; +use nacos_sdk::api::{ + config::{ConfigChangeListener, ConfigResponse, ConfigService, ConfigServiceBuilder}, + props::ClientProps, +}; +use std::sync::{Arc, RwLock}; + +pub struct NacosClient { + pub client: Arc>, +} + +unsafe impl Send for NacosClient {} + +unsafe impl Sync for NacosClient {} + +pub struct ConfigChangeListenerImpl; + +impl NacosClient { + pub fn new_init_client(config: NacosConfig) -> Self { + let server_addr = config.addr; + let namespace = config.namespace; + let app = config.app; + match config.enable_auth { + None => { + info!("disable nacos auth!"); + info!("nacos init,addr:{}", server_addr); + let client = Arc::new(RwLock::new( + ConfigServiceBuilder::new( + ClientProps::new() + .server_addr(server_addr) + .namespace(namespace) + .app_name(app), + ) + .build() + .expect("NacosClient build failed!Please check NacosConfig"), + )); + Self { client } + } + Some(auth) => { + info!("enable nacos auth!"); + info!("nacos init,addr:{}", server_addr); + let client = Arc::new(RwLock::new( + ConfigServiceBuilder::new( + ClientProps::new() + .server_addr(server_addr) + .namespace(namespace) + .app_name(app) + .auth_username(auth.auth_username) + .auth_password(auth.auth_password), + ) + // .enable_auth_plugin_http() + .build() + .expect("NacosClient build failed!Please check NacosConfig"), + )); + return Self { client }; + } + } + } + pub fn get_condition_config(&self, data_id: String) -> Option { + let config_resp = self + .client + .read() + .unwrap() + .get_config(data_id.clone(), "condition".to_string()); + return match config_resp { + Ok(config_resp) => { + self.add_listener(data_id.clone(), "condition".to_string()); + let string = config_resp.content(); + Some(serde_yaml::from_str(string).unwrap()) + } + Err(_err) => None, + }; + } + + pub fn get_tag_config(&self, data_id: String) -> Option { + let config_resp = self + .client + .read() + .unwrap() + .get_config(data_id.clone(), "tag".to_string()); + return match config_resp { + Ok(config_resp) => { + self.add_listener(data_id.clone(), "tag".to_string()); + let string = config_resp.content(); + let result = serde_yaml::from_str(string); + match result { + Ok(config) => { + info!("success to get TagRouter config and parse success"); + Some(config) + } + _ => { + info!("failed to parse TagRouter rule"); + None + } + } + } + Err(_err) => None, + }; + } + pub fn add_listener(&self, data_id: String, group: String) { + let res_listener = self + .client + .write() + .expect("failed to create nacos config listener") + .add_listener(data_id, group, Arc::new(ConfigChangeListenerImpl {})); + match res_listener { + Ok(_) => { + info!("listening the config success"); + } + Err(err) => tracing::error!("listen config error {:?}", err), + } + } +} + +impl ConfigChangeListener for ConfigChangeListenerImpl { + fn notify(&self, config_resp: ConfigResponse) { + let content_type = config_resp.content_type(); + let event = RouterConfigChangeEvent { + service_name: config_resp.data_id().clone(), + router_kind: config_resp.group().clone(), + content: config_resp.content().clone(), + }; + if content_type == "yaml" { + get_global_router_manager().write().unwrap().notify(event); + } + info!("notify config={:?}", config_resp); + } +} diff --git a/dubbo/src/cluster/router/router_chain.rs b/dubbo/src/cluster/router/router_chain.rs new file mode 100644 index 00000000..930f0f29 --- /dev/null +++ b/dubbo/src/cluster/router/router_chain.rs @@ -0,0 +1,74 @@ +use crate::{ + cluster::router::{ + condition::condition_router::ConditionRouter, tag::tag_router::TagRouter, Router, + }, + invocation::RpcInvocation, +}; +use dubbo_base::Url; +use std::sync::{Arc, RwLock}; + +#[derive(Debug, Default, Clone)] +pub struct RouterChain { + pub condition_router: Option, + pub tag_router: Option>>, + pub self_url: Url, +} + +impl RouterChain { + pub fn new() -> Self { + RouterChain { + condition_router: None, + tag_router: None, + self_url: Url::new(), + } + } + pub fn set_condition_router(&mut self, router: Option) { + self.condition_router = router; + } + pub fn set_tag_router(&mut self, router: Option>>) { + self.tag_router = router; + } + pub fn route(&self, invokers: Vec, invocation: Arc) -> Vec { + let mut result = invokers.clone(); + match &self.tag_router { + None => {} + Some(router) => { + result = + router + .read() + .unwrap() + .route(result, self.self_url.clone(), invocation.clone()) + } + } + match &self.condition_router { + Some(router) => { + result = router.route(result, self.self_url.clone(), invocation.clone()) + } + None => {} + } + result + } +} + +#[test] +fn test() { + use crate::cluster::router::manager::router_manager::get_global_router_manager; + + let u1 = Url::from_url("triple://127.0.0.1:8888/org.apache.dubbo.sample.tri.Greeter").unwrap(); + let u2 = Url::from_url("triple://127.0.0.1:8889/org.apache.dubbo.sample.tri.Greeter").unwrap(); + let u3 = Url::from_url("triple://127.0.0.1:8800/org.apache.dubbo.sample.tri.Greeter").unwrap(); + let u4 = Url::from_url("triple://127.0.2.1:880/org.apache.dubbo.sample.tri.Greeter").unwrap(); + let u5 = Url::from_url("triple://127.0.1.1:8888/org.apache.dubbo.sample.tri.Greeter").unwrap(); + let invos = vec![u1, u2, u3, u4, u5]; + let invo = Arc::new( + RpcInvocation::default() + .with_method_name("greet".to_string()) + .with_service_unique_name("org.apache.dubbo.sample.tri.Greeter".to_string()), + ); + let x = get_global_router_manager() + .read() + .unwrap() + .get_router_chain(invo.clone()); + let result = x.route(invos, invo.clone()); + dbg!(result); +} diff --git a/dubbo/src/cluster/router/tag/mod.rs b/dubbo/src/cluster/router/tag/mod.rs new file mode 100644 index 00000000..6ac5b218 --- /dev/null +++ b/dubbo/src/cluster/router/tag/mod.rs @@ -0,0 +1 @@ +pub mod tag_router; diff --git a/dubbo/src/cluster/router/tag/tag_router.rs b/dubbo/src/cluster/router/tag/tag_router.rs new file mode 100644 index 00000000..d1a962e7 --- /dev/null +++ b/dubbo/src/cluster/router/tag/tag_router.rs @@ -0,0 +1,70 @@ +use crate::{ + cluster::router::{utils::to_original_map, Router}, + codegen::RpcInvocation, +}; +use dubbo_base::Url; +use dubbo_config::router::TagRouterConfig; +use std::{collections::HashMap, fmt::Debug, sync::Arc}; + +#[derive(Debug, Clone, Default)] +pub struct TagRouter { + pub tag_rules: HashMap>, + pub force: bool, + pub enabled: bool, +} + +impl TagRouter { + pub fn parse_config(&mut self, config: TagRouterConfig) { + self.tag_rules = HashMap::new(); + self.force = config.force; + self.enabled = config.enabled; + for tag in &config.tags { + let mut tags = HashMap::new(); + for rule in &tag.matches { + tags.insert(rule.key.clone(), rule.value.clone()); + } + self.tag_rules.insert(tag.name.clone(), tags); + } + } + + pub fn match_tag(&self, params: HashMap) -> Option { + let mut tag_result = None; + for (tag, tag_rules) in &self.tag_rules { + for (key, value) in tag_rules { + match params.get(key.as_str()) { + None => {} + Some(val) => { + if val == value { + tag_result = Some(tag.clone()) + } + } + } + } + } + tag_result + } +} + +impl Router for TagRouter { + fn route(&self, invokers: Vec, url: Url, _invocation: Arc) -> Vec { + if !self.enabled { + return invokers; + }; + let self_param = to_original_map(url); + let invocation_tag = self.match_tag(self_param); + let mut invokers_result = Vec::new(); + for invoker in &invokers { + let invoker_param = to_original_map(invoker.clone()); + let invoker_tag = self.match_tag(invoker_param); + if invoker_tag == invocation_tag { + invokers_result.push(invoker.clone()) + } + } + if invokers_result.is_empty() { + if !self.force { + return invokers; + } + } + invokers_result + } +} diff --git a/dubbo/src/cluster/router/utils.rs b/dubbo/src/cluster/router/utils.rs new file mode 100644 index 00000000..2ca50fcc --- /dev/null +++ b/dubbo/src/cluster/router/utils.rs @@ -0,0 +1,16 @@ +use dubbo_base::Url; +use std::{collections::HashMap, string::String}; + +pub fn to_original_map(url: Url) -> HashMap { + let mut result: HashMap = HashMap::new(); + result.insert("scheme".parse().unwrap(), url.scheme); + result.insert("location".parse().unwrap(), url.location); + result.insert("ip".parse().unwrap(), url.ip); + result.insert("port".parse().unwrap(), url.port); + result.insert("service_name".parse().unwrap(), url.service_name); + result.insert("service_key".parse().unwrap(), url.service_key); + for (key, value) in url.params { + result.insert(key, value); + } + result +} diff --git a/dubbo/src/triple/client/builder.rs b/dubbo/src/triple/client/builder.rs index d68067a8..b8118e23 100644 --- a/dubbo/src/triple/client/builder.rs +++ b/dubbo/src/triple/client/builder.rs @@ -30,6 +30,7 @@ use dubbo_base::Url; pub type ClientBoxService = BoxCloneService, http::Response, crate::Error>; +#[allow(dead_code)] #[derive(Clone, Debug, Default)] pub struct ClientBuilder { pub timeout: Option, @@ -56,7 +57,7 @@ impl ClientBuilder { connector: "", directory: Some(Arc::new(Box::new(StaticDirectory::new(&host)))), direct: true, - host: host.clone().to_string(), + host: host.to_string(), } } From 556d8b7d28827abe6ae34bec291aa58338c3e3f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=AF=9B=E6=96=87=E8=B6=85?= Date: Sat, 16 Sep 2023 16:38:45 +0800 Subject: [PATCH 32/48] Ftr: failover cluster (#156) --- dubbo/Cargo.toml | 3 +- dubbo/src/cluster/directory.rs | 10 +- dubbo/src/cluster/mod.rs | 387 +++++++++++++-- dubbo/src/invocation.rs | 4 +- dubbo/src/protocol/mod.rs | 7 +- dubbo/src/protocol/triple/triple_invoker.rs | 9 +- dubbo/src/triple/client/builder.rs | 7 +- dubbo/src/triple/client/mod.rs | 2 +- dubbo/src/triple/client/replay.rs | 441 ++++++++++++++++++ dubbo/src/triple/client/triple.rs | 43 +- .../echo/src/generated/grpc.examples.echo.rs | 187 +++++--- 11 files changed, 960 insertions(+), 140 deletions(-) create mode 100644 dubbo/src/triple/client/replay.rs diff --git a/dubbo/Cargo.toml b/dubbo/Cargo.toml index 736f0e8d..97c48992 100644 --- a/dubbo/Cargo.toml +++ b/dubbo/Cargo.toml @@ -47,6 +47,7 @@ dubbo-config = { path = "../config", version = "0.3.0" } #对象存储 state = { version = "0.5", features = ["tls"] } +thiserror = "1.0.48" regex = "1.9.1" nacos-sdk = { version = "0.3.0", features = ["default"] } -serde_yaml = "0.9.22" \ No newline at end of file +serde_yaml = "0.9.22" diff --git a/dubbo/src/cluster/directory.rs b/dubbo/src/cluster/directory.rs index afe9657b..06b840f0 100644 --- a/dubbo/src/cluster/directory.rs +++ b/dubbo/src/cluster/directory.rs @@ -24,7 +24,6 @@ use std::{ use crate::{ codegen::TripleInvoker, - invocation::{Invocation, RpcInvocation}, protocol::BoxInvoker, registry::{memory_registry::MemoryNotifyListener, BoxRegistry}, }; @@ -51,7 +50,7 @@ impl StaticDirectory { panic!("http uri parse error: {}, host: {}", err, host) } }; - StaticDirectory { uri: uri } + StaticDirectory { uri } } pub fn from_uri(uri: &http::Uri) -> StaticDirectory { @@ -60,12 +59,12 @@ impl StaticDirectory { } impl Directory for StaticDirectory { - fn list(&self, invocation: Arc) -> Vec { + fn list(&self, service_name: String) -> Vec { let url = Url::from_url(&format!( "tri://{}:{}/{}", self.uri.host().unwrap(), self.uri.port().unwrap(), - invocation.get_target_service_unique_name(), + service_name, )) .unwrap(); let invoker = Box::new(TripleInvoker::new(url)); @@ -89,8 +88,7 @@ impl RegistryDirectory { } impl Directory for RegistryDirectory { - fn list(&self, invocation: Arc) -> Vec { - let service_name = invocation.get_target_service_unique_name(); + fn list(&self, service_name: String) -> Vec { let url = Url::from_url(&format!( "triple://{}:{}/{}", diff --git a/dubbo/src/cluster/mod.rs b/dubbo/src/cluster/mod.rs index e6a5ffaa..7411a675 100644 --- a/dubbo/src/cluster/mod.rs +++ b/dubbo/src/cluster/mod.rs @@ -15,17 +15,19 @@ * limitations under the License. */ -use std::{fmt::Debug, sync::Arc, task::Poll}; +use std::{fmt::Debug, sync::Arc}; -use aws_smithy_http::body::SdkBody; use dubbo_base::Url; -use futures_util::TryFutureExt; -use tower::ready_cache::ReadyCache; +use thiserror::Error; +use tower::{ready_cache::ReadyCache, ServiceExt}; use tower_service::Service; use crate::{ - invocation::RpcInvocation, + codegen::RpcInvocation, + invocation::Invocation, protocol::{triple::triple_invoker::TripleInvoker, BoxInvoker, Invoker}, + triple::client::replay::ClonedBody, + StdError, }; pub mod directory; @@ -33,7 +35,7 @@ pub mod loadbalance; pub mod router; pub trait Directory: Debug { - fn list(&self, invocation: Arc) -> Vec; + fn list(&self, service_name: String) -> Vec; } type BoxDirectory = Box; @@ -55,7 +57,7 @@ impl Cluster for MockCluster { #[derive(Debug)] pub struct FailoverCluster { dir: Arc, - caches: ReadyCache>, + caches: ReadyCache>, } impl FailoverCluster { @@ -67,7 +69,45 @@ impl FailoverCluster { } } -impl Service> for FailoverCluster { +#[derive(Error, Debug)] +#[error("no available service for {0}")] +pub struct NoAvailableServiceErr(String); + +#[derive(Error, Debug)] +#[error("invalid service name {0}")] +pub struct InvalidServiceNameErr(String); + +impl FailoverCluster { + async fn invoke( + req: http::Request, + mut invoker: BoxInvoker, + ) -> Result, (StdError, http::Request)> { + let clone_request = FailoverCluster::clone_request(&req); + let invoker = invoker + .ready() + .await + .map_err(|e| (e, FailoverCluster::clone_request(&req)))?; + let ret = invoker.call(req).await.map_err(|e| (e, clone_request))?; + + Ok(ret) + } + + fn clone_request(req: &http::Request) -> http::Request { + let mut clone = http::Request::new(req.body().clone()); + *clone.method_mut() = req.method().clone(); + *clone.uri_mut() = req.uri().clone(); + *clone.headers_mut() = req.headers().clone(); + *clone.version_mut() = req.version(); + + if let Some(inv) = req.extensions().get::().cloned() { + clone.extensions_mut().insert(inv); + } + + clone + } +} + +impl Service> for FailoverCluster { type Response = http::Response; type Error = crate::Error; @@ -76,35 +116,55 @@ impl Service> for FailoverCluster { fn poll_ready( &mut self, - _cx: &mut std::task::Context<'_>, + cx: &mut std::task::Context<'_>, ) -> std::task::Poll> { - // if self.dir.is_empty() return err - Poll::Ready(Ok(())) + self.caches.poll_pending(cx).map_err(|e| e.into()) } - fn call(&mut self, req: http::Request) -> Self::Future { - // let clone_body = req.body().try_clone().unwrap(); - // let mut clone_req = http::Request::builder() - // .uri(req.uri().clone()) - // .method(req.method().clone()); - // *clone_req.headers_mut().unwrap() = req.headers().clone(); - // let r = clone_req.body(clone_body).unwrap(); - let invokers = self.dir.list( - RpcInvocation::default() - .with_service_unique_name("hello".to_string()) - .into(), - ); - let mut i: usize = 0; - for invoker in invokers { - self.caches.push(i, invoker); - i += 1; + fn call(&mut self, req: http::Request) -> Self::Future { + let inv = req.extensions().get::(); + if inv.is_none() { + return Box::pin(async move { + return Err( + InvalidServiceNameErr("service name must not be null".to_owned()).into(), + ); + }); } - Box::pin(self.caches.call_ready_index(0, req).map_err(Into::into)) + let inv = inv.unwrap(); + let service_name = inv.get_target_service_unique_name(); + + let invokers = self.dir.list(service_name.clone()); + + Box::pin(async move { + let mut current_req = req; + let mut last_err = None; + + let is_empty = invokers.is_empty(); + if is_empty { + return Err(NoAvailableServiceErr(service_name).into()); + } + + for invoker in invokers { + match FailoverCluster::invoke(current_req, invoker).await { + Ok(resp) => return Ok(resp), + Err((e, cloned_request)) => { + current_req = cloned_request; + last_err = Some(e); + } + } + } + + if last_err.is_none() { + return Err(NoAvailableServiceErr(service_name).into()); + } + + return Err(last_err.unwrap()); + }) } } -impl Invoker> for FailoverCluster { +impl Invoker> for FailoverCluster { fn get_url(&self) -> dubbo_base::Url { Url::from_url("triple://127.0.0.1:8888/helloworld.Greeter").unwrap() } @@ -125,10 +185,277 @@ impl MockDirectory { } impl Directory for MockDirectory { - fn list(&self, _invo: Arc) -> Vec { + fn list(&self, service_name: String) -> Vec { // tracing::info!("MockDirectory: {}", meta); let u = Url::from_url("triple://127.0.0.1:8888/helloworld.Greeter").unwrap(); vec![Box::new(TripleInvoker::new(u))] // self.router_chain.route(u, invo); } } + + +// #[derive(Debug, Default)] +// pub struct RouterChain { +// router: HashMap, +// invokers: Arc>, +// } + +// impl RouterChain { +// pub fn route(&mut self, url: Url, invo: Arc) -> Arc> { +// let r = self.router.get("mock").unwrap(); +// r.route(self.invokers.clone(), url, invo) +// } +// } + +// pub trait Router: Debug { +// fn route( +// &self, +// invokers: Arc>, +// url: Url, +// invo: Arc, +// ) -> Arc>; +// } + +// pub type BoxRouter = Box; + +#[cfg(test)] +pub mod tests { + use std::task::Poll; + + use bytes::{Buf, BufMut, BytesMut}; + use dubbo_base::Url; + use futures_util::future::poll_fn; + use http::StatusCode; + use http_body::Body; + use thiserror::Error; + use tower::ServiceExt; + use tower_service::Service; + + use crate::{ + boxed, + cluster::FailoverCluster, + codegen::{Invoker, RpcInvocation}, + empty_body, + invocation::Invocation, + triple::client::replay::ClonedBody, + }; + + use super::Directory; + + #[derive(Error, Debug)] + #[error("{0}")] + struct NoResponseErr(String); + + #[derive(Debug)] + struct MockDirectory; + + impl Directory for MockDirectory { + fn list(&self, service_name: String) -> Vec { + println!("get invoker list for {}", service_name); + + vec![ + Box::new(MockInvoker(1)), + Box::new(MockInvoker(2)), + Box::new(MockInvoker(3)), + Box::new(MockInvoker(4)), + Box::new(MockInvoker(5)), + ] + } + } + + #[derive(Debug)] + struct MockInvoker(u8); + + impl Invoker> for MockInvoker { + fn get_url(&self) -> dubbo_base::Url { + let str = format!( + "triple://127.0.0.1:8888/failover_cluster_service/{}", + self.0 + ); + Url::from_url(str.as_str()).unwrap() + } + } + + impl Service> for MockInvoker { + type Response = http::Response; + + type Error = crate::Error; + + type Future = crate::BoxFuture, crate::Error>; + + fn poll_ready( + &mut self, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, req: http::Request) -> Self::Future { + let inv = req.extensions().get::(); + if inv.is_none() { + return Box::pin(async move { + let response = http::Response::builder() + .status(StatusCode::OK) + .body(empty_body()) + .unwrap(); + + return Ok(response); + }); + } + + let inv = inv.unwrap(); + let method_name = inv.get_method_name(); + if method_name.eq("invoker_request") { + return Box::pin(async move { + let response = http::Response::builder() + .status(StatusCode::OK) + .body(boxed("invoker response".to_owned())) + .unwrap(); + + return Ok(response); + }); + } + + let self_index = self.0; + if method_name.eq("failover_request") { + return Box::pin(async move { + let body = req.into_body(); + let mut pin_body = Box::pin(body); + let ret = poll_fn(|cx| pin_body.as_mut().poll_data(cx)).await; + + if ret.is_none() { + #[derive(Error, Debug)] + #[error("{0}")] + struct BodyIsNoneErr(&'static str); + + return Err(BodyIsNoneErr("body must not be null").into()); + } + + let ret = ret.unwrap(); + + if ret.is_err() { + #[derive(Error, Debug)] + #[error("{0}")] + struct BodyIsErr(&'static str); + return Err(BodyIsErr("body must be ok").into()); + } + + let mut ret = ret.unwrap(); + + let index = ret.get_u8(); + + if index == self_index { + let ret_msg = format!("failover cluster index: {} was invoked", self_index); + let response = http::Response::builder() + .status(StatusCode::OK) + .body(boxed(ret_msg)) + .unwrap(); + + return Ok(response); + } + + #[derive(Error, Debug)] + #[error("{0}")] + struct NotTargetInvoker(String); + + let ret_msg = format!( + "failover cluster index: {} was invoked, but is not target invoker {}", + self_index, index + ); + + println!("{}", ret_msg); + return Err(NotTargetInvoker(ret_msg).into()); + }); + } + + return Box::pin(async move { return Err(NoResponseErr(method_name).into()) }); + } + } + + #[tokio::test] + async fn test_failover_cluster() { + let mut cluster = FailoverCluster::new(Box::new(MockDirectory)); + let cluster = cluster.ready().await; + assert!(cluster.is_ok()); + + let cluster = cluster.unwrap(); + + let empty_stream = futures::stream::empty(); + let cloned_body = ClonedBody::new(empty_stream); + + let rpc_inv = RpcInvocation::default() + .with_service_unique_name("failover_cluster_service".to_owned()) + .with_method_name("invoker_request".to_owned()); + + let req = http::Request::builder() + .extension(rpc_inv) + .body(cloned_body) + .unwrap(); + + let ret = cluster.call(req).await; + assert!(ret.is_ok()); + + let ret = ret.unwrap(); + + assert_eq!(ret.status(), StatusCode::OK); + + let body = ret.into_body(); + + let mut pin = Box::pin(body); + let data = poll_fn(|cx| pin.as_mut().poll_data(cx)).await; + assert!(data.is_some()); + let data = data.unwrap(); + assert!(data.is_ok()); + let data = data.unwrap(); + + assert_eq!( + String::from_utf8(data.to_vec()).unwrap(), + "invoker response" + ) + } + + #[tokio::test] + async fn test_failover_request() { + let mut cluster = FailoverCluster::new(Box::new(MockDirectory)); + let cluster = cluster.ready().await; + assert!(cluster.is_ok()); + + let cluster = cluster.unwrap(); + + let once_stream = futures::stream::once(async { + let mut mut_bytes = BytesMut::default(); + mut_bytes.put_u8(5); + return Ok(mut_bytes.freeze()); + }); + let cloned_body = ClonedBody::new(once_stream); + + let rpc_inv = RpcInvocation::default() + .with_service_unique_name("failover_cluster_service".to_owned()) + .with_method_name("failover_request".to_owned()); + + let req = http::Request::builder() + .extension(rpc_inv) + .body(cloned_body) + .unwrap(); + + let ret = cluster.call(req).await; + assert!(ret.is_ok()); + + let ret = ret.unwrap(); + + assert_eq!(ret.status(), StatusCode::OK); + + let body = ret.into_body(); + + let mut pin = Box::pin(body); + let data = poll_fn(|cx| pin.as_mut().poll_data(cx)).await; + assert!(data.is_some()); + let data = data.unwrap(); + assert!(data.is_ok()); + let data = data.unwrap(); + + let resp_str = String::from_utf8(data.to_vec()).unwrap(); + println!("{}", resp_str); + assert_eq!(resp_str, "failover cluster index: 5 was invoked") + } +} diff --git a/dubbo/src/invocation.rs b/dubbo/src/invocation.rs index bdd152be..d3eb8ad0 100644 --- a/dubbo/src/invocation.rs +++ b/dubbo/src/invocation.rs @@ -196,7 +196,7 @@ pub trait Invocation { fn get_method_name(&self) -> String; } -#[derive(Default)] +#[derive(Default,Clone)] pub struct RpcInvocation { target_service_unique_name: String, method_name: String, @@ -224,4 +224,4 @@ impl Invocation for RpcInvocation { fn get_method_name(&self) -> String { self.method_name.clone() } -} +} \ No newline at end of file diff --git a/dubbo/src/protocol/mod.rs b/dubbo/src/protocol/mod.rs index 4dceb45e..50080555 100644 --- a/dubbo/src/protocol/mod.rs +++ b/dubbo/src/protocol/mod.rs @@ -26,6 +26,8 @@ use tower_service::Service; use dubbo_base::Url; +use crate::triple::client::replay::{ClonedBytesStream, ClonedBody}; + pub mod server_desc; pub mod triple; @@ -49,12 +51,11 @@ pub trait Invoker: Debug + Service { pub type BoxExporter = Box; pub type BoxInvoker = Box< dyn Invoker< - http::Request, + http::Request, Response = http::Response, Error = crate::Error, Future = crate::BoxFuture, crate::Error>, - > + Send - + Sync, + > + Send, >; pub struct WrapperInvoker(T); diff --git a/dubbo/src/protocol/triple/triple_invoker.rs b/dubbo/src/protocol/triple/triple_invoker.rs index db18f5f1..af174451 100644 --- a/dubbo/src/protocol/triple/triple_invoker.rs +++ b/dubbo/src/protocol/triple/triple_invoker.rs @@ -15,7 +15,6 @@ * limitations under the License. */ -use aws_smithy_http::body::SdkBody; use dubbo_base::Url; use std::{ fmt::{Debug, Formatter}, @@ -25,7 +24,7 @@ use tower_service::Service; use crate::{ protocol::Invoker, - triple::{client::builder::ClientBoxService, transport::connection::Connection}, + triple::{client::{builder::ClientBoxService, replay::ClonedBody}, transport::connection::Connection}, utils::boxed_clone::BoxCloneService, }; @@ -51,14 +50,14 @@ impl Debug for TripleInvoker { } } -impl Service> for TripleInvoker { +impl Service> for TripleInvoker { type Response = http::Response; type Error = crate::Error; type Future = crate::BoxFuture; - fn call(&mut self, req: http::Request) -> Self::Future { + fn call(&mut self, req: http::Request) -> Self::Future { self.conn.call(req) } @@ -70,7 +69,7 @@ impl Service> for TripleInvoker { } } -impl Invoker> for TripleInvoker { +impl Invoker> for TripleInvoker { fn get_url(&self) -> Url { self.url.clone() } diff --git a/dubbo/src/triple/client/builder.rs b/dubbo/src/triple/client/builder.rs index b8118e23..5b2702fb 100644 --- a/dubbo/src/triple/client/builder.rs +++ b/dubbo/src/triple/client/builder.rs @@ -24,11 +24,12 @@ use crate::{ utils::boxed_clone::BoxCloneService, }; -use aws_smithy_http::body::SdkBody; use dubbo_base::Url; +use super::replay::ClonedBody; + pub type ClientBoxService = - BoxCloneService, http::Response, crate::Error>; + BoxCloneService, http::Response, crate::Error>; #[allow(dead_code)] #[derive(Clone, Debug, Default)] @@ -101,7 +102,7 @@ impl ClientBuilder { Self { direct, ..self } } - pub fn build(self, _invocation: Arc) -> Option { + pub fn build(self) -> Option { if self.direct { return Some(Box::new(TripleInvoker::new( Url::from_url(&self.host).unwrap(), diff --git a/dubbo/src/triple/client/mod.rs b/dubbo/src/triple/client/mod.rs index 7a8e0131..97be85eb 100644 --- a/dubbo/src/triple/client/mod.rs +++ b/dubbo/src/triple/client/mod.rs @@ -17,5 +17,5 @@ pub mod builder; pub mod triple; - +pub mod replay; pub use triple::TripleClient; diff --git a/dubbo/src/triple/client/replay.rs b/dubbo/src/triple/client/replay.rs new file mode 100644 index 00000000..4ba8b930 --- /dev/null +++ b/dubbo/src/triple/client/replay.rs @@ -0,0 +1,441 @@ +use std::{ + collections::VecDeque, + pin::Pin, + sync::{Arc, Mutex}, + task::{Context, Poll}, +}; + +use bytes::{Buf, BufMut, Bytes, BytesMut}; +use futures_core::{ready, stream::BoxStream, Stream}; +use futures_util::StreamExt; +use http_body::Body; +use pin_project::pin_project; + +use crate::status::Status; + +type BoxBytesStream = BoxStream<'static, Result>; +pub struct ClonedBytesStream { + shared: Arc>>, + owned: Option, + replay: bool, +} + +pub struct OwnedBytesStream { + bytes_stream: BoxBytesStream, + buf: InnerBuffer, +} + +#[derive(Clone)] +pub struct InnerBuffer { + bufs: VecDeque, + capacity: usize, +} + +impl ClonedBytesStream { + pub fn new(buffer_capacity: usize, stream: BoxBytesStream) -> Self { + Self { + shared: Arc::new(Mutex::new(None)), + owned: Some(OwnedBytesStream { + bytes_stream: stream, + buf: InnerBuffer { + bufs: Default::default(), + capacity: buffer_capacity, + }, + }), + replay: false, + } + } +} + +impl Clone for ClonedBytesStream { + fn clone(&self) -> Self { + Self { + shared: self.shared.clone(), + owned: None, + replay: true, + } + } +} + +impl Drop for ClonedBytesStream { + fn drop(&mut self) { + if let Some(owned) = self.owned.take() { + let lock = self.shared.lock(); + if let Ok(mut lock) = lock { + *lock = Some(owned); + } + } + } +} + +impl InnerBuffer { + pub fn push_bytes(&mut self, bytes: Bytes) -> Bytes { + self.bufs.push_back(bytes.clone()); + bytes + } + + pub fn is_capped(&self) -> bool { + self.capacity == 0 + } +} + +impl Buf for InnerBuffer { + fn remaining(&self) -> usize { + self.bufs.iter().map(|bytes| bytes.remaining()).sum() + } + + fn chunk(&self) -> &[u8] { + self.bufs.front().map(Buf::chunk).unwrap_or(&[]) + } + + fn chunks_vectored<'a>(&'a self, dst: &mut [std::io::IoSlice<'a>]) -> usize { + if dst.is_empty() { + return 0; + } + + let mut filled = 0; + + for bytes in self.bufs.iter() { + filled += bytes.chunks_vectored(&mut dst[filled..]) + } + + filled + } + + fn advance(&mut self, mut cnt: usize) { + while cnt > 0 { + let first = self.bufs.front_mut(); + if first.is_none() { + break; + } + let first = first.unwrap(); + let first_remaining = first.remaining(); + if first_remaining > cnt { + first.advance(cnt); + break; + } + + first.advance(first_remaining); + cnt = cnt - first_remaining; + self.bufs.pop_front(); + } + } + + fn copy_to_bytes(&mut self, len: usize) -> bytes::Bytes { + match self.bufs.front_mut() { + Some(buf) if len <= buf.remaining() => { + let bytes = buf.copy_to_bytes(len); + if buf.remaining() == 0 { + self.bufs.pop_front(); + } + bytes + } + _ => { + let mut bytes = BytesMut::with_capacity(len); + bytes.put(self.take(len)); + bytes.freeze() + } + } + } +} + +pub enum BytesData { + BufferedBytes(InnerBuffer), + OriginBytes(Bytes), +} + +impl Buf for BytesData { + fn remaining(&self) -> usize { + match self { + BytesData::BufferedBytes(bytes) => bytes.remaining(), + BytesData::OriginBytes(bytes) => bytes.remaining(), + } + } + + fn chunk(&self) -> &[u8] { + match self { + BytesData::BufferedBytes(bytes) => bytes.chunk(), + BytesData::OriginBytes(bytes) => bytes.chunk(), + } + } + + fn advance(&mut self, cnt: usize) { + match self { + BytesData::BufferedBytes(bytes) => bytes.advance(cnt), + BytesData::OriginBytes(bytes) => bytes.advance(cnt), + } + } + + fn copy_to_bytes(&mut self, len: usize) -> bytes::Bytes { + match self { + BytesData::BufferedBytes(bytes) => bytes.copy_to_bytes(len), + BytesData::OriginBytes(bytes) => bytes.copy_to_bytes(len), + } + } +} + +impl Stream for ClonedBytesStream { + type Item = Result; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut_self = self.get_mut(); + + let owned_bytes_stream = mut_self.owned.get_or_insert_with(|| { + let lock = mut_self.shared.lock(); + if let Err(e) = lock { + panic!("bytes streams get shared data lock failed. {}", e); + } + let mut data = lock.unwrap(); + + data.take().expect("cannot get shared bytes streams.") + }); + + if mut_self.replay { + mut_self.replay = false; + if owned_bytes_stream.buf.has_remaining() { + return Poll::Ready(Some(Ok(BytesData::BufferedBytes( + owned_bytes_stream.buf.clone(), + )))); + } + } + + let next = owned_bytes_stream.bytes_stream.poll_next_unpin(cx); + + let next = match ready!(next) { + Some(next) => match next { + Ok(next) => next, + Err(e) => return Poll::Ready(Some(Err(e))), + }, + _ => return Poll::Ready(None), + }; + + let len = next.len(); + owned_bytes_stream.buf.capacity = owned_bytes_stream.buf.capacity.saturating_sub(len); + let next = if owned_bytes_stream.buf.is_capped() { + if owned_bytes_stream.buf.has_remaining() { + owned_bytes_stream.buf.bufs = VecDeque::default(); + } + next + } else { + owned_bytes_stream.buf.push_bytes(next) + }; + + return Poll::Ready(Some(Ok(BytesData::OriginBytes(next)))); + } +} + +#[pin_project] +#[derive(Clone)] +pub struct ClonedBody(#[pin] ClonedBytesStream); + +impl ClonedBody { + pub fn new(inner_body: T) -> Self + where + T: Stream> + Send + 'static, + { + let inner_body = Box::pin(inner_body); + let inner_body = ClonedBytesStream::new(1024 * 64, inner_body); + ClonedBody(inner_body) + } +} + +impl Body for ClonedBody { + type Data = BytesData; + + type Error = Status; + + fn poll_data( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll>> { + self.project().0.poll_next(cx) + } + + fn poll_trailers( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll, Self::Error>> { + Poll::Ready(Ok(None)) + } + + fn size_hint(&self) -> http_body::SizeHint { + http_body::SizeHint::default() + } +} + +#[cfg(test)] +pub mod tests { + use super::*; + + #[tokio::test] + async fn test_cloned_bytes_stream() { + let bytes1 = Ok(Bytes::from("hello".as_bytes())); + let bytes2 = Ok(Bytes::from(" world".as_bytes())); + let bytes3 = Ok(Bytes::from(", end test!".as_bytes())); + + let stream = futures_util::stream::iter(vec![bytes1, bytes2, bytes3]); + + let mut origin_stream = ClonedBytesStream::new(64 * 1024, Box::pin(stream)); + let hello_bytes = origin_stream.next().await; + + assert!(hello_bytes.is_some()); + + let hello_bytes = hello_bytes.unwrap(); + + assert!(hello_bytes.is_ok()); + + assert_eq!(bytes_data_to_str(hello_bytes.unwrap()), "hello"); + + let world_bytes = origin_stream.next().await; + + assert!(world_bytes.is_some()); + + let world_bytes = world_bytes.unwrap(); + + assert!(world_bytes.is_ok()); + + assert_eq!(bytes_data_to_str(world_bytes.unwrap()), " world"); + + let end_bytes = origin_stream.next().await; + + assert!(end_bytes.is_some()); + + let end_bytes = end_bytes.unwrap(); + + assert!(end_bytes.is_ok()); + + assert_eq!(bytes_data_to_str(end_bytes.unwrap()), ", end test!"); + + let none_bytes = origin_stream.next().await; + + assert!(none_bytes.is_none()); + } + + #[tokio::test] + async fn test_cloned_bytes_stream_and_replay() { + let bytes1 = Ok(Bytes::from("hello".as_bytes())); + let bytes2 = Ok(Bytes::from(" world".as_bytes())); + let bytes3 = Ok(Bytes::from(", end test!".as_bytes())); + + let stream = futures_util::stream::iter(vec![bytes1, bytes2, bytes3]); + + let mut origin_stream = ClonedBytesStream::new(64 * 1024, Box::pin(stream)); + origin_stream.next().await; + origin_stream.next().await; + origin_stream.next().await; + + let none_bytes = origin_stream.next().await; + assert!(none_bytes.is_none()); + + let mut clone_origin_stream = origin_stream.clone(); + + drop(origin_stream); + + let cached_bytes = clone_origin_stream.next().await; + assert!(cached_bytes.is_some()); + + let cached_bytes = cached_bytes.unwrap(); + + assert!(cached_bytes.is_ok()); + + assert_eq!( + bytes_data_to_str(cached_bytes.unwrap()), + "hello world, end test!" + ); + + let none_bytes = clone_origin_stream.next().await; + assert!(none_bytes.is_none()); + } + + #[tokio::test] + async fn test_replay_stream_continue_poll_next() { + let bytes1 = Ok(Bytes::from("hello".as_bytes())); + let bytes2 = Ok(Bytes::from(" world".as_bytes())); + let bytes3 = Ok(Bytes::from(", end test!".as_bytes())); + + let stream = futures_util::stream::iter(vec![bytes1, bytes2, bytes3]); + + let mut origin_stream = ClonedBytesStream::new(1024 * 64, Box::pin(stream)); + origin_stream.next().await; + origin_stream.next().await; + + let mut clone_origin_stream = origin_stream.clone(); + + drop(origin_stream); + + let cached_bytes = clone_origin_stream.next().await; + assert!(cached_bytes.is_some()); + + let cached_bytes = cached_bytes.unwrap(); + + assert!(cached_bytes.is_ok()); + + assert_eq!(bytes_data_to_str(cached_bytes.unwrap()), "hello world"); + + let next_bytes = clone_origin_stream.next().await; + + assert!(next_bytes.is_some()); + + let next_bytes = next_bytes.unwrap(); + + assert!(next_bytes.is_ok()); + + assert_eq!(bytes_data_to_str(next_bytes.unwrap()), ", end test!"); + + let none_bytes = clone_origin_stream.next().await; + assert!(none_bytes.is_none()); + } + + #[tokio::test] + async fn test_cloned_bytes_stream_reached_max_capacity() { + let bytes1 = Ok(Bytes::from("hello".as_bytes())); + let bytes2 = Ok(Bytes::from(" world".as_bytes())); + let bytes3 = Ok(Bytes::from(", end test!".as_bytes())); + + let stream = futures_util::stream::iter(vec![bytes1, bytes2, bytes3]); + + let mut origin_stream = ClonedBytesStream::new(5, Box::pin(stream)); + let hello_bytes = origin_stream.next().await; + + assert!(hello_bytes.is_some()); + + let hello_bytes = hello_bytes.unwrap(); + + assert!(hello_bytes.is_ok()); + + assert_eq!(bytes_data_to_str(hello_bytes.unwrap()), "hello"); + + let world_bytes = origin_stream.next().await; + + assert!(world_bytes.is_some()); + + let world_bytes = world_bytes.unwrap(); + + assert!(world_bytes.is_ok()); + + assert_eq!(bytes_data_to_str(world_bytes.unwrap()), " world"); + + let mut cloned_origin_stream = origin_stream.clone(); + + drop(origin_stream); + + let end_bytes = cloned_origin_stream.next().await; + + assert!(end_bytes.is_some()); + + let end_bytes = end_bytes.unwrap(); + + assert!(end_bytes.is_ok()); + + assert_eq!(bytes_data_to_str(end_bytes.unwrap()), ", end test!"); + + let none_bytes = cloned_origin_stream.next().await; + assert!(none_bytes.is_none()); + } + + fn bytes_data_to_str(mut bytes_data: BytesData) -> String { + let len = bytes_data.remaining(); + let bytes = bytes_data.copy_to_bytes(len); + String::from_utf8(bytes.to_vec()).unwrap() + } +} diff --git a/dubbo/src/triple/client/triple.rs b/dubbo/src/triple/client/triple.rs index b81661cd..22273415 100644 --- a/dubbo/src/triple/client/triple.rs +++ b/dubbo/src/triple/client/triple.rs @@ -19,10 +19,10 @@ use std::str::FromStr; use futures_util::{future, stream, StreamExt, TryStreamExt}; -use aws_smithy_http::body::SdkBody; use http::HeaderValue; use super::builder::ClientBuilder; +use super::replay::ClonedBody; use crate::codegen::RpcInvocation; use crate::{ @@ -57,8 +57,9 @@ impl TripleClient { &self, uri: http::Uri, path: http::uri::PathAndQuery, - body: SdkBody, - ) -> http::Request { + body: ClonedBody, + invocation: RpcInvocation, + ) -> http::Request { let mut parts = uri.into_parts(); parts.path_and_query = Some(path); @@ -110,6 +111,8 @@ impl TripleClient { http::HeaderValue::from_static("gzip"), ); + req.extensions_mut().insert(invocation); + // const ( // TripleContentType = "application/grpc+proto" // TripleUserAgent = "grpc-go/1.35.0-dev" @@ -138,25 +141,23 @@ impl TripleClient { M2: Send + Sync + 'static, { let req = req.map(|m| stream::once(future::ready(m))); - let body_stream = encode( + let en = encode( codec.encoder(), req.into_inner().map(Ok), self.send_compression_encoding, ) .into_stream(); - let body = hyper::Body::wrap_stream(body_stream); - let bytes = hyper::body::to_bytes(body).await.unwrap(); - let sdk_body = SdkBody::from(bytes); + let body = ClonedBody::new(en); let mut conn = self .builder .clone() .unwrap() - .build(invocation.into()) + .build() .unwrap(); let http_uri = http::Uri::from_str(&conn.get_url().to_url()).unwrap(); - let req = self.map_request(http_uri.clone(), path, sdk_body); + let req = self.map_request(http_uri.clone(), path, body, invocation); let response = conn .call(req) @@ -210,18 +211,19 @@ impl TripleClient { self.send_compression_encoding, ) .into_stream(); - let body = hyper::Body::wrap_stream(en); - let sdk_body = SdkBody::from(body); + + + let body = ClonedBody::new(en); let mut conn = self .builder .clone() .unwrap() - .build(invocation.into()) + .build() .unwrap(); let http_uri = http::Uri::from_str(&conn.get_url().to_url()).unwrap(); - let req = self.map_request(http_uri.clone(), path, sdk_body); + let req = self.map_request(http_uri.clone(), path, body, invocation); let response = conn .call(req) @@ -259,18 +261,17 @@ impl TripleClient { self.send_compression_encoding, ) .into_stream(); - let body = hyper::Body::wrap_stream(en); - let sdk_body = SdkBody::from(body); + let body = ClonedBody::new(en); let mut conn = self .builder .clone() .unwrap() - .build(invocation.into()) + .build() .unwrap(); let http_uri = http::Uri::from_str(&conn.get_url().to_url()).unwrap(); - let req = self.map_request(http_uri.clone(), path, sdk_body); + let req = self.map_request(http_uri.clone(), path, body, invocation); // let mut conn = Connection::new().with_host(http_uri); let response = conn @@ -325,18 +326,18 @@ impl TripleClient { self.send_compression_encoding, ) .into_stream(); - let body = hyper::Body::wrap_stream(en); - let sdk_body = SdkBody::from(body); + + let body = ClonedBody::new(en); let mut conn = self .builder .clone() .unwrap() - .build(invocation.into()) + .build() .unwrap(); let http_uri = http::Uri::from_str(&conn.get_url().to_url()).unwrap(); - let req = self.map_request(http_uri.clone(), path, sdk_body); + let req = self.map_request(http_uri.clone(), path, body, invocation); let response = conn .call(req) diff --git a/examples/echo/src/generated/grpc.examples.echo.rs b/examples/echo/src/generated/grpc.examples.echo.rs index 16fb1638..e756d5c7 100644 --- a/examples/echo/src/generated/grpc.examples.echo.rs +++ b/examples/echo/src/generated/grpc.examples.echo.rs @@ -1,12 +1,12 @@ -// @generated by apache/dubbo-rust. - /// EchoRequest is the request for echo. +#[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct EchoRequest { #[prost(string, tag = "1")] pub message: ::prost::alloc::string::String, } /// EchoResponse is the response for echo. +#[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct EchoResponse { #[prost(string, tag = "1")] @@ -31,21 +31,21 @@ pub mod echo_client { inner: TripleClient::new(builder), } } - pub fn with_cluster(mut self, invoker: ClusterInvoker) -> Self { - self.inner = self.inner.with_cluster(invoker); - self - } /// UnaryEcho is unary echo. pub async fn unary_echo( &mut self, request: Request, ) -> Result, dubbo::status::Status> { - let codec = - dubbo::codegen::ProstCodec::::default(); + let codec = dubbo::codegen::ProstCodec::< + super::EchoRequest, + super::EchoResponse, + >::default(); let invocation = RpcInvocation::default() .with_service_unique_name(String::from("grpc.examples.echo.Echo")) .with_method_name(String::from("UnaryEcho")); - let path = http::uri::PathAndQuery::from_static("/grpc.examples.echo.Echo/UnaryEcho"); + let path = http::uri::PathAndQuery::from_static( + "/grpc.examples.echo.Echo/UnaryEcho", + ); self.inner.unary(request, codec, path, invocation).await } /// ServerStreamingEcho is server side streaming. @@ -53,51 +53,51 @@ pub mod echo_client { &mut self, request: Request, ) -> Result>, dubbo::status::Status> { - let codec = - dubbo::codegen::ProstCodec::::default(); + let codec = dubbo::codegen::ProstCodec::< + super::EchoRequest, + super::EchoResponse, + >::default(); let invocation = RpcInvocation::default() .with_service_unique_name(String::from("grpc.examples.echo.Echo")) .with_method_name(String::from("ServerStreamingEcho")); let path = http::uri::PathAndQuery::from_static( "/grpc.examples.echo.Echo/ServerStreamingEcho", ); - self.inner - .server_streaming(request, codec, path, invocation) - .await + self.inner.server_streaming(request, codec, path, invocation).await } /// ClientStreamingEcho is client side streaming. pub async fn client_streaming_echo( &mut self, request: impl IntoStreamingRequest, ) -> Result, dubbo::status::Status> { - let codec = - dubbo::codegen::ProstCodec::::default(); + let codec = dubbo::codegen::ProstCodec::< + super::EchoRequest, + super::EchoResponse, + >::default(); let invocation = RpcInvocation::default() .with_service_unique_name(String::from("grpc.examples.echo.Echo")) .with_method_name(String::from("ClientStreamingEcho")); let path = http::uri::PathAndQuery::from_static( "/grpc.examples.echo.Echo/ClientStreamingEcho", ); - self.inner - .client_streaming(request, codec, path, invocation) - .await + self.inner.client_streaming(request, codec, path, invocation).await } /// BidirectionalStreamingEcho is bidi streaming. pub async fn bidirectional_streaming_echo( &mut self, request: impl IntoStreamingRequest, ) -> Result>, dubbo::status::Status> { - let codec = - dubbo::codegen::ProstCodec::::default(); + let codec = dubbo::codegen::ProstCodec::< + super::EchoRequest, + super::EchoResponse, + >::default(); let invocation = RpcInvocation::default() .with_service_unique_name(String::from("grpc.examples.echo.Echo")) .with_method_name(String::from("BidirectionalStreamingEcho")); let path = http::uri::PathAndQuery::from_static( "/grpc.examples.echo.Echo/BidirectionalStreamingEcho", ); - self.inner - .bidi_streaming(request, codec, path, invocation) - .await + self.inner.bidi_streaming(request, codec, path, invocation).await } } } @@ -114,7 +114,9 @@ pub mod echo_server { request: Request, ) -> Result, dubbo::status::Status>; ///Server streaming response type for the ServerStreamingEcho method. - type ServerStreamingEchoStream: futures_util::Stream> + type ServerStreamingEchoStream: futures_util::Stream< + Item = Result, + > + Send + 'static; /// ServerStreamingEcho is server side streaming. @@ -128,14 +130,19 @@ pub mod echo_server { request: Request>, ) -> Result, dubbo::status::Status>; ///Server streaming response type for the BidirectionalStreamingEcho method. - type BidirectionalStreamingEchoStream: futures_util::Stream> + type BidirectionalStreamingEchoStream: futures_util::Stream< + Item = Result, + > + Send + 'static; /// BidirectionalStreamingEcho is bidi streaming. async fn bidirectional_streaming_echo( &self, request: Request>, - ) -> Result, dubbo::status::Status>; + ) -> Result< + Response, + dubbo::status::Status, + >; } /// Echo is the echo service. #[derive(Debug)] @@ -165,7 +172,10 @@ pub mod echo_server { type Response = http::Response; type Error = std::convert::Infallible; type Future = BoxFuture; - fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { + fn poll_ready( + &mut self, + _cx: &mut Context<'_>, + ) -> Poll> { Poll::Ready(Ok(())) } fn call(&mut self, req: http::Request) -> Self::Future { @@ -178,18 +188,26 @@ pub mod echo_server { } impl UnarySvc for UnaryEchoServer { type Response = super::EchoResponse; - type Future = BoxFuture, dubbo::status::Status>; - fn call(&mut self, request: Request) -> Self::Future { + type Future = BoxFuture< + Response, + dubbo::status::Status, + >; + fn call( + &mut self, + request: Request, + ) -> Self::Future { let inner = self.inner.0.clone(); let fut = async move { inner.unary_echo(request).await }; Box::pin(fut) } } let fut = async move { - let mut server = TripleServer::new(dubbo::codegen::ProstCodec::< - super::EchoResponse, - super::EchoRequest, - >::default()); + let mut server = TripleServer::new( + dubbo::codegen::ProstCodec::< + super::EchoResponse, + super::EchoRequest, + >::default(), + ); let res = server.unary(UnaryEchoServer { inner }, req).await; Ok(res) }; @@ -200,22 +218,32 @@ pub mod echo_server { struct ServerStreamingEchoServer { inner: _Inner, } - impl ServerStreamingSvc for ServerStreamingEchoServer { + impl ServerStreamingSvc + for ServerStreamingEchoServer { type Response = super::EchoResponse; type ResponseStream = T::ServerStreamingEchoStream; - type Future = - BoxFuture, dubbo::status::Status>; - fn call(&mut self, request: Request) -> Self::Future { + type Future = BoxFuture< + Response, + dubbo::status::Status, + >; + fn call( + &mut self, + request: Request, + ) -> Self::Future { let inner = self.inner.0.clone(); - let fut = async move { inner.server_streaming_echo(request).await }; + let fut = async move { + inner.server_streaming_echo(request).await + }; Box::pin(fut) } } let fut = async move { - let mut server = TripleServer::new(dubbo::codegen::ProstCodec::< - super::EchoResponse, - super::EchoRequest, - >::default()); + let mut server = TripleServer::new( + dubbo::codegen::ProstCodec::< + super::EchoResponse, + super::EchoRequest, + >::default(), + ); let res = server .server_streaming(ServerStreamingEchoServer { inner }, req) .await; @@ -228,23 +256,31 @@ pub mod echo_server { struct ClientStreamingEchoServer { inner: _Inner, } - impl ClientStreamingSvc for ClientStreamingEchoServer { + impl ClientStreamingSvc + for ClientStreamingEchoServer { type Response = super::EchoResponse; - type Future = BoxFuture, dubbo::status::Status>; + type Future = BoxFuture< + Response, + dubbo::status::Status, + >; fn call( &mut self, request: Request>, ) -> Self::Future { let inner = self.inner.0.clone(); - let fut = async move { inner.client_streaming_echo(request).await }; + let fut = async move { + inner.client_streaming_echo(request).await + }; Box::pin(fut) } } let fut = async move { - let mut server = TripleServer::new(dubbo::codegen::ProstCodec::< - super::EchoResponse, - super::EchoRequest, - >::default()); + let mut server = TripleServer::new( + dubbo::codegen::ProstCodec::< + super::EchoResponse, + super::EchoRequest, + >::default(), + ); let res = server .client_streaming(ClientStreamingEchoServer { inner }, req) .await; @@ -257,41 +293,56 @@ pub mod echo_server { struct BidirectionalStreamingEchoServer { inner: _Inner, } - impl StreamingSvc for BidirectionalStreamingEchoServer { + impl StreamingSvc + for BidirectionalStreamingEchoServer { type Response = super::EchoResponse; type ResponseStream = T::BidirectionalStreamingEchoStream; - type Future = - BoxFuture, dubbo::status::Status>; + type Future = BoxFuture< + Response, + dubbo::status::Status, + >; fn call( &mut self, request: Request>, ) -> Self::Future { let inner = self.inner.0.clone(); - let fut = - async move { inner.bidirectional_streaming_echo(request).await }; + let fut = async move { + inner.bidirectional_streaming_echo(request).await + }; Box::pin(fut) } } let fut = async move { - let mut server = TripleServer::new(dubbo::codegen::ProstCodec::< - super::EchoResponse, - super::EchoRequest, - >::default()); + let mut server = TripleServer::new( + dubbo::codegen::ProstCodec::< + super::EchoResponse, + super::EchoRequest, + >::default(), + ); let res = server - .bidi_streaming(BidirectionalStreamingEchoServer { inner }, req) + .bidi_streaming( + BidirectionalStreamingEchoServer { + inner, + }, + req, + ) .await; Ok(res) }; Box::pin(fut) } - _ => Box::pin(async move { - Ok(http::Response::builder() - .status(200) - .header("grpc-status", "12") - .header("content-type", "application/grpc") - .body(empty_body()) - .unwrap()) - }), + _ => { + Box::pin(async move { + Ok( + http::Response::builder() + .status(200) + .header("grpc-status", "12") + .header("content-type", "application/grpc") + .body(empty_body()) + .unwrap(), + ) + }) + } } } } From 4eafa334b524ec225e9a7a794aa7c5ba74d8e04b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=AF=9B=E6=96=87=E8=B6=85?= Date: Tue, 19 Sep 2023 21:00:46 +0800 Subject: [PATCH 33/48] Ftr: add ServiceNameDirectory (#157) * Ftr: failover cluster * Ftr: add ServiceNameDirectory --- dubbo/Cargo.toml | 3 +- dubbo/src/cluster/directory.rs | 263 ++++++++++++++++++++++++++++- dubbo/src/triple/client/builder.rs | 3 +- 3 files changed, 264 insertions(+), 5 deletions(-) diff --git a/dubbo/Cargo.toml b/dubbo/Cargo.toml index 97c48992..43be50b9 100644 --- a/dubbo/Cargo.toml +++ b/dubbo/Cargo.toml @@ -14,7 +14,7 @@ hyper = { version = "0.14.26", features = ["full"] } http = "0.2" tower-service.workspace = true http-body = "0.4.4" -tower = { workspace = true, features = ["timeout", "ready-cache"] } +tower = { workspace = true, features = ["timeout", "ready-cache","discover"] } futures-util = "0.3.23" futures-core ="0.3.23" argh = "0.1" @@ -46,7 +46,6 @@ dubbo-config = { path = "../config", version = "0.3.0" } #对象存储 state = { version = "0.5", features = ["tls"] } - thiserror = "1.0.48" regex = "1.9.1" nacos-sdk = { version = "0.3.0", features = ["default"] } diff --git a/dubbo/src/cluster/directory.rs b/dubbo/src/cluster/directory.rs index 06b840f0..547878bf 100644 --- a/dubbo/src/cluster/directory.rs +++ b/dubbo/src/cluster/directory.rs @@ -18,17 +18,26 @@ use std::{ collections::HashMap, fmt::Debug, + pin::Pin, str::FromStr, sync::{Arc, RwLock}, + task::{Context, Poll}, }; use crate::{ codegen::TripleInvoker, protocol::BoxInvoker, registry::{memory_registry::MemoryNotifyListener, BoxRegistry}, + triple::client::replay::ClonedBody, + StdError, }; use dubbo_base::Url; use dubbo_logger::tracing; +use futures_core::ready; +use tower::{ + discover::{Change, Discover}, + ready_cache::ReadyCache, +}; use crate::cluster::Directory; @@ -89,7 +98,6 @@ impl RegistryDirectory { impl Directory for RegistryDirectory { fn list(&self, service_name: String) -> Vec { - let url = Url::from_url(&format!( "triple://{}:{}/{}", "127.0.0.1", "8888", service_name @@ -116,6 +124,259 @@ impl Directory for RegistryDirectory { for item in url_vec.iter() { invokers.push(Box::new(TripleInvoker::new(item.clone()))); } + invokers } } + +pub struct ServiceNameDirectory { + cache: ReadyCache>, + discover: D, +} + +impl ServiceNameDirectory +where + D: Discover + Unpin, + D::Error: Into, +{ + pub fn new(discover: D) -> Self { + Self { + cache: ReadyCache::default(), + discover, + } + } + + fn update_cache(&mut self, cx: &mut Context<'_>) -> Poll> { + loop { + let discover = Pin::new(&mut self.discover); + let update = ready!(discover.poll_discover(cx)); + match update { + Some(update) => { + let change = match update { + Err(_) => continue, + Ok(change) => change, + }; + + match change { + Change::Insert(key, invoker) => { + self.cache.push(key, invoker); + } + Change::Remove(key) => { + self.cache.evict(&key); + } + } + } + None => break, + } + } + + // poll pending + let _ = ready!(self.cache.poll_pending(cx))?; + + Poll::Ready(Ok(())) + } + + pub fn list(&mut self, cx: &mut Context<'_>) -> Poll, StdError>> { + let _ = self.update_cache(cx)?; + + let ready_len = self.cache.ready_len(); + + if ready_len == 0 { + return Poll::Pending; + } + + let mut invoker_list = Vec::with_capacity(ready_len); + + for idx in 0..ready_len { + let check = self.cache.check_ready_index(cx, idx); + let is_ready = match check { + Ok(is_ready) => is_ready, + Err(_) => false, + }; + if !is_ready { + continue; + } + + let invoker_url = match self.cache.get_ready_index(idx) { + None => continue, + Some((k, _)) => k.clone(), + }; + + invoker_list.push(invoker_url); + } + + Poll::Ready(Ok(invoker_list)) + } +} + +#[cfg(test)] +pub mod tests { + use std::{ + convert::Infallible, + pin::Pin, + task::{Context, Poll}, + }; + + use crate::{boxed, cluster::directory::ServiceNameDirectory}; + use bytes::Buf; + use futures_core::Stream; + use futures_util::future::poll_fn; + use http::StatusCode; + use http_body::Body; + use tower::{discover::Change, Service}; + + use dubbo_base::Url; + + use crate::{codegen::Invoker, protocol::BoxInvoker, triple::client::replay::ClonedBody}; + + pub struct MockStaticServiceList + where + T: IntoIterator, + { + iter: T::IntoIter, + } + + impl MockStaticServiceList + where + T: IntoIterator, + { + pub fn new(list: T) -> Self { + let iter = list.into_iter(); + + Self { iter } + } + } + + impl Stream for MockStaticServiceList + where + T: IntoIterator, + T::IntoIter: Unpin, + { + type Item = Result, Infallible>; + + fn poll_next(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { + let mut_self_ref = self.get_mut(); + let mut_iter_ref = &mut mut_self_ref.iter; + + match mut_iter_ref.next() { + Some(next) => { + let invoker_url = next.get_url(); + let raw_url_string = invoker_url.raw_url_string(); + return Poll::Ready(Some(Ok(Change::Insert(raw_url_string, next)))); + } + None => Poll::Ready(None), + } + } + } + + #[derive(Debug)] + struct MockInvoker(u8, bool); + + impl Invoker> for MockInvoker { + fn get_url(&self) -> dubbo_base::Url { + let str = format!( + "triple://127.0.0.1:8888/failover_cluster_service/directory/{}", + self.0 + ); + Url::from_url(str.as_str()).unwrap() + } + } + + impl Service> for MockInvoker { + type Response = http::Response; + + type Error = crate::Error; + + type Future = crate::BoxFuture, crate::Error>; + + fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { + if !self.1 { + return Poll::Pending; + } + + Poll::Ready(Ok(())) + } + + fn call(&mut self, req: http::Request) -> Self::Future { + Box::pin(async move { + let mut body = req.into_body(); + + let mut body_data = poll_fn(|cx| Pin::new(&mut body).poll_data(cx)) + .await + .unwrap() + .unwrap(); + + let len = body_data.remaining(); + let bytes = body_data.copy_to_bytes(len); + + let req_str = String::from_utf8(bytes.to_vec()).unwrap(); + println!("req: {}", req_str); + let echo_data = format!("echo: {}", req_str); + let response = http::Response::builder() + .status(StatusCode::OK) + .body(boxed(echo_data)) + .unwrap(); + + return Ok(response); + }) + } + } + + #[tokio::test] + async fn test_directory() { + let invoker_list = vec![ + Box::new(MockInvoker(1, true)) as BoxInvoker, + Box::new(MockInvoker(2, true)) as BoxInvoker, + Box::new(MockInvoker(3, true)) as BoxInvoker, + Box::new(MockInvoker(4, true)) as BoxInvoker, + Box::new(MockInvoker(5, true)) as BoxInvoker, + Box::new(MockInvoker(6, true)) as BoxInvoker, + ]; + let service_list = MockStaticServiceList::new(invoker_list); + + let mut service_name_directory = ServiceNameDirectory::new(service_list); + + let list = poll_fn(|cx| service_name_directory.list(cx)).await; + + assert!(list.is_ok()); + + let list = list.unwrap(); + + assert!(!list.is_empty()); + + assert_eq!(6, list.len()); + + for uri in list { + println!("invoker uri: {}", uri); + } + } + + #[tokio::test] + async fn test_ready_in_any_order() { + let invoker_list = vec![ + Box::new(MockInvoker(1, true)) as BoxInvoker, + Box::new(MockInvoker(2, false)) as BoxInvoker, + Box::new(MockInvoker(3, false)) as BoxInvoker, + Box::new(MockInvoker(4, true)) as BoxInvoker, + Box::new(MockInvoker(5, false)) as BoxInvoker, + Box::new(MockInvoker(6, true)) as BoxInvoker, + ]; + let service_list = MockStaticServiceList::new(invoker_list); + + let mut service_name_directory = ServiceNameDirectory::new(service_list); + + let list = poll_fn(|cx| service_name_directory.list(cx)).await; + + assert!(list.is_ok()); + + let list = list.unwrap(); + + assert!(!list.is_empty()); + + assert_eq!(3, list.len()); + + for uri in list { + println!("invoker uri: {}", uri); + } + } +} diff --git a/dubbo/src/triple/client/builder.rs b/dubbo/src/triple/client/builder.rs index 5b2702fb..32b26a55 100644 --- a/dubbo/src/triple/client/builder.rs +++ b/dubbo/src/triple/client/builder.rs @@ -23,11 +23,10 @@ use crate::{ protocol::BoxInvoker, utils::boxed_clone::BoxCloneService, }; - use dubbo_base::Url; - use super::replay::ClonedBody; + pub type ClientBoxService = BoxCloneService, http::Response, crate::Error>; From a957c54d1d58a02a4e60b95b1f49f42e8cb4e992 Mon Sep 17 00:00:00 2001 From: Urara <95117705+AdachiAndShimamura@users.noreply.github.com> Date: Sat, 7 Oct 2023 23:20:15 +0800 Subject: [PATCH 34/48] Feat/cluster Optimized the Router module (#160) * perf: Optimized the logic of the routing module. Refactored route logic decision-making, eliminating unnecessary cloning and improving performance. * perf: Optimized the logic of the routing module. Refactored route logic decision-making, eliminating unnecessary cloning and improving performance. * perf: Removed unnecessary configurations. * perf: Removed unnecessary configurations. * perf: Optimized the Router module Optimized the Router module Added Router Chain to MockDirectory --- application.yaml | 32 +--- config/src/router.rs | 6 +- dubbo/src/cluster/directory.rs | 9 +- dubbo/src/cluster/mod.rs | 67 +++---- .../router/condition/condition_router.rs | 13 +- dubbo/src/cluster/router/condition/matcher.rs | 80 ++++---- .../cluster/router/condition/single_router.rs | 34 ++-- .../router/manager/condition_manager.rs | 73 ++++--- .../cluster/router/manager/router_manager.rs | 137 +++++++------- .../src/cluster/router/manager/tag_manager.rs | 21 +- dubbo/src/cluster/router/mod.rs | 8 +- .../nacos_config_center/nacos_client.rs | 138 +++++++------- dubbo/src/cluster/router/router_chain.rs | 72 +++---- dubbo/src/cluster/router/tag/tag_router.rs | 32 +++- dubbo/src/invocation.rs | 4 +- dubbo/src/protocol/mod.rs | 3 +- dubbo/src/protocol/triple/triple_invoker.rs | 5 +- dubbo/src/triple/client/builder.rs | 6 +- dubbo/src/triple/client/mod.rs | 2 +- dubbo/src/triple/client/replay.rs | 2 +- dubbo/src/triple/client/triple.rs | 14 +- .../echo/src/generated/grpc.examples.echo.rs | 179 ++++++------------ 22 files changed, 396 insertions(+), 541 deletions(-) diff --git a/application.yaml b/application.yaml index 902b0efd..bec29a67 100644 --- a/application.yaml +++ b/application.yaml @@ -25,33 +25,5 @@ dubbo: routers: consumer: - service: "org.apache.dubbo.sample.tri.Greeter" - url: triple://localhost:20000 - protocol: triple - nacos: - addr: "127.0.0.1:8848" - namespace: "" - app: "" - conditions: - - scope: "service" - force: false - runtime: true - enabled: true - key: "org.apache.dubbo.sample.tri.Greeter" - conditions: - - method=greet => port=8889 - - scope: "service" - force: true - runtime: true - enabled: true - key: "user.UserService" - conditions: - - method=get_s => port=2003 - tags: - force: true - enabled: true - key: shop-detail - tags: - - name: gray - matches: - - key: env - value: gray \ No newline at end of file + url: tri://127.0.0.1:20000 + protocol: triple \ No newline at end of file diff --git a/config/src/router.rs b/config/src/router.rs index 98aa14a6..b45bd478 100644 --- a/config/src/router.rs +++ b/config/src/router.rs @@ -2,9 +2,10 @@ use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Default)] pub struct ConditionRouterConfig { + #[serde(rename = "configVersion")] + pub config_version: String, pub scope: String, pub force: bool, - pub runtime: bool, pub enabled: bool, pub key: String, pub conditions: Vec, @@ -12,6 +13,8 @@ pub struct ConditionRouterConfig { #[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq)] pub struct TagRouterConfig { + #[serde(rename = "configVersion")] + pub config_version: String, pub force: bool, pub enabled: bool, pub key: String, @@ -28,6 +31,7 @@ pub struct ConsumerConfig { #[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq)] pub struct Tag { pub name: String, + #[serde(rename = "match")] pub matches: Vec, } diff --git a/dubbo/src/cluster/directory.rs b/dubbo/src/cluster/directory.rs index 547878bf..66a3c43a 100644 --- a/dubbo/src/cluster/directory.rs +++ b/dubbo/src/cluster/directory.rs @@ -39,7 +39,7 @@ use tower::{ ready_cache::ReadyCache, }; -use crate::cluster::Directory; +use crate::{cluster::Directory, codegen::RpcInvocation, invocation::Invocation}; /// Directory. /// @@ -68,12 +68,12 @@ impl StaticDirectory { } impl Directory for StaticDirectory { - fn list(&self, service_name: String) -> Vec { + fn list(&self, inv: Arc) -> Vec { let url = Url::from_url(&format!( "tri://{}:{}/{}", self.uri.host().unwrap(), self.uri.port().unwrap(), - service_name, + inv.get_target_service_unique_name(), )) .unwrap(); let invoker = Box::new(TripleInvoker::new(url)); @@ -97,7 +97,8 @@ impl RegistryDirectory { } impl Directory for RegistryDirectory { - fn list(&self, service_name: String) -> Vec { + fn list(&self, inv: Arc) -> Vec { + let service_name = inv.get_target_service_unique_name(); let url = Url::from_url(&format!( "triple://{}:{}/{}", "127.0.0.1", "8888", service_name diff --git a/dubbo/src/cluster/mod.rs b/dubbo/src/cluster/mod.rs index 7411a675..964150d5 100644 --- a/dubbo/src/cluster/mod.rs +++ b/dubbo/src/cluster/mod.rs @@ -23,6 +23,9 @@ use tower::{ready_cache::ReadyCache, ServiceExt}; use tower_service::Service; use crate::{ + cluster::router::{ + manager::router_manager::get_global_router_manager, router_chain::RouterChain, + }, codegen::RpcInvocation, invocation::Invocation, protocol::{triple::triple_invoker::TripleInvoker, BoxInvoker, Invoker}, @@ -35,7 +38,7 @@ pub mod loadbalance; pub mod router; pub trait Directory: Debug { - fn list(&self, service_name: String) -> Vec; + fn list(&self, inv: Arc) -> Vec; } type BoxDirectory = Box; @@ -134,7 +137,7 @@ impl Service> for FailoverCluster { let inv = inv.unwrap(); let service_name = inv.get_target_service_unique_name(); - let invokers = self.dir.list(service_name.clone()); + let invokers = self.dir.list(Arc::new(inv.clone())); Box::pin(async move { let mut current_req = req; @@ -172,55 +175,36 @@ impl Invoker> for FailoverCluster { #[derive(Debug, Default)] pub struct MockDirectory { - // router_chain: RouterChain, + router_chain: RouterChain, } impl MockDirectory { - pub fn new() -> MockDirectory { - // let router_chain = get_global_router_manager().read().unwrap().get_router_chain(invocation); - Self { - // router_chain - } + pub fn new(service_name: String) -> MockDirectory { + let router_chain = get_global_router_manager() + .read() + .unwrap() + .get_router_chain(service_name); + Self { router_chain } } } impl Directory for MockDirectory { - fn list(&self, service_name: String) -> Vec { - // tracing::info!("MockDirectory: {}", meta); + fn list(&self, inv: Arc) -> Vec { let u = Url::from_url("triple://127.0.0.1:8888/helloworld.Greeter").unwrap(); - vec![Box::new(TripleInvoker::new(u))] - // self.router_chain.route(u, invo); + let mut urls = vec![u]; + // tracing::info!("MockDirectory: {}", meta); + urls = self.router_chain.route(urls, inv); + let mut result = Vec::new(); + for url in urls { + result.push(Box::new(TripleInvoker::new(url)) as BoxInvoker); + } + result } } - -// #[derive(Debug, Default)] -// pub struct RouterChain { -// router: HashMap, -// invokers: Arc>, -// } - -// impl RouterChain { -// pub fn route(&mut self, url: Url, invo: Arc) -> Arc> { -// let r = self.router.get("mock").unwrap(); -// r.route(self.invokers.clone(), url, invo) -// } -// } - -// pub trait Router: Debug { -// fn route( -// &self, -// invokers: Arc>, -// url: Url, -// invo: Arc, -// ) -> Arc>; -// } - -// pub type BoxRouter = Box; - #[cfg(test)] pub mod tests { - use std::task::Poll; + use std::{sync::Arc, task::Poll}; use bytes::{Buf, BufMut, BytesMut}; use dubbo_base::Url; @@ -250,8 +234,11 @@ pub mod tests { struct MockDirectory; impl Directory for MockDirectory { - fn list(&self, service_name: String) -> Vec { - println!("get invoker list for {}", service_name); + fn list(&self, inv: Arc) -> Vec { + println!( + "get invoker list for {}", + inv.get_target_service_unique_name() + ); vec![ Box::new(MockInvoker(1)), diff --git a/dubbo/src/cluster/router/condition/condition_router.rs b/dubbo/src/cluster/router/condition/condition_router.rs index f39cd1c7..73aca005 100644 --- a/dubbo/src/cluster/router/condition/condition_router.rs +++ b/dubbo/src/cluster/router/condition/condition_router.rs @@ -17,19 +17,18 @@ pub struct ConditionRouter { } impl Router for ConditionRouter { - fn route(&self, invokers: Vec, url: Url, invo: Arc) -> Vec { - let mut invokers_result = invokers.clone(); - if let Some(routers) = self.application_routers.clone() { + fn route(&self, mut invokers: Vec, url: Url, invo: Arc) -> Vec { + if let Some(routers) = &self.application_routers { for router in &routers.read().unwrap().routers { - invokers_result = router.route(invokers_result, url.clone(), invo.clone()) + invokers = router.route(invokers, url.clone(), invo.clone()); } } - if let Some(routers) = self.service_routers.clone() { + if let Some(routers) = &self.service_routers { for router in &routers.read().unwrap().routers { - invokers_result = router.route(invokers_result, url.clone(), invo.clone()) + invokers = router.route(invokers, url.clone(), invo.clone()); } } - invokers_result + invokers } } diff --git a/dubbo/src/cluster/router/condition/matcher.rs b/dubbo/src/cluster/router/condition/matcher.rs index 8c9177e8..92bbe2da 100644 --- a/dubbo/src/cluster/router/condition/matcher.rs +++ b/dubbo/src/cluster/router/condition/matcher.rs @@ -1,6 +1,5 @@ -use crate::codegen::RpcInvocation; use regex::Regex; -use std::{collections::HashSet, error::Error, option::Option, sync::Arc}; +use std::{collections::HashSet, error::Error, option::Option}; #[derive(Clone, Debug, Default)] pub struct ConditionMatcher { @@ -18,50 +17,25 @@ impl ConditionMatcher { } } - pub fn is_match( - &self, - value: Option, - invocation: Arc, - _is_when: bool, - ) -> Result> { + pub fn is_match(&self, value: Option) -> Result> { match value { - None => { - // if key does not present in whichever of url, invocation or attachment based on the matcher type, then return false. - Ok(false) - } + None => Ok(false), Some(val) => { - if !self.matches.is_empty() && self.mismatches.is_empty() { - for match_ in self.matches.iter() { - if self.do_pattern_match(match_, &val, invocation.clone())? { - return Ok(true); - } - } - Ok(false) - } else if !self.mismatches.is_empty() && self.matches.is_empty() { - for mismatch in self.mismatches.iter() { - if self.do_pattern_match(mismatch, &val, invocation.clone())? { - return Ok(false); - } + for match_ in self.matches.iter() { + if self.do_pattern_match(match_, &val) { + return Ok(true); } - Ok(true) - } else if !self.matches.is_empty() && !self.mismatches.is_empty() { - for mismatch in self.mismatches.iter() { - if self.do_pattern_match(mismatch, &val, invocation.clone())? { - return Ok(false); - } - } - for match_ in self.matches.iter() { - if self.do_pattern_match(match_, &val, invocation.clone())? { - return Ok(true); - } + } + for mismatch in self.mismatches.iter() { + if !self.do_pattern_match(mismatch, &val) { + return Ok(true); } - Ok(false) - } else { - Ok(false) } + Ok(false) } } } + pub fn get_matches(&mut self) -> &mut HashSet { &mut self.matches } @@ -69,16 +43,26 @@ impl ConditionMatcher { &mut self.mismatches } - fn do_pattern_match( - &self, - pattern: &String, - value: &String, - _invocation: Arc, - ) -> Result> { - if pattern.contains("*") { - return Ok(star_matcher(pattern, value)); + fn do_pattern_match(&self, pattern: &str, value: &str) -> bool { + if pattern.contains('*') { + return star_matcher(pattern, value); + } + + if pattern.contains('~') { + let parts: Vec<&str> = pattern.split('~').collect(); + + if parts.len() == 2 { + if let (Ok(left), Ok(right), Ok(val)) = ( + parts[0].parse::(), + parts[1].parse::(), + value.parse::(), + ) { + return range_matcher(val, left, right); + } + } + return false; } - Ok(pattern.eq(value)) + pattern == value } } @@ -89,6 +73,6 @@ pub fn star_matcher(pattern: &str, input: &str) -> bool { regex.is_match(input) } -pub fn _range_matcher(val: i32, min: i32, max: i32) -> bool { +pub fn range_matcher(val: i32, min: i32, max: i32) -> bool { min <= val && val <= max } diff --git a/dubbo/src/cluster/router/condition/single_router.rs b/dubbo/src/cluster/router/condition/single_router.rs index e290b150..5f06aa8f 100644 --- a/dubbo/src/cluster/router/condition/single_router.rs +++ b/dubbo/src/cluster/router/condition/single_router.rs @@ -169,20 +169,16 @@ impl ConditionSingleRouter { pub fn match_when(&self, url: Url, invocation: Arc) -> bool { if self.when_condition.is_empty() { - true - } else { - false - }; - self.do_match(url, &self.when_condition, invocation, true) + return true; + } + self.do_match(url, &self.when_condition, invocation) } pub fn match_then(&self, url: Url, invocation: Arc) -> bool { - if self.when_condition.is_empty() { - true - } else { - false - }; - self.do_match(url, &self.then_condition, invocation, false) + if self.then_condition.is_empty() { + return false; + } + self.do_match(url, &self.then_condition, invocation) } pub fn do_match( @@ -190,25 +186,19 @@ impl ConditionSingleRouter { url: Url, conditions: &HashMap>>, invocation: Arc, - is_when: bool, ) -> bool { let sample: HashMap = to_original_map(url); - for (key, condition_matcher) in conditions { + conditions.iter().all(|(key, condition_matcher)| { let matcher = condition_matcher.read().unwrap(); let value = get_value(key, &sample, invocation.clone()); - match matcher.is_match(value, invocation.clone(), is_when) { - Ok(result) => { - if !result { - return false; - } - } + match matcher.is_match(value) { + Ok(result) => result, Err(error) => { info!("Error occurred: {:?}", error); - return false; + false } } - } - true + }) } } diff --git a/dubbo/src/cluster/router/manager/condition_manager.rs b/dubbo/src/cluster/router/manager/condition_manager.rs index 4207b79b..7ad5e1b6 100644 --- a/dubbo/src/cluster/router/manager/condition_manager.rs +++ b/dubbo/src/cluster/router/manager/condition_manager.rs @@ -16,28 +16,30 @@ pub struct ConditionRouterManager { } impl ConditionRouterManager { - pub fn get_router(&self, service_name: String) -> Option { - let routers_service = self.routers_service.get(&service_name); - match routers_service { - Some(routers_service) => { - if self.routers_application.read().unwrap().is_null() { - return Some(ConditionRouter::new(Some(routers_service.clone()), None)); - } - Some(ConditionRouter::new( + pub fn get_router(&self, service_name: &String) -> Option { + let routers_application_is_null = self.routers_application.read().unwrap().is_null(); + self.routers_service + .get(service_name) + .map(|routers_service| { + ConditionRouter::new( Some(routers_service.clone()), - Some(self.routers_application.clone()), - )) - } - None => { - if self.routers_application.read().unwrap().is_null() { - return None; + if routers_application_is_null { + None + } else { + Some(self.routers_application.clone()) + }, + ) + }) + .or_else(|| { + if routers_application_is_null { + None + } else { + Some(ConditionRouter::new( + None, + Some(self.routers_application.clone()), + )) } - Some(ConditionRouter::new( - None, - Some(self.routers_application.clone()), - )) - } - } + }) } pub fn update(&mut self, config: ConditionRouterConfig) { @@ -45,31 +47,26 @@ impl ConditionRouterManager { let scope = config.scope; let key = config.key; let enable = config.enabled; - let mut routers = Vec::new(); - for condition in config.conditions { - routers.push(ConditionSingleRouter::new(condition, force, enable)); - } + + let routers = config + .conditions + .into_iter() + .map(|condition| ConditionSingleRouter::new(condition, force, enable)) + .collect::>(); + match scope.as_str() { "application" => { self.routers_application.write().unwrap().routers = routers; } "service" => { - if let Some(x) = self.routers_service.get(&key) { - x.write().unwrap().routers = routers - } else { - self.routers_service.insert( - key, - Arc::new(RwLock::new(ConditionSingleRouters::new(routers))), - ); - } + self.routers_service + .entry(key) + .or_insert_with(|| Arc::new(RwLock::new(ConditionSingleRouters::new(vec![])))) + .write() + .unwrap() + .routers = routers; } _ => {} } } - - pub fn _parse_rules(&mut self, configs: Vec) { - for config in configs { - self.update(config) - } - } } diff --git a/dubbo/src/cluster/router/manager/router_manager.rs b/dubbo/src/cluster/router/manager/router_manager.rs index a2a66586..e963181e 100644 --- a/dubbo/src/cluster/router/manager/router_manager.rs +++ b/dubbo/src/cluster/router/manager/router_manager.rs @@ -1,10 +1,7 @@ -use crate::{ - cluster::router::{ - manager::{condition_manager::ConditionRouterManager, tag_manager::TagRouterManager}, - nacos_config_center::nacos_client::NacosClient, - router_chain::RouterChain, - }, - invocation::{Invocation, RpcInvocation}, +use crate::cluster::router::{ + manager::{condition_manager::ConditionRouterManager, tag_manager::TagRouterManager}, + nacos_config_center::nacos_client::NacosClient, + router_chain::RouterChain, }; use dubbo_base::Url; use dubbo_config::{ @@ -19,7 +16,8 @@ use std::{ }; pub static GLOBAL_ROUTER_MANAGER: OnceCell>> = OnceCell::new(); - +const TAG: &str = "tag"; +const CONDITION: &str = "condition"; pub struct RouterManager { pub condition_router_manager: ConditionRouterManager, pub tag_router_manager: TagRouterManager, @@ -28,30 +26,28 @@ pub struct RouterManager { } impl RouterManager { - pub fn get_router_chain(&self, invocation: Arc) -> RouterChain { - let service = invocation.get_target_service_unique_name().clone(); - let condition_router = self.condition_router_manager.get_router(service.clone()); - let tag_router = self.tag_router_manager.get_router(); + pub fn get_router_chain(&self, service: String) -> RouterChain { let mut chain = RouterChain::new(); - match self.consumer.get(service.as_str()) { - None => {} - Some(url) => { - chain.set_condition_router(condition_router); - chain.set_tag_router(tag_router); - chain.self_url = url.clone(); + if let Some(url) = self.consumer.get(service.as_str()) { + if let Some(tag_router) = self.tag_router_manager.get_router(&service) { + chain.add_router(TAG.to_string(), Box::new(tag_router)); + } + if let Some(condition_router) = self.condition_router_manager.get_router(&service) { + chain.add_router(CONDITION.to_string(), Box::new(condition_router)); } + chain.self_url = url.clone(); } chain } pub fn notify(&mut self, event: RouterConfigChangeEvent) { match event.router_kind.as_str() { - "condition" => { + CONDITION => { let config: ConditionRouterConfig = serde_yaml::from_str(event.content.as_str()).unwrap(); self.condition_router_manager.update(config) } - "tag" => { + TAG => { let config: TagRouterConfig = serde_yaml::from_str(event.content.as_str()).unwrap(); self.tag_router_manager.update(config) } @@ -66,76 +62,71 @@ impl RouterManager { self.init_router_managers_for_nacos(); } - pub fn init_router_managers_for_nacos(&mut self) { - let config = self + fn init_router_managers_for_nacos(&mut self) { + if let Some(tag_config) = self .nacos .as_ref() - .unwrap() - .get_tag_config("application".to_string()); - match config { - None => {} - Some(tag_config) => { - self.tag_router_manager.init(); - self.tag_router_manager.update(tag_config) - } + .and_then(|n| n.get_config("application", TAG, TAG)) + { + self.tag_router_manager.update(tag_config); } + + if let Some(condition_app_config) = self + .nacos + .as_ref() + .and_then(|n| n.get_config("application", CONDITION, TAG)) + { + self.condition_router_manager.update(condition_app_config); + } + for (service_name, _) in &self.consumer { - let config = self + if let Some(condition_config) = self .nacos .as_ref() - .unwrap() - .get_condition_config(service_name.clone()); - match config { - None => {} - Some(condition_config) => self.condition_router_manager.update(condition_config), + .and_then(|n| n.get_config(service_name, CONDITION, CONDITION)) + { + self.condition_router_manager.update(condition_config); } } } pub fn init(&mut self) { let config = get_global_config().routers.clone(); + self.init_consumer_configs(); + if let Some(nacos_config) = &config.nacos { + self.init_nacos(nacos_config.clone()); + } else { + trace!("Nacos not configured, using local YAML configuration for routing"); + if let Some(condition_configs) = &config.conditions { + for condition_config in condition_configs { + self.condition_router_manager + .update(condition_config.clone()); + } + } else { + info!("Unconfigured Condition Router") + } + if let Some(tag_config) = &config.tags { + self.tag_router_manager.update(tag_config.clone()); + } else { + info!("Unconfigured Tag Router") + } + } + } + + fn init_consumer_configs(&mut self) { let consumer_configs = get_global_config() .routers .consumer .clone() - .unwrap_or(Vec::new()); + .unwrap_or_else(Vec::new); + for consumer_config in consumer_configs { - self.consumer.insert( - consumer_config.service.clone(), - Url::from_url( - format!("{}/{}", consumer_config.url, consumer_config.service).as_str(), - ) - .expect("consumer配置出错!Url生成错误"), - ); - } - match &config.nacos { - None => { - trace!("Nacos not configured, using local YAML configuration for routing"); - let condition = config.conditions.clone(); - match condition { - None => { - info!("Unconfigured Condition Router") - } - Some(cons) => { - for con in cons { - self.condition_router_manager.update(con) - } - } - } - let tag = config.tags.clone(); - match tag { - None => { - info!("Unconfigured Tag Router") - } - Some(ta) => { - self.tag_router_manager.init(); - self.tag_router_manager.update(ta) - } - } - } - Some(config) => { - self.init_nacos(config.clone()); - } + let service_url = Url::from_url( + format!("{}/{}", consumer_config.url, consumer_config.service).as_str(), + ) + .expect("Consumer config error"); + + self.consumer.insert(consumer_config.service, service_url); } } } diff --git a/dubbo/src/cluster/router/manager/tag_manager.rs b/dubbo/src/cluster/router/manager/tag_manager.rs index ce30c92e..8dc24999 100644 --- a/dubbo/src/cluster/router/manager/tag_manager.rs +++ b/dubbo/src/cluster/router/manager/tag_manager.rs @@ -1,27 +1,20 @@ -use crate::cluster::router::tag::tag_router::TagRouter; +use crate::cluster::router::tag::tag_router::{TagRouter, TagRouterInner}; use dubbo_config::router::TagRouterConfig; use std::sync::{Arc, RwLock}; #[derive(Debug, Clone, Default)] pub struct TagRouterManager { - pub tag_router: Option>>, + pub tag_router: Arc>, } impl TagRouterManager { - pub fn init(&mut self) { - self.tag_router = Some(Arc::new(RwLock::new(TagRouter::default()))) - } - - pub fn get_router(&self) -> Option>> { - self.tag_router.clone() + pub fn get_router(&self, _service_name: &String) -> Option { + Some(TagRouter { + inner: self.tag_router.clone(), + }) } pub fn update(&mut self, config: TagRouterConfig) { - self.tag_router - .as_ref() - .unwrap() - .write() - .unwrap() - .parse_config(config) + self.tag_router.write().unwrap().parse_config(config); } } diff --git a/dubbo/src/cluster/router/mod.rs b/dubbo/src/cluster/router/mod.rs index 84ceae15..17c9aec2 100644 --- a/dubbo/src/cluster/router/mod.rs +++ b/dubbo/src/cluster/router/mod.rs @@ -9,17 +9,17 @@ use crate::invocation::RpcInvocation; use dubbo_base::Url; use std::{fmt::Debug, sync::Arc}; -pub trait Router: Debug + Clone { - fn route(&self, invokers: Vec, url: Url, invo: Arc) -> Vec; +pub trait Router: Debug { + fn route(&self, invokers: Vec, url: Url, invocation: Arc) -> Vec; } -// pub type BoxRouter = Box; +pub type BoxRouter = Box; #[derive(Debug, Default, Clone)] pub struct MockRouter {} impl Router for MockRouter { - fn route(&self, invokers: Vec, _url: Url, _invo: Arc) -> Vec { + fn route(&self, invokers: Vec, _url: Url, _invocation: Arc) -> Vec { invokers } } diff --git a/dubbo/src/cluster/router/nacos_config_center/nacos_client.rs b/dubbo/src/cluster/router/nacos_config_center/nacos_client.rs index 1600e9b0..ce72641a 100644 --- a/dubbo/src/cluster/router/nacos_config_center/nacos_client.rs +++ b/dubbo/src/cluster/router/nacos_config_center/nacos_client.rs @@ -1,7 +1,7 @@ use crate::cluster::router::manager::router_manager::{ get_global_router_manager, RouterConfigChangeEvent, }; -use dubbo_config::router::{ConditionRouterConfig, NacosConfig, TagRouterConfig}; +use dubbo_config::router::NacosConfig; use dubbo_logger::{tracing, tracing::info}; use nacos_sdk::api::{ config::{ConfigChangeListener, ConfigResponse, ConfigService, ConfigServiceBuilder}, @@ -24,94 +24,86 @@ impl NacosClient { let server_addr = config.addr; let namespace = config.namespace; let app = config.app; - match config.enable_auth { - None => { - info!("disable nacos auth!"); - info!("nacos init,addr:{}", server_addr); - let client = Arc::new(RwLock::new( - ConfigServiceBuilder::new( - ClientProps::new() - .server_addr(server_addr) - .namespace(namespace) - .app_name(app), - ) - .build() - .expect("NacosClient build failed!Please check NacosConfig"), - )); - Self { client } - } - Some(auth) => { - info!("enable nacos auth!"); - info!("nacos init,addr:{}", server_addr); - let client = Arc::new(RwLock::new( - ConfigServiceBuilder::new( - ClientProps::new() - .server_addr(server_addr) - .namespace(namespace) - .app_name(app) - .auth_username(auth.auth_username) - .auth_password(auth.auth_password), - ) - // .enable_auth_plugin_http() - .build() - .expect("NacosClient build failed!Please check NacosConfig"), - )); - return Self { client }; - } + let enable_auth = config.enable_auth; + + let mut props = ClientProps::new() + .server_addr(server_addr) + .namespace(namespace) + .app_name(app); + + if enable_auth.is_some() { + info!("enable nacos auth!"); + } else { + info!("disable nacos auth!"); } - } - pub fn get_condition_config(&self, data_id: String) -> Option { - let config_resp = self - .client - .read() - .unwrap() - .get_config(data_id.clone(), "condition".to_string()); - return match config_resp { - Ok(config_resp) => { - self.add_listener(data_id.clone(), "condition".to_string()); - let string = config_resp.content(); - Some(serde_yaml::from_str(string).unwrap()) - } - Err(_err) => None, - }; + + if let Some(auth) = enable_auth { + props = props + .auth_username(auth.auth_username) + .auth_password(auth.auth_password); + } + + let client = Arc::new(RwLock::new( + ConfigServiceBuilder::new(props) + .build() + .expect("NacosClient build failed! Please check NacosConfig"), + )); + + Self { client } } - pub fn get_tag_config(&self, data_id: String) -> Option { + pub fn get_config(&self, data_id: &str, group: &str, config_type: &str) -> Option + where + T: serde::de::DeserializeOwned, + { let config_resp = self .client .read() .unwrap() - .get_config(data_id.clone(), "tag".to_string()); - return match config_resp { + .get_config(data_id.to_string(), group.to_string()); + + match config_resp { Ok(config_resp) => { - self.add_listener(data_id.clone(), "tag".to_string()); + self.add_listener(data_id, group); let string = config_resp.content(); let result = serde_yaml::from_str(string); + match result { Ok(config) => { - info!("success to get TagRouter config and parse success"); + info!( + "success to get {}Router config and parse success", + config_type + ); Some(config) } - _ => { - info!("failed to parse TagRouter rule"); + Err(_) => { + info!("failed to parse {}Router rule", config_type); None } } } - Err(_err) => None, - }; + Err(_) => None, + } } - pub fn add_listener(&self, data_id: String, group: String) { - let res_listener = self + + pub fn add_listener(&self, data_id: &str, group: &str) { + if let Err(err) = self .client .write() - .expect("failed to create nacos config listener") - .add_listener(data_id, group, Arc::new(ConfigChangeListenerImpl {})); - match res_listener { - Ok(_) => { - info!("listening the config success"); - } - Err(err) => tracing::error!("listen config error {:?}", err), + .map_err(|e| format!("failed to create nacos config listener: {}", e)) + .and_then(|client| { + client + .add_listener( + data_id.to_string(), + group.to_string(), + Arc::new(ConfigChangeListenerImpl {}), + ) + .map_err(|e| format!("failed to add nacos config listener: {}", e)) + }) + { + tracing::error!("{}", err); + } else { + info!("listening the config success"); } } } @@ -120,13 +112,15 @@ impl ConfigChangeListener for ConfigChangeListenerImpl { fn notify(&self, config_resp: ConfigResponse) { let content_type = config_resp.content_type(); let event = RouterConfigChangeEvent { - service_name: config_resp.data_id().clone(), - router_kind: config_resp.group().clone(), - content: config_resp.content().clone(), + service_name: config_resp.data_id().to_string(), + router_kind: config_resp.group().to_string(), + content: config_resp.content().to_string(), }; + if content_type == "yaml" { get_global_router_manager().write().unwrap().notify(event); } - info!("notify config={:?}", config_resp); + + info!("notify config: {:?}", config_resp); } } diff --git a/dubbo/src/cluster/router/router_chain.rs b/dubbo/src/cluster/router/router_chain.rs index 930f0f29..42d5826f 100644 --- a/dubbo/src/cluster/router/router_chain.rs +++ b/dubbo/src/cluster/router/router_chain.rs @@ -1,52 +1,30 @@ -use crate::{ - cluster::router::{ - condition::condition_router::ConditionRouter, tag::tag_router::TagRouter, Router, - }, - invocation::RpcInvocation, -}; +use crate::{cluster::router::BoxRouter, invocation::RpcInvocation}; use dubbo_base::Url; -use std::sync::{Arc, RwLock}; +use std::{collections::HashMap, sync::Arc}; -#[derive(Debug, Default, Clone)] +#[derive(Debug, Default)] pub struct RouterChain { - pub condition_router: Option, - pub tag_router: Option>>, + pub routers: HashMap, pub self_url: Url, } impl RouterChain { pub fn new() -> Self { RouterChain { - condition_router: None, - tag_router: None, + routers: HashMap::new(), self_url: Url::new(), } } - pub fn set_condition_router(&mut self, router: Option) { - self.condition_router = router; - } - pub fn set_tag_router(&mut self, router: Option>>) { - self.tag_router = router; - } - pub fn route(&self, invokers: Vec, invocation: Arc) -> Vec { - let mut result = invokers.clone(); - match &self.tag_router { - None => {} - Some(router) => { - result = - router - .read() - .unwrap() - .route(result, self.self_url.clone(), invocation.clone()) - } - } - match &self.condition_router { - Some(router) => { - result = router.route(result, self.self_url.clone(), invocation.clone()) - } - None => {} + + pub fn route(&self, mut invokers: Vec, invocation: Arc) -> Vec { + for (_, value) in self.routers.iter() { + invokers = value.route(invokers, self.self_url.clone(), invocation.clone()) } - result + invokers + } + + pub fn add_router(&mut self, key: String, router: BoxRouter) { + self.routers.insert(key, router); } } @@ -54,13 +32,16 @@ impl RouterChain { fn test() { use crate::cluster::router::manager::router_manager::get_global_router_manager; - let u1 = Url::from_url("triple://127.0.0.1:8888/org.apache.dubbo.sample.tri.Greeter").unwrap(); - let u2 = Url::from_url("triple://127.0.0.1:8889/org.apache.dubbo.sample.tri.Greeter").unwrap(); - let u3 = Url::from_url("triple://127.0.0.1:8800/org.apache.dubbo.sample.tri.Greeter").unwrap(); - let u4 = Url::from_url("triple://127.0.2.1:880/org.apache.dubbo.sample.tri.Greeter").unwrap(); - let u5 = Url::from_url("triple://127.0.1.1:8888/org.apache.dubbo.sample.tri.Greeter").unwrap(); - let invos = vec![u1, u2, u3, u4, u5]; - let invo = Arc::new( + let u1 = Url::from_url("tri://127.0.0.1:8888/org.apache.dubbo.sample.tri.Greeter").unwrap(); + let u2 = Url::from_url("tri://127.0.0.1:8889/org.apache.dubbo.sample.tri.Greeter").unwrap(); + let u3 = Url::from_url("tri://127.0.0.1:8800/org.apache.dubbo.sample.tri.Greeter").unwrap(); + let u4 = Url::from_url("tri://127.0.2.1:8880/org.apache.dubbo.sample.tri.Greeter").unwrap(); + let u5 = Url::from_url("tri://127.0.1.1:8882/org.apache.dubbo.sample.tri.Greeter").unwrap(); + let u6 = Url::from_url("tri://213.0.1.1:8888/org.apache.dubbo.sample.tri.Greeter").unwrap(); + let u7 = Url::from_url("tri://169.0.1.1:8887/org.apache.dubbo.sample.tri.Greeter").unwrap(); + let invs = vec![u1, u2, u3, u4, u5, u6, u7]; + let len = invs.len().clone(); + let inv = Arc::new( RpcInvocation::default() .with_method_name("greet".to_string()) .with_service_unique_name("org.apache.dubbo.sample.tri.Greeter".to_string()), @@ -68,7 +49,8 @@ fn test() { let x = get_global_router_manager() .read() .unwrap() - .get_router_chain(invo.clone()); - let result = x.route(invos, invo.clone()); + .get_router_chain(inv.get_target_service_unique_name()); + let result = x.route(invs, inv.clone()); + println!("total:{},result:{}", len, result.len().clone()); dbg!(result); } diff --git a/dubbo/src/cluster/router/tag/tag_router.rs b/dubbo/src/cluster/router/tag/tag_router.rs index d1a962e7..7a83ea57 100644 --- a/dubbo/src/cluster/router/tag/tag_router.rs +++ b/dubbo/src/cluster/router/tag/tag_router.rs @@ -4,16 +4,30 @@ use crate::{ }; use dubbo_base::Url; use dubbo_config::router::TagRouterConfig; -use std::{collections::HashMap, fmt::Debug, sync::Arc}; +use std::{ + collections::HashMap, + fmt::Debug, + sync::{Arc, RwLock}, +}; #[derive(Debug, Clone, Default)] -pub struct TagRouter { +pub struct TagRouterInner { pub tag_rules: HashMap>, pub force: bool, pub enabled: bool, } -impl TagRouter { +#[derive(Debug, Clone, Default)] +pub struct TagRouter { + pub(crate) inner: Arc>, +} +impl Router for TagRouter { + fn route(&self, invokers: Vec, url: Url, invocation: Arc) -> Vec { + return self.inner.read().unwrap().route(invokers, url, invocation); + } +} + +impl TagRouterInner { pub fn parse_config(&mut self, config: TagRouterConfig) { self.tag_rules = HashMap::new(); self.force = config.force; @@ -43,26 +57,28 @@ impl TagRouter { } tag_result } -} -impl Router for TagRouter { - fn route(&self, invokers: Vec, url: Url, _invocation: Arc) -> Vec { + pub fn route(&self, invokers: Vec, url: Url, _invocation: Arc) -> Vec { if !self.enabled { return invokers; }; let self_param = to_original_map(url); let invocation_tag = self.match_tag(self_param); let mut invokers_result = Vec::new(); + let mut invokers_no_tag = Vec::new(); for invoker in &invokers { let invoker_param = to_original_map(invoker.clone()); let invoker_tag = self.match_tag(invoker_param); + if invoker_tag == None { + invokers_no_tag.push(invoker.clone()); + } if invoker_tag == invocation_tag { - invokers_result.push(invoker.clone()) + invokers_result.push(invoker.clone()); } } if invokers_result.is_empty() { if !self.force { - return invokers; + return invokers_no_tag; } } invokers_result diff --git a/dubbo/src/invocation.rs b/dubbo/src/invocation.rs index d3eb8ad0..57503379 100644 --- a/dubbo/src/invocation.rs +++ b/dubbo/src/invocation.rs @@ -196,7 +196,7 @@ pub trait Invocation { fn get_method_name(&self) -> String; } -#[derive(Default,Clone)] +#[derive(Default, Clone)] pub struct RpcInvocation { target_service_unique_name: String, method_name: String, @@ -224,4 +224,4 @@ impl Invocation for RpcInvocation { fn get_method_name(&self) -> String { self.method_name.clone() } -} \ No newline at end of file +} diff --git a/dubbo/src/protocol/mod.rs b/dubbo/src/protocol/mod.rs index 50080555..c941ae5d 100644 --- a/dubbo/src/protocol/mod.rs +++ b/dubbo/src/protocol/mod.rs @@ -21,12 +21,11 @@ use std::{ }; use async_trait::async_trait; -use aws_smithy_http::body::SdkBody; use tower_service::Service; use dubbo_base::Url; -use crate::triple::client::replay::{ClonedBytesStream, ClonedBody}; +use crate::triple::client::replay::ClonedBody; pub mod server_desc; pub mod triple; diff --git a/dubbo/src/protocol/triple/triple_invoker.rs b/dubbo/src/protocol/triple/triple_invoker.rs index af174451..9f9c7698 100644 --- a/dubbo/src/protocol/triple/triple_invoker.rs +++ b/dubbo/src/protocol/triple/triple_invoker.rs @@ -24,7 +24,10 @@ use tower_service::Service; use crate::{ protocol::Invoker, - triple::{client::{builder::ClientBoxService, replay::ClonedBody}, transport::connection::Connection}, + triple::{ + client::{builder::ClientBoxService, replay::ClonedBody}, + transport::connection::Connection, + }, utils::boxed_clone::BoxCloneService, }; diff --git a/dubbo/src/triple/client/builder.rs b/dubbo/src/triple/client/builder.rs index 32b26a55..a9756f7e 100644 --- a/dubbo/src/triple/client/builder.rs +++ b/dubbo/src/triple/client/builder.rs @@ -19,7 +19,7 @@ use std::sync::Arc; use crate::{ cluster::{directory::StaticDirectory, Cluster, Directory, MockCluster, MockDirectory}, - codegen::{RegistryDirectory, RpcInvocation, TripleInvoker}, + codegen::{RegistryDirectory, TripleInvoker}, protocol::BoxInvoker, utils::boxed_clone::BoxCloneService, }; @@ -101,14 +101,14 @@ impl ClientBuilder { Self { direct, ..self } } - pub fn build(self) -> Option { + pub fn build(self, service_name: String) -> Option { if self.direct { return Some(Box::new(TripleInvoker::new( Url::from_url(&self.host).unwrap(), ))); } - let cluster = MockCluster::default().join(Box::new(MockDirectory::new())); + let cluster = MockCluster::default().join(Box::new(MockDirectory::new(service_name))); return Some(cluster); } diff --git a/dubbo/src/triple/client/mod.rs b/dubbo/src/triple/client/mod.rs index 97be85eb..013d9e30 100644 --- a/dubbo/src/triple/client/mod.rs +++ b/dubbo/src/triple/client/mod.rs @@ -16,6 +16,6 @@ */ pub mod builder; -pub mod triple; pub mod replay; +pub mod triple; pub use triple::TripleClient; diff --git a/dubbo/src/triple/client/replay.rs b/dubbo/src/triple/client/replay.rs index 4ba8b930..195d7509 100644 --- a/dubbo/src/triple/client/replay.rs +++ b/dubbo/src/triple/client/replay.rs @@ -253,7 +253,7 @@ impl Body for ClonedBody { fn poll_trailers( self: Pin<&mut Self>, - cx: &mut Context<'_>, + _cx: &mut Context<'_>, ) -> Poll, Self::Error>> { Poll::Ready(Ok(None)) } diff --git a/dubbo/src/triple/client/triple.rs b/dubbo/src/triple/client/triple.rs index 22273415..ace7aafd 100644 --- a/dubbo/src/triple/client/triple.rs +++ b/dubbo/src/triple/client/triple.rs @@ -21,12 +21,11 @@ use futures_util::{future, stream, StreamExt, TryStreamExt}; use http::HeaderValue; -use super::builder::ClientBuilder; -use super::replay::ClonedBody; +use super::{builder::ClientBuilder, replay::ClonedBody}; use crate::codegen::RpcInvocation; use crate::{ - invocation::{IntoStreamingRequest, Metadata, Request, Response}, + invocation::{IntoStreamingRequest, Invocation, Metadata, Request, Response}, triple::{codec::Codec, compression::CompressionEncoding, decode::Decoding, encode::encode}, }; @@ -153,7 +152,7 @@ impl TripleClient { .builder .clone() .unwrap() - .build() + .build(invocation.get_target_service_unique_name()) .unwrap(); let http_uri = http::Uri::from_str(&conn.get_url().to_url()).unwrap(); @@ -212,14 +211,13 @@ impl TripleClient { ) .into_stream(); - let body = ClonedBody::new(en); let mut conn = self .builder .clone() .unwrap() - .build() + .build(invocation.get_target_service_unique_name()) .unwrap(); let http_uri = http::Uri::from_str(&conn.get_url().to_url()).unwrap(); @@ -267,7 +265,7 @@ impl TripleClient { .builder .clone() .unwrap() - .build() + .build(invocation.get_target_service_unique_name()) .unwrap(); let http_uri = http::Uri::from_str(&conn.get_url().to_url()).unwrap(); @@ -333,7 +331,7 @@ impl TripleClient { .builder .clone() .unwrap() - .build() + .build(invocation.get_target_service_unique_name()) .unwrap(); let http_uri = http::Uri::from_str(&conn.get_url().to_url()).unwrap(); diff --git a/examples/echo/src/generated/grpc.examples.echo.rs b/examples/echo/src/generated/grpc.examples.echo.rs index e756d5c7..0701ec3b 100644 --- a/examples/echo/src/generated/grpc.examples.echo.rs +++ b/examples/echo/src/generated/grpc.examples.echo.rs @@ -36,16 +36,12 @@ pub mod echo_client { &mut self, request: Request, ) -> Result, dubbo::status::Status> { - let codec = dubbo::codegen::ProstCodec::< - super::EchoRequest, - super::EchoResponse, - >::default(); + let codec = + dubbo::codegen::ProstCodec::::default(); let invocation = RpcInvocation::default() .with_service_unique_name(String::from("grpc.examples.echo.Echo")) .with_method_name(String::from("UnaryEcho")); - let path = http::uri::PathAndQuery::from_static( - "/grpc.examples.echo.Echo/UnaryEcho", - ); + let path = http::uri::PathAndQuery::from_static("/grpc.examples.echo.Echo/UnaryEcho"); self.inner.unary(request, codec, path, invocation).await } /// ServerStreamingEcho is server side streaming. @@ -53,51 +49,51 @@ pub mod echo_client { &mut self, request: Request, ) -> Result>, dubbo::status::Status> { - let codec = dubbo::codegen::ProstCodec::< - super::EchoRequest, - super::EchoResponse, - >::default(); + let codec = + dubbo::codegen::ProstCodec::::default(); let invocation = RpcInvocation::default() .with_service_unique_name(String::from("grpc.examples.echo.Echo")) .with_method_name(String::from("ServerStreamingEcho")); let path = http::uri::PathAndQuery::from_static( "/grpc.examples.echo.Echo/ServerStreamingEcho", ); - self.inner.server_streaming(request, codec, path, invocation).await + self.inner + .server_streaming(request, codec, path, invocation) + .await } /// ClientStreamingEcho is client side streaming. pub async fn client_streaming_echo( &mut self, request: impl IntoStreamingRequest, ) -> Result, dubbo::status::Status> { - let codec = dubbo::codegen::ProstCodec::< - super::EchoRequest, - super::EchoResponse, - >::default(); + let codec = + dubbo::codegen::ProstCodec::::default(); let invocation = RpcInvocation::default() .with_service_unique_name(String::from("grpc.examples.echo.Echo")) .with_method_name(String::from("ClientStreamingEcho")); let path = http::uri::PathAndQuery::from_static( "/grpc.examples.echo.Echo/ClientStreamingEcho", ); - self.inner.client_streaming(request, codec, path, invocation).await + self.inner + .client_streaming(request, codec, path, invocation) + .await } /// BidirectionalStreamingEcho is bidi streaming. pub async fn bidirectional_streaming_echo( &mut self, request: impl IntoStreamingRequest, ) -> Result>, dubbo::status::Status> { - let codec = dubbo::codegen::ProstCodec::< - super::EchoRequest, - super::EchoResponse, - >::default(); + let codec = + dubbo::codegen::ProstCodec::::default(); let invocation = RpcInvocation::default() .with_service_unique_name(String::from("grpc.examples.echo.Echo")) .with_method_name(String::from("BidirectionalStreamingEcho")); let path = http::uri::PathAndQuery::from_static( "/grpc.examples.echo.Echo/BidirectionalStreamingEcho", ); - self.inner.bidi_streaming(request, codec, path, invocation).await + self.inner + .bidi_streaming(request, codec, path, invocation) + .await } } } @@ -114,9 +110,7 @@ pub mod echo_server { request: Request, ) -> Result, dubbo::status::Status>; ///Server streaming response type for the ServerStreamingEcho method. - type ServerStreamingEchoStream: futures_util::Stream< - Item = Result, - > + type ServerStreamingEchoStream: futures_util::Stream> + Send + 'static; /// ServerStreamingEcho is server side streaming. @@ -130,19 +124,14 @@ pub mod echo_server { request: Request>, ) -> Result, dubbo::status::Status>; ///Server streaming response type for the BidirectionalStreamingEcho method. - type BidirectionalStreamingEchoStream: futures_util::Stream< - Item = Result, - > + type BidirectionalStreamingEchoStream: futures_util::Stream> + Send + 'static; /// BidirectionalStreamingEcho is bidi streaming. async fn bidirectional_streaming_echo( &self, request: Request>, - ) -> Result< - Response, - dubbo::status::Status, - >; + ) -> Result, dubbo::status::Status>; } /// Echo is the echo service. #[derive(Debug)] @@ -172,10 +161,7 @@ pub mod echo_server { type Response = http::Response; type Error = std::convert::Infallible; type Future = BoxFuture; - fn poll_ready( - &mut self, - _cx: &mut Context<'_>, - ) -> Poll> { + fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } fn call(&mut self, req: http::Request) -> Self::Future { @@ -188,26 +174,18 @@ pub mod echo_server { } impl UnarySvc for UnaryEchoServer { type Response = super::EchoResponse; - type Future = BoxFuture< - Response, - dubbo::status::Status, - >; - fn call( - &mut self, - request: Request, - ) -> Self::Future { + type Future = BoxFuture, dubbo::status::Status>; + fn call(&mut self, request: Request) -> Self::Future { let inner = self.inner.0.clone(); let fut = async move { inner.unary_echo(request).await }; Box::pin(fut) } } let fut = async move { - let mut server = TripleServer::new( - dubbo::codegen::ProstCodec::< - super::EchoResponse, - super::EchoRequest, - >::default(), - ); + let mut server = TripleServer::new(dubbo::codegen::ProstCodec::< + super::EchoResponse, + super::EchoRequest, + >::default()); let res = server.unary(UnaryEchoServer { inner }, req).await; Ok(res) }; @@ -218,32 +196,22 @@ pub mod echo_server { struct ServerStreamingEchoServer { inner: _Inner, } - impl ServerStreamingSvc - for ServerStreamingEchoServer { + impl ServerStreamingSvc for ServerStreamingEchoServer { type Response = super::EchoResponse; type ResponseStream = T::ServerStreamingEchoStream; - type Future = BoxFuture< - Response, - dubbo::status::Status, - >; - fn call( - &mut self, - request: Request, - ) -> Self::Future { + type Future = + BoxFuture, dubbo::status::Status>; + fn call(&mut self, request: Request) -> Self::Future { let inner = self.inner.0.clone(); - let fut = async move { - inner.server_streaming_echo(request).await - }; + let fut = async move { inner.server_streaming_echo(request).await }; Box::pin(fut) } } let fut = async move { - let mut server = TripleServer::new( - dubbo::codegen::ProstCodec::< - super::EchoResponse, - super::EchoRequest, - >::default(), - ); + let mut server = TripleServer::new(dubbo::codegen::ProstCodec::< + super::EchoResponse, + super::EchoRequest, + >::default()); let res = server .server_streaming(ServerStreamingEchoServer { inner }, req) .await; @@ -256,31 +224,23 @@ pub mod echo_server { struct ClientStreamingEchoServer { inner: _Inner, } - impl ClientStreamingSvc - for ClientStreamingEchoServer { + impl ClientStreamingSvc for ClientStreamingEchoServer { type Response = super::EchoResponse; - type Future = BoxFuture< - Response, - dubbo::status::Status, - >; + type Future = BoxFuture, dubbo::status::Status>; fn call( &mut self, request: Request>, ) -> Self::Future { let inner = self.inner.0.clone(); - let fut = async move { - inner.client_streaming_echo(request).await - }; + let fut = async move { inner.client_streaming_echo(request).await }; Box::pin(fut) } } let fut = async move { - let mut server = TripleServer::new( - dubbo::codegen::ProstCodec::< - super::EchoResponse, - super::EchoRequest, - >::default(), - ); + let mut server = TripleServer::new(dubbo::codegen::ProstCodec::< + super::EchoResponse, + super::EchoRequest, + >::default()); let res = server .client_streaming(ClientStreamingEchoServer { inner }, req) .await; @@ -293,56 +253,41 @@ pub mod echo_server { struct BidirectionalStreamingEchoServer { inner: _Inner, } - impl StreamingSvc - for BidirectionalStreamingEchoServer { + impl StreamingSvc for BidirectionalStreamingEchoServer { type Response = super::EchoResponse; type ResponseStream = T::BidirectionalStreamingEchoStream; - type Future = BoxFuture< - Response, - dubbo::status::Status, - >; + type Future = + BoxFuture, dubbo::status::Status>; fn call( &mut self, request: Request>, ) -> Self::Future { let inner = self.inner.0.clone(); - let fut = async move { - inner.bidirectional_streaming_echo(request).await - }; + let fut = + async move { inner.bidirectional_streaming_echo(request).await }; Box::pin(fut) } } let fut = async move { - let mut server = TripleServer::new( - dubbo::codegen::ProstCodec::< - super::EchoResponse, - super::EchoRequest, - >::default(), - ); + let mut server = TripleServer::new(dubbo::codegen::ProstCodec::< + super::EchoResponse, + super::EchoRequest, + >::default()); let res = server - .bidi_streaming( - BidirectionalStreamingEchoServer { - inner, - }, - req, - ) + .bidi_streaming(BidirectionalStreamingEchoServer { inner }, req) .await; Ok(res) }; Box::pin(fut) } - _ => { - Box::pin(async move { - Ok( - http::Response::builder() - .status(200) - .header("grpc-status", "12") - .header("content-type", "application/grpc") - .body(empty_body()) - .unwrap(), - ) - }) - } + _ => Box::pin(async move { + Ok(http::Response::builder() + .status(200) + .header("grpc-status", "12") + .header("content-type", "application/grpc") + .body(empty_body()) + .unwrap()) + }), } } } From 29dc2903749fea8933ce8e71920d8753c83b09e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=AF=9B=E6=96=87=E8=B6=85?= Date: Fri, 17 Nov 2023 19:50:48 +0800 Subject: [PATCH 35/48] Refactor: refactor Cluster component (#165) * Refactor: refactor Cluster component - add p2c loadbalance component - add simple router component - add replay body component - add failover component - add service directory compoent * Enhance: add cache for routers --- Cargo.toml | 1 + common/base/src/lib.rs | 2 +- dubbo-build/src/client.rs | 6 - dubbo/Cargo.toml | 4 +- dubbo/src/cluster/clone_body.rs | 382 +++++++++++++++ dubbo/src/cluster/clone_invoker.rs | 248 ++++++++++ dubbo/src/cluster/directory.rs | 383 --------------- dubbo/src/cluster/failover.rs | 76 +++ dubbo/src/cluster/loadbalance/impls/mod.rs | 18 - dubbo/src/cluster/loadbalance/impls/random.rs | 59 --- .../cluster/loadbalance/impls/roundrobin.rs | 85 ---- dubbo/src/cluster/loadbalance/mod.rs | 42 -- dubbo/src/cluster/loadbalance/types.rs | 42 -- dubbo/src/cluster/mod.rs | 451 ++---------------- dubbo/src/codegen.rs | 4 +- dubbo/src/directory/mod.rs | 298 ++++++++++++ dubbo/src/invoker/mod.rs | 75 +++ dubbo/src/lib.rs | 6 + dubbo/src/loadbalancer/mod.rs | 102 ++++ dubbo/src/param.rs | 12 + dubbo/src/protocol/mod.rs | 11 +- dubbo/src/protocol/triple/triple_invoker.rs | 29 +- dubbo/src/registry/mod.rs | 2 + dubbo/src/registry/n_registry.rs | 127 +++++ dubbo/src/route/mod.rs | 196 ++++++++ dubbo/src/svc.rs | 88 ++++ dubbo/src/triple/client/builder.rs | 62 ++- dubbo/src/triple/client/mod.rs | 2 +- dubbo/src/triple/client/replay.rs | 441 ----------------- dubbo/src/triple/client/triple.rs | 101 ++-- .../echo/src/generated/grpc.examples.echo.rs | 179 ++++--- examples/greeter/src/greeter/client.rs | 15 +- registry/nacos/Cargo.toml | 2 +- 33 files changed, 1874 insertions(+), 1677 deletions(-) create mode 100644 dubbo/src/cluster/clone_body.rs create mode 100644 dubbo/src/cluster/clone_invoker.rs delete mode 100644 dubbo/src/cluster/directory.rs create mode 100644 dubbo/src/cluster/failover.rs delete mode 100644 dubbo/src/cluster/loadbalance/impls/mod.rs delete mode 100644 dubbo/src/cluster/loadbalance/impls/random.rs delete mode 100644 dubbo/src/cluster/loadbalance/impls/roundrobin.rs delete mode 100644 dubbo/src/cluster/loadbalance/mod.rs delete mode 100644 dubbo/src/cluster/loadbalance/types.rs create mode 100644 dubbo/src/directory/mod.rs create mode 100644 dubbo/src/invoker/mod.rs create mode 100644 dubbo/src/loadbalancer/mod.rs create mode 100644 dubbo/src/param.rs create mode 100644 dubbo/src/registry/n_registry.rs create mode 100644 dubbo/src/route/mod.rs create mode 100644 dubbo/src/svc.rs delete mode 100644 dubbo/src/triple/client/replay.rs diff --git a/Cargo.toml b/Cargo.toml index 5270ba42..0abff00d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,4 +1,5 @@ [workspace] +resolver = "2" members = [ "common/logger", "common/utils", diff --git a/common/base/src/lib.rs b/common/base/src/lib.rs index b97b342f..cb3bc09a 100644 --- a/common/base/src/lib.rs +++ b/common/base/src/lib.rs @@ -23,4 +23,4 @@ pub mod node; pub mod url; pub use node::Node; -pub use url::Url; +pub use url::Url; \ No newline at end of file diff --git a/dubbo-build/src/client.rs b/dubbo-build/src/client.rs index bfcfe45b..418bc10e 100644 --- a/dubbo-build/src/client.rs +++ b/dubbo-build/src/client.rs @@ -78,12 +78,6 @@ pub fn generate( } } - // pub fn build(builder: ClientBuilder) -> Self { - // Self { - // inner: TripleClient::new(builder), - // } - // } - pub fn new(builder: ClientBuilder) -> Self { Self { inner: TripleClient::new(builder), diff --git a/dubbo/Cargo.toml b/dubbo/Cargo.toml index 43be50b9..84874748 100644 --- a/dubbo/Cargo.toml +++ b/dubbo/Cargo.toml @@ -14,13 +14,15 @@ hyper = { version = "0.14.26", features = ["full"] } http = "0.2" tower-service.workspace = true http-body = "0.4.4" -tower = { workspace = true, features = ["timeout", "ready-cache","discover"] } +tower = { workspace = true, features = ["timeout", "ready-cache","discover","retry"] } futures-util = "0.3.23" futures-core ="0.3.23" argh = "0.1" rustls-pemfile = "1.0.0" tokio-rustls="0.23.4" tokio = { version = "1.0", features = [ "rt-multi-thread", "time", "fs", "macros", "net", "signal", "full" ] } +tokio-util = "0.7.9" +tokio-stream = "0.1" prost = "0.10.4" async-trait = "0.1.56" tower-layer.workspace = true diff --git a/dubbo/src/cluster/clone_body.rs b/dubbo/src/cluster/clone_body.rs new file mode 100644 index 00000000..1d6b65a8 --- /dev/null +++ b/dubbo/src/cluster/clone_body.rs @@ -0,0 +1,382 @@ +use std::{ + collections::VecDeque, + pin::Pin, + sync::{Arc, Mutex}, + task::{Context, Poll}, +}; + +use bytes::{Buf, BufMut, Bytes, BytesMut}; +use futures_core::ready; + +use http::HeaderMap; +use http_body::Body; +use pin_project::pin_project; +use thiserror::Error; + +use crate::StdError; + + +#[derive(Error, Debug)] +#[error("buffered body reach max capacity.")] +pub struct ReachMaxCapacityError; + +pub struct BufferedBody { + shared: Arc>>>, + owned: Option>, + replay_body: bool, + replay_trailers: bool, + is_empty: bool, + size_hint: http_body::SizeHint, +} + +pub struct OwnedBufferedBody { + body: B, + trailers: Option, + buf: InnerBuffer, +} + + + +impl BufferedBody { + + pub fn new(body: B, buf_size: usize) -> Self { + let size_hint = body.size_hint(); + let is_empty = body.is_end_stream(); + BufferedBody { + shared: Default::default(), + owned: Some(OwnedBufferedBody { + body, + trailers: None, + buf: InnerBuffer { + bufs: Default::default(), + capacity: buf_size, + }, + }), + replay_body: false, + replay_trailers: false, + is_empty, + size_hint, + } + } + +} + +impl Clone for BufferedBody { + + fn clone(&self) -> Self { + Self { + shared: self.shared.clone(), + owned: None, + replay_body: true, + replay_trailers: true, + is_empty: self.is_empty, + size_hint: self.size_hint.clone(), + } + } +} + +impl Drop for BufferedBody { + fn drop(&mut self) { + if let Some(owned) = self.owned.take() { + let lock = self.shared.lock(); + if let Ok(mut lock) = lock { + *lock = Some(owned); + } + } + } +} + +impl Body for BufferedBody +where + B: http_body::Body + Unpin, + B::Error: Into, +{ + type Data = BytesData; + type Error = StdError; + + fn poll_data( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll>> { + let mut_self = self.get_mut(); + + let owned_body = mut_self.owned.get_or_insert_with(|| { + let lock = mut_self.shared.lock(); + if let Err(e) = lock { + panic!("buffered body get shared data lock failed. {}", e); + } + let mut data = lock.unwrap(); + + data.take().expect("cannot get shared buffered body.") + }); + + + + if mut_self.replay_body { + mut_self.replay_body = false; + if owned_body.buf.has_remaining() { + return Poll::Ready(Some(Ok(BytesData::BufferedBytes( + owned_body.buf.clone(), + )))); + } + + if owned_body.buf.is_capped() { + return Poll::Ready(Some(Err(ReachMaxCapacityError.into()))); + } + } + + if mut_self.is_empty { + return Poll::Ready(None); + } + + let mut data = { + let pin = Pin::new(&mut owned_body.body); + let data = ready!(pin.poll_data(cx)); + match data { + Some(Ok(data)) => data, + Some(Err(e)) => return Poll::Ready(Some(Err(e.into()))), + None => { + mut_self.is_empty = true; + return Poll::Ready(None); + } + } + }; + + let len = data.remaining(); + + owned_body.buf.capacity = owned_body.buf.capacity.saturating_sub(len); + + let data = if owned_body.buf.is_capped() { + if owned_body.buf.has_remaining() { + owned_body.buf.bufs = VecDeque::default(); + } + data.copy_to_bytes(len) + } else { + owned_body.buf.push_bytes(data.copy_to_bytes(len)) + }; + + Poll::Ready(Some(Ok(BytesData::OriginBytes(data)))) + + + } + + fn poll_trailers( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll, Self::Error>> { + let mut_self = self.get_mut(); + let owned_body = mut_self.owned.get_or_insert_with(|| { + let lock = mut_self.shared.lock(); + if let Err(e) = lock { + panic!("buffered body get shared data lock failed. {}", e); + } + let mut data = lock.unwrap(); + + data.take().expect("cannot get shared buffered body.") + }); + + if mut_self.replay_trailers { + mut_self.replay_trailers = false; + if let Some(ref trailers) = owned_body.trailers { + return Poll::Ready(Ok(Some(trailers.clone()))); + } + } + + let mut_body = &mut owned_body.body; + if !mut_body.is_end_stream() { + let trailers = ready!(Pin::new(mut_body).poll_trailers(cx)).map(|trailers| { + owned_body.trailers = trailers.clone(); + trailers + }); + return Poll::Ready(trailers.map_err(|e|e.into())); + } + + Poll::Ready(Ok(None)) + } + + fn is_end_stream(&self) -> bool { + if self.is_empty { + return true; + } + + let is_end = self.owned.as_ref() + .map(|owned|owned.body.is_end_stream()) + .unwrap_or(false); + + !self.replay_body && !self.replay_trailers && is_end + } + + fn size_hint(&self) -> http_body::SizeHint { + self.size_hint.clone() + } + + +} + + + +#[derive(Clone)] +pub struct InnerBuffer { + bufs: VecDeque, + capacity: usize, +} + +impl InnerBuffer { + pub fn push_bytes(&mut self, bytes: Bytes) -> Bytes { + self.bufs.push_back(bytes.clone()); + bytes + } + + pub fn is_capped(&self) -> bool { + self.capacity == 0 + } +} + +impl Buf for InnerBuffer { + fn remaining(&self) -> usize { + self.bufs.iter().map(|bytes| bytes.remaining()).sum() + } + + fn chunk(&self) -> &[u8] { + self.bufs.front().map(Buf::chunk).unwrap_or(&[]) + } + + fn chunks_vectored<'a>(&'a self, dst: &mut [std::io::IoSlice<'a>]) -> usize { + if dst.is_empty() { + return 0; + } + + let mut filled = 0; + + for bytes in self.bufs.iter() { + filled += bytes.chunks_vectored(&mut dst[filled..]) + } + + filled + } + + fn advance(&mut self, mut cnt: usize) { + while cnt > 0 { + let first = self.bufs.front_mut(); + if first.is_none() { + break; + } + let first = first.unwrap(); + let first_remaining = first.remaining(); + if first_remaining > cnt { + first.advance(cnt); + break; + } + + first.advance(first_remaining); + cnt = cnt - first_remaining; + self.bufs.pop_front(); + } + } + + fn copy_to_bytes(&mut self, len: usize) -> bytes::Bytes { + match self.bufs.front_mut() { + Some(buf) if len <= buf.remaining() => { + let bytes = buf.copy_to_bytes(len); + if buf.remaining() == 0 { + self.bufs.pop_front(); + } + bytes + } + _ => { + let mut bytes = BytesMut::with_capacity(len); + bytes.put(self.take(len)); + bytes.freeze() + } + } + } +} + +pub enum BytesData { + BufferedBytes(InnerBuffer), + OriginBytes(Bytes), +} + +impl Buf for BytesData { + fn remaining(&self) -> usize { + match self { + BytesData::BufferedBytes(bytes) => bytes.remaining(), + BytesData::OriginBytes(bytes) => bytes.remaining(), + } + } + + fn chunk(&self) -> &[u8] { + match self { + BytesData::BufferedBytes(bytes) => bytes.chunk(), + BytesData::OriginBytes(bytes) => bytes.chunk(), + } + } + + fn advance(&mut self, cnt: usize) { + match self { + BytesData::BufferedBytes(bytes) => bytes.advance(cnt), + BytesData::OriginBytes(bytes) => bytes.advance(cnt), + } + } + + fn copy_to_bytes(&mut self, len: usize) -> bytes::Bytes { + match self { + BytesData::BufferedBytes(bytes) => bytes.copy_to_bytes(len), + BytesData::OriginBytes(bytes) => bytes.copy_to_bytes(len), + } + } +} + +#[pin_project] +pub struct CloneBody(#[pin] BufferedBody); + +impl CloneBody +where + B: http_body::Body + Unpin, + B::Error: Into, +{ + pub fn new(inner_body: B) -> Self { + let inner_body = BufferedBody::new(inner_body, 1024 * 64); + CloneBody(inner_body) + } +} + +impl Body for CloneBody +where + B: http_body::Body + Unpin, + B::Error: Into, +{ + + type Data = BytesData; + + type Error = StdError; + + fn poll_data( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll>> { + self.project().0.poll_data(cx) + } + + fn poll_trailers( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll, Self::Error>> { + self.project().0.poll_trailers(cx) + } + + fn size_hint(&self) -> http_body::SizeHint { + self.0.size_hint() + } +} + + +impl Clone for CloneBody +where + B: http_body::Body + Unpin, + B::Error: Into, +{ + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} \ No newline at end of file diff --git a/dubbo/src/cluster/clone_invoker.rs b/dubbo/src/cluster/clone_invoker.rs new file mode 100644 index 00000000..20e18118 --- /dev/null +++ b/dubbo/src/cluster/clone_invoker.rs @@ -0,0 +1,248 @@ +use std::{task::Poll, pin::Pin, mem}; + +use dubbo_logger::tracing::debug; +use futures_core::{Future, TryFuture, ready, future::BoxFuture}; +use futures_util::FutureExt; +use pin_project::pin_project; +use thiserror::Error; +use tokio::{task::JoinHandle, sync::{watch::{Sender, Receiver}, self}}; +use tokio_util::sync::ReusableBoxFuture; +use tower::{ServiceExt, buffer::Buffer}; +use tower_service::Service; + +use crate::StdError; + +enum Inner { + Invalid, + Ready(S), + Pending(JoinHandle>), +} + +#[derive(Debug, Error)] +#[error("the inner service has not got ready yet!")] +struct InnerServiceNotReadyErr; + + + +#[pin_project(project = InnerServiceCallingResponseProj)] +enum InnerServiceCallingResponse { + Call(#[pin] Fut), + Fail +} + +impl Future for InnerServiceCallingResponse +where + Fut: TryFuture, + Fut::Error: Into +{ + type Output = Result; + + fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll { + match self.project() { + InnerServiceCallingResponseProj::Call(call) => call.try_poll(cx).map_err(Into::into), + InnerServiceCallingResponseProj::Fail => Poll::Ready(Err(InnerServiceNotReadyErr.into())) + } + } +} + +#[derive(Clone)] +enum ObserveState { + Ready, + Pending, +} + + +struct ReadyService { + inner: Inner, + tx: Sender +} + + +impl ReadyService { + + fn new(inner: S) -> (Self, Receiver) { + let (tx, rx) = sync::watch::channel(ObserveState::Ready); + let ready_service = Self { inner: Inner::Ready(inner), tx}; + (ready_service, rx) + } + +} + +impl Service for ReadyService +where + S: Service + Send + 'static, + >::Error: Into, +{ + type Response = S::Response; + + type Error = StdError; + + type Future = InnerServiceCallingResponse; + + fn poll_ready(&mut self, cx: &mut std::task::Context<'_>) -> Poll> { + loop { + match mem::replace(&mut self.inner, Inner::Invalid) { + Inner::Ready(mut svc) => { + let poll_ready = svc.poll_ready(cx); + match poll_ready { + Poll::Pending => { + self.inner = Inner::Pending(tokio::spawn(async move { + let poll_ready = svc.ready().await; + match poll_ready { + Ok(_) => Ok(svc), + Err(err) => { + Err((svc, err.into())) + } + } + })); + + let _ = self.tx.send(ObserveState::Pending); + continue; + } + Poll::Ready(ret) => { + self.inner = Inner::Ready(svc); + + let _ = self.tx.send(ObserveState::Ready); + return Poll::Ready(ret.map_err(Into::into)); + } + } + }, + Inner::Pending(mut join_handle) => { + if let Poll::Ready(res) = join_handle.poll_unpin(cx) { + let (svc, res) = match res { + Err(join_err) => panic!("ReadyService panicked: {join_err}"), + Ok(Err((svc, err))) => (svc, Poll::Ready(Err(err))), + Ok(Ok(svc)) => (svc, Poll::Ready(Ok(()))) + }; + + self.inner = Inner::Ready(svc); + + let _ = self.tx.send(ObserveState::Ready); + return res; + } else { + self.inner = Inner::Pending(join_handle); + + let _ = self.tx.send(ObserveState::Pending); + return Poll::Pending; + } + + }, + Inner::Invalid => panic!("ReadyService panicked: inner state is invalid") + } + } + } + + fn call(&mut self, req: Req) -> Self::Future { + match self.inner { + Inner::Ready(ref mut svc) => InnerServiceCallingResponse::Call(svc.call(req)), + _ => InnerServiceCallingResponse::Fail + } + } +} + + +impl Drop for ReadyService { + fn drop(&mut self) { + if let Inner::Pending(ref handler) = self.inner { + handler.abort(); + } + } +} + +pub struct CloneInvoker +where + Inv: Service + Send + 'static, + Inv::Error: Into + Send + Sync + 'static, + Inv::Future: Send, + Req: Send +{ + inner: Buffer, Req>, + rx: Receiver, + poll: ReusableBoxFuture<'static, ObserveState>, + polling: bool, +} + +impl CloneInvoker +where + Inv: Service + Send + 'static, + Inv::Error: Into + Send + Sync + 'static, + Inv::Future: Send, + Req: Send + 'static +{ + + pub fn new(invoker: Inv) -> Self { + + let (ready_service, rx) = ReadyService::new(invoker); + + let buffer: Buffer, Req> = Buffer::new(ready_service, 1024); + + Self { inner: buffer, rx, polling: false, poll: ReusableBoxFuture::new(futures::future::pending()) } + } +} + +impl Service for CloneInvoker +where + Inv: Service + Send + 'static, + Inv::Error: Into + Send + Sync + 'static, + Inv::Future: Send, + Req: Send + 'static +{ + type Response = Inv::Response; + + type Error = StdError; + + type Future = BoxFuture<'static, Result>; + + fn poll_ready(&mut self, cx: &mut std::task::Context<'_>) -> Poll> { + loop { + if !self.polling { + match self.rx.borrow().clone() { + ObserveState::Ready => return self.inner.poll_ready(cx), + ObserveState::Pending => { + self.polling = true; + let mut rx = self.rx.clone(); + self.poll.set(async move { + loop { + let current_state = rx.borrow_and_update().clone(); + if matches!(current_state, ObserveState::Ready) { + return current_state; + } + if let Err(_) = rx.changed().await { + debug!("the readyService has already shutdown!"); + futures::future::pending::().await; + } + } + }); + } + } + + } + + let state = ready!(self.poll.poll_unpin(cx)); + self.polling = false; + + if matches!(state, ObserveState::Pending) { + continue; + } + + return self.inner.poll_ready(cx); + } + } + + fn call(&mut self, req: Req) -> Self::Future { + Box::pin(self.inner.call(req)) + } +} + + +impl Clone for CloneInvoker +where + Inv: Service + Send + 'static, + Inv::Error: Into + Send + Sync + 'static, + Inv::Future: Send, + Req: Send +{ + fn clone(&self) -> Self { + Self { inner: self.inner.clone(), rx: self.rx.clone(), polling: false, poll: ReusableBoxFuture::new(futures::future::pending())} + } +} \ No newline at end of file diff --git a/dubbo/src/cluster/directory.rs b/dubbo/src/cluster/directory.rs deleted file mode 100644 index 66a3c43a..00000000 --- a/dubbo/src/cluster/directory.rs +++ /dev/null @@ -1,383 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -use std::{ - collections::HashMap, - fmt::Debug, - pin::Pin, - str::FromStr, - sync::{Arc, RwLock}, - task::{Context, Poll}, -}; - -use crate::{ - codegen::TripleInvoker, - protocol::BoxInvoker, - registry::{memory_registry::MemoryNotifyListener, BoxRegistry}, - triple::client::replay::ClonedBody, - StdError, -}; -use dubbo_base::Url; -use dubbo_logger::tracing; -use futures_core::ready; -use tower::{ - discover::{Change, Discover}, - ready_cache::ReadyCache, -}; - -use crate::{cluster::Directory, codegen::RpcInvocation, invocation::Invocation}; - -/// Directory. -/// -/// [Directory Service](http://en.wikipedia.org/wiki/Directory_service) - -#[derive(Debug, Clone)] -pub struct StaticDirectory { - uri: http::Uri, -} - -impl StaticDirectory { - pub fn new(host: &str) -> StaticDirectory { - let uri = match http::Uri::from_str(host) { - Ok(v) => v, - Err(err) => { - tracing::error!("http uri parse error: {}, host: {}", err, host); - panic!("http uri parse error: {}, host: {}", err, host) - } - }; - StaticDirectory { uri } - } - - pub fn from_uri(uri: &http::Uri) -> StaticDirectory { - StaticDirectory { uri: uri.clone() } - } -} - -impl Directory for StaticDirectory { - fn list(&self, inv: Arc) -> Vec { - let url = Url::from_url(&format!( - "tri://{}:{}/{}", - self.uri.host().unwrap(), - self.uri.port().unwrap(), - inv.get_target_service_unique_name(), - )) - .unwrap(); - let invoker = Box::new(TripleInvoker::new(url)); - vec![invoker] - } -} - -#[derive(Debug, Clone)] -pub struct RegistryDirectory { - registry: Arc, - service_instances: Arc>>>, -} - -impl RegistryDirectory { - pub fn new(registry: BoxRegistry) -> RegistryDirectory { - RegistryDirectory { - registry: Arc::new(registry), - service_instances: Arc::new(RwLock::new(HashMap::new())), - } - } -} - -impl Directory for RegistryDirectory { - fn list(&self, inv: Arc) -> Vec { - let service_name = inv.get_target_service_unique_name(); - let url = Url::from_url(&format!( - "triple://{}:{}/{}", - "127.0.0.1", "8888", service_name - )) - .unwrap(); - - self.registry - .subscribe( - url, - Arc::new(MemoryNotifyListener { - service_instances: Arc::clone(&self.service_instances), - }), - ) - .expect("subscribe"); - - let map = self - .service_instances - .read() - .expect("service_instances.read"); - let binding = Vec::new(); - let url_vec = map.get(&service_name).unwrap_or(&binding); - // url_vec.to_vec() - let mut invokers: Vec = vec![]; - for item in url_vec.iter() { - invokers.push(Box::new(TripleInvoker::new(item.clone()))); - } - - invokers - } -} - -pub struct ServiceNameDirectory { - cache: ReadyCache>, - discover: D, -} - -impl ServiceNameDirectory -where - D: Discover + Unpin, - D::Error: Into, -{ - pub fn new(discover: D) -> Self { - Self { - cache: ReadyCache::default(), - discover, - } - } - - fn update_cache(&mut self, cx: &mut Context<'_>) -> Poll> { - loop { - let discover = Pin::new(&mut self.discover); - let update = ready!(discover.poll_discover(cx)); - match update { - Some(update) => { - let change = match update { - Err(_) => continue, - Ok(change) => change, - }; - - match change { - Change::Insert(key, invoker) => { - self.cache.push(key, invoker); - } - Change::Remove(key) => { - self.cache.evict(&key); - } - } - } - None => break, - } - } - - // poll pending - let _ = ready!(self.cache.poll_pending(cx))?; - - Poll::Ready(Ok(())) - } - - pub fn list(&mut self, cx: &mut Context<'_>) -> Poll, StdError>> { - let _ = self.update_cache(cx)?; - - let ready_len = self.cache.ready_len(); - - if ready_len == 0 { - return Poll::Pending; - } - - let mut invoker_list = Vec::with_capacity(ready_len); - - for idx in 0..ready_len { - let check = self.cache.check_ready_index(cx, idx); - let is_ready = match check { - Ok(is_ready) => is_ready, - Err(_) => false, - }; - if !is_ready { - continue; - } - - let invoker_url = match self.cache.get_ready_index(idx) { - None => continue, - Some((k, _)) => k.clone(), - }; - - invoker_list.push(invoker_url); - } - - Poll::Ready(Ok(invoker_list)) - } -} - -#[cfg(test)] -pub mod tests { - use std::{ - convert::Infallible, - pin::Pin, - task::{Context, Poll}, - }; - - use crate::{boxed, cluster::directory::ServiceNameDirectory}; - use bytes::Buf; - use futures_core::Stream; - use futures_util::future::poll_fn; - use http::StatusCode; - use http_body::Body; - use tower::{discover::Change, Service}; - - use dubbo_base::Url; - - use crate::{codegen::Invoker, protocol::BoxInvoker, triple::client::replay::ClonedBody}; - - pub struct MockStaticServiceList - where - T: IntoIterator, - { - iter: T::IntoIter, - } - - impl MockStaticServiceList - where - T: IntoIterator, - { - pub fn new(list: T) -> Self { - let iter = list.into_iter(); - - Self { iter } - } - } - - impl Stream for MockStaticServiceList - where - T: IntoIterator, - T::IntoIter: Unpin, - { - type Item = Result, Infallible>; - - fn poll_next(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { - let mut_self_ref = self.get_mut(); - let mut_iter_ref = &mut mut_self_ref.iter; - - match mut_iter_ref.next() { - Some(next) => { - let invoker_url = next.get_url(); - let raw_url_string = invoker_url.raw_url_string(); - return Poll::Ready(Some(Ok(Change::Insert(raw_url_string, next)))); - } - None => Poll::Ready(None), - } - } - } - - #[derive(Debug)] - struct MockInvoker(u8, bool); - - impl Invoker> for MockInvoker { - fn get_url(&self) -> dubbo_base::Url { - let str = format!( - "triple://127.0.0.1:8888/failover_cluster_service/directory/{}", - self.0 - ); - Url::from_url(str.as_str()).unwrap() - } - } - - impl Service> for MockInvoker { - type Response = http::Response; - - type Error = crate::Error; - - type Future = crate::BoxFuture, crate::Error>; - - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { - if !self.1 { - return Poll::Pending; - } - - Poll::Ready(Ok(())) - } - - fn call(&mut self, req: http::Request) -> Self::Future { - Box::pin(async move { - let mut body = req.into_body(); - - let mut body_data = poll_fn(|cx| Pin::new(&mut body).poll_data(cx)) - .await - .unwrap() - .unwrap(); - - let len = body_data.remaining(); - let bytes = body_data.copy_to_bytes(len); - - let req_str = String::from_utf8(bytes.to_vec()).unwrap(); - println!("req: {}", req_str); - let echo_data = format!("echo: {}", req_str); - let response = http::Response::builder() - .status(StatusCode::OK) - .body(boxed(echo_data)) - .unwrap(); - - return Ok(response); - }) - } - } - - #[tokio::test] - async fn test_directory() { - let invoker_list = vec![ - Box::new(MockInvoker(1, true)) as BoxInvoker, - Box::new(MockInvoker(2, true)) as BoxInvoker, - Box::new(MockInvoker(3, true)) as BoxInvoker, - Box::new(MockInvoker(4, true)) as BoxInvoker, - Box::new(MockInvoker(5, true)) as BoxInvoker, - Box::new(MockInvoker(6, true)) as BoxInvoker, - ]; - let service_list = MockStaticServiceList::new(invoker_list); - - let mut service_name_directory = ServiceNameDirectory::new(service_list); - - let list = poll_fn(|cx| service_name_directory.list(cx)).await; - - assert!(list.is_ok()); - - let list = list.unwrap(); - - assert!(!list.is_empty()); - - assert_eq!(6, list.len()); - - for uri in list { - println!("invoker uri: {}", uri); - } - } - - #[tokio::test] - async fn test_ready_in_any_order() { - let invoker_list = vec![ - Box::new(MockInvoker(1, true)) as BoxInvoker, - Box::new(MockInvoker(2, false)) as BoxInvoker, - Box::new(MockInvoker(3, false)) as BoxInvoker, - Box::new(MockInvoker(4, true)) as BoxInvoker, - Box::new(MockInvoker(5, false)) as BoxInvoker, - Box::new(MockInvoker(6, true)) as BoxInvoker, - ]; - let service_list = MockStaticServiceList::new(invoker_list); - - let mut service_name_directory = ServiceNameDirectory::new(service_list); - - let list = poll_fn(|cx| service_name_directory.list(cx)).await; - - assert!(list.is_ok()); - - let list = list.unwrap(); - - assert!(!list.is_empty()); - - assert_eq!(3, list.len()); - - for uri in list { - println!("invoker uri: {}", uri); - } - } -} diff --git a/dubbo/src/cluster/failover.rs b/dubbo/src/cluster/failover.rs new file mode 100644 index 00000000..1a8149c6 --- /dev/null +++ b/dubbo/src/cluster/failover.rs @@ -0,0 +1,76 @@ +use std::task::Poll; + +use futures_util::future; +use http::Request; +use tower::{ServiceExt, retry::Retry, util::Oneshot}; +use tower_service::Service; + +use crate::StdError; + +pub struct Failover { + inner: N // loadbalancer service +} + +#[derive(Clone)] +pub struct FailoverPolicy; + + +impl Failover { + pub fn new(inner: N) -> Self { + Self { inner } + } +} + +impl tower::retry::Policy, Res, E> for FailoverPolicy +where + B: http_body::Body + Clone, +{ + + type Future = future::Ready; + + fn retry(&self, req: &Request, result: Result<&Res, &E>) -> Option { + //TODO some error handling or logging + match result { + Ok(_) => None, + Err(_) => Some(future::ready(self.clone())) + } + } + + fn clone_request(&self, req: &Request) -> Option> { + let mut clone = http::Request::new(req.body().clone()); + *clone.method_mut() = req.method().clone(); + *clone.uri_mut() = req.uri().clone(); + *clone.headers_mut() = req.headers().clone(); + *clone.version_mut() = req.version(); + + + Some(clone) + } +} + + + +impl Service> for Failover +where + // B is CloneBody + B: http_body::Body + Clone, + // loadbalancer service + N: Service> + Clone + 'static , + N::Error: Into, + N::Future: Send +{ + type Response = N::Response; + + type Error = N::Error; + + type Future = Oneshot, Request>; + + fn poll_ready(&mut self, cx: &mut std::task::Context<'_>) -> Poll> { + self.inner.poll_ready(cx) + } + + fn call(&mut self, req: Request) -> Self::Future { + let retry = Retry::new(FailoverPolicy, self.inner.clone()); + retry.oneshot(req) + } +} \ No newline at end of file diff --git a/dubbo/src/cluster/loadbalance/impls/mod.rs b/dubbo/src/cluster/loadbalance/impls/mod.rs deleted file mode 100644 index 5a84af8c..00000000 --- a/dubbo/src/cluster/loadbalance/impls/mod.rs +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -pub mod random; -pub mod roundrobin; diff --git a/dubbo/src/cluster/loadbalance/impls/random.rs b/dubbo/src/cluster/loadbalance/impls/random.rs deleted file mode 100644 index 3e1cf651..00000000 --- a/dubbo/src/cluster/loadbalance/impls/random.rs +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -use dubbo_base::Url; -use std::{ - fmt::{Debug, Formatter}, - sync::Arc, -}; - -use crate::{ - cluster::loadbalance::types::{LoadBalance, Metadata}, - codegen::RpcInvocation, -}; - -pub struct RandomLoadBalance { - pub metadata: Metadata, -} - -impl Default for RandomLoadBalance { - fn default() -> Self { - RandomLoadBalance { - metadata: Metadata::new("random"), - } - } -} - -impl Debug for RandomLoadBalance { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "RandomLoadBalance") - } -} - -impl LoadBalance for RandomLoadBalance { - fn select( - &self, - invokers: Arc>, - _url: Option, - _invocation: Arc, - ) -> Option { - if invokers.is_empty() { - return None; - } - let index = rand::random::() % invokers.len(); - Some(invokers[index].clone()) - } -} diff --git a/dubbo/src/cluster/loadbalance/impls/roundrobin.rs b/dubbo/src/cluster/loadbalance/impls/roundrobin.rs deleted file mode 100644 index 5fd0ed49..00000000 --- a/dubbo/src/cluster/loadbalance/impls/roundrobin.rs +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -use dubbo_base::Url; -use std::{ - collections::HashMap, - fmt::{Debug, Formatter}, - sync::{ - atomic::{AtomicUsize, Ordering}, - Arc, RwLock, - }, -}; - -use crate::{ - cluster::loadbalance::types::{LoadBalance, Metadata}, - codegen::RpcInvocation, -}; - -pub struct RoundRobinLoadBalance { - pub metadata: Metadata, - pub counter_map: RwLock>, -} - -impl Default for RoundRobinLoadBalance { - fn default() -> Self { - RoundRobinLoadBalance { - metadata: Metadata::new("roundrobin"), - counter_map: RwLock::new(HashMap::new()), - } - } -} - -impl Debug for RoundRobinLoadBalance { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "RoundRobinLoadBalance") - } -} - -impl RoundRobinLoadBalance { - fn guarantee_counter_key(&self, key: &str) { - let contained = self.counter_map.try_read().unwrap().contains_key(key); - if !contained { - self.counter_map - .try_write() - .unwrap() - .insert(key.to_string(), AtomicUsize::new(0)); - } - } -} - -impl LoadBalance for RoundRobinLoadBalance { - fn select( - &self, - invokers: Arc>, - _url: Option, - invocation: Arc, - ) -> Option { - if invokers.is_empty() { - return None; - } - let fingerprint = invocation.unique_fingerprint(); - self.guarantee_counter_key(fingerprint.as_str()); - let index = self - .counter_map - .try_read() - .unwrap() - .get(fingerprint.as_str())? - .fetch_add(1, Ordering::SeqCst) - % invokers.len(); - Some(invokers[index].clone()) - } -} diff --git a/dubbo/src/cluster/loadbalance/mod.rs b/dubbo/src/cluster/loadbalance/mod.rs deleted file mode 100644 index 1d04f7fc..00000000 --- a/dubbo/src/cluster/loadbalance/mod.rs +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -use std::collections::HashMap; - -use lazy_static::lazy_static; - -use crate::cluster::loadbalance::{ - impls::{random::RandomLoadBalance, roundrobin::RoundRobinLoadBalance}, - types::BoxLoadBalance, -}; - -pub mod impls; -pub mod types; - -lazy_static! { - pub static ref LOAD_BALANCE_EXTENSIONS: HashMap = - init_loadbalance_extensions(); -} - -fn init_loadbalance_extensions() -> HashMap { - let mut loadbalance_map: HashMap = HashMap::new(); - loadbalance_map.insert("random".to_string(), Box::new(RandomLoadBalance::default())); - loadbalance_map.insert( - "roundrobin".to_string(), - Box::new(RoundRobinLoadBalance::default()), - ); - loadbalance_map -} diff --git a/dubbo/src/cluster/loadbalance/types.rs b/dubbo/src/cluster/loadbalance/types.rs deleted file mode 100644 index 9273d07e..00000000 --- a/dubbo/src/cluster/loadbalance/types.rs +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -use dubbo_base::Url; -use std::{fmt::Debug, sync::Arc}; - -use crate::codegen::RpcInvocation; - -pub type BoxLoadBalance = Box; - -pub trait LoadBalance: Debug { - fn select( - &self, - invokers: Arc>, - url: Option, - invocation: Arc, - ) -> Option; -} - -pub struct Metadata { - pub name: &'static str, -} - -impl Metadata { - pub fn new(name: &'static str) -> Self { - Metadata { name } - } -} diff --git a/dubbo/src/cluster/mod.rs b/dubbo/src/cluster/mod.rs index 964150d5..47394e2e 100644 --- a/dubbo/src/cluster/mod.rs +++ b/dubbo/src/cluster/mod.rs @@ -15,434 +15,77 @@ * limitations under the License. */ -use std::{fmt::Debug, sync::Arc}; -use dubbo_base::Url; -use thiserror::Error; -use tower::{ready_cache::ReadyCache, ServiceExt}; +use http::Request; use tower_service::Service; -use crate::{ - cluster::router::{ - manager::router_manager::get_global_router_manager, router_chain::RouterChain, - }, - codegen::RpcInvocation, - invocation::Invocation, - protocol::{triple::triple_invoker::TripleInvoker, BoxInvoker, Invoker}, - triple::client::replay::ClonedBody, - StdError, -}; +use crate::{codegen::RpcInvocation, StdError, svc::NewService, param::Param}; -pub mod directory; -pub mod loadbalance; -pub mod router; +use self::{failover::Failover, clone_body::CloneBody}; + +mod clone_body; +mod clone_invoker; +mod failover; -pub trait Directory: Debug { - fn list(&self, inv: Arc) -> Vec; +pub struct NewCluster { + inner: N, // new loadbalancer service } -type BoxDirectory = Box; - -pub trait Cluster { - fn join(&self, dir: BoxDirectory) -> BoxInvoker; -} - -#[derive(Debug, Default)] -pub struct MockCluster {} - -impl Cluster for MockCluster { - fn join(&self, dir: BoxDirectory) -> BoxInvoker { - Box::new(FailoverCluster::new(dir)) - } -} - -// 在Cluster上进行缓存Service -#[derive(Debug)] -pub struct FailoverCluster { - dir: Arc, - caches: ReadyCache>, -} - -impl FailoverCluster { - pub fn new(dir: BoxDirectory) -> FailoverCluster { - Self { - dir: Arc::new(dir), - caches: ReadyCache::default(), - } - } -} - -#[derive(Error, Debug)] -#[error("no available service for {0}")] -pub struct NoAvailableServiceErr(String); - -#[derive(Error, Debug)] -#[error("invalid service name {0}")] -pub struct InvalidServiceNameErr(String); - -impl FailoverCluster { - async fn invoke( - req: http::Request, - mut invoker: BoxInvoker, - ) -> Result, (StdError, http::Request)> { - let clone_request = FailoverCluster::clone_request(&req); - let invoker = invoker - .ready() - .await - .map_err(|e| (e, FailoverCluster::clone_request(&req)))?; - let ret = invoker.call(req).await.map_err(|e| (e, clone_request))?; - - Ok(ret) - } - - fn clone_request(req: &http::Request) -> http::Request { - let mut clone = http::Request::new(req.body().clone()); - *clone.method_mut() = req.method().clone(); - *clone.uri_mut() = req.uri().clone(); - *clone.headers_mut() = req.headers().clone(); - *clone.version_mut() = req.version(); - - if let Some(inv) = req.extensions().get::().cloned() { - clone.extensions_mut().insert(inv); - } - - clone - } +pub struct Cluster { + inner: S // failover service } -impl Service> for FailoverCluster { - type Response = http::Response; - - type Error = crate::Error; - - type Future = crate::BoxFuture; - - fn poll_ready( - &mut self, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - self.caches.poll_pending(cx).map_err(|e| e.into()) - } - fn call(&mut self, req: http::Request) -> Self::Future { - let inv = req.extensions().get::(); - if inv.is_none() { - return Box::pin(async move { - return Err( - InvalidServiceNameErr("service name must not be null".to_owned()).into(), - ); - }); - } - - let inv = inv.unwrap(); - let service_name = inv.get_target_service_unique_name(); - - let invokers = self.dir.list(Arc::new(inv.clone())); - - Box::pin(async move { - let mut current_req = req; - let mut last_err = None; - - let is_empty = invokers.is_empty(); - if is_empty { - return Err(NoAvailableServiceErr(service_name).into()); - } - - for invoker in invokers { - match FailoverCluster::invoke(current_req, invoker).await { - Ok(resp) => return Ok(resp), - Err((e, cloned_request)) => { - current_req = cloned_request; - last_err = Some(e); - } - } +impl NewCluster { + + pub fn layer() -> impl tower_layer::Layer { + tower_layer::layer_fn(|inner: N| { + NewCluster { + inner, // new loadbalancer service } - - if last_err.is_none() { - return Err(NoAvailableServiceErr(service_name).into()); - } - - return Err(last_err.unwrap()); }) } -} -impl Invoker> for FailoverCluster { - fn get_url(&self) -> dubbo_base::Url { - Url::from_url("triple://127.0.0.1:8888/helloworld.Greeter").unwrap() - } -} +} -#[derive(Debug, Default)] -pub struct MockDirectory { - router_chain: RouterChain, -} +impl NewService for NewCluster +where + T: Param, + // new loadbalancer service + S: NewService, +{ -impl MockDirectory { - pub fn new(service_name: String) -> MockDirectory { - let router_chain = get_global_router_manager() - .read() - .unwrap() - .get_router_chain(service_name); - Self { router_chain } - } -} + type Service = Cluster>; + + fn new_service(&self, target: T) -> Self::Service { -impl Directory for MockDirectory { - fn list(&self, inv: Arc) -> Vec { - let u = Url::from_url("triple://127.0.0.1:8888/helloworld.Greeter").unwrap(); - let mut urls = vec![u]; - // tracing::info!("MockDirectory: {}", meta); - urls = self.router_chain.route(urls, inv); - let mut result = Vec::new(); - for url in urls { - result.push(Box::new(TripleInvoker::new(url)) as BoxInvoker); + Cluster { + inner: Failover::new(self.inner.new_service(target)) } - result } } + +impl Service> for Cluster +where + S: Service>>, + B: http_body::Body + Unpin, + B::Error: Into, +{ -#[cfg(test)] -pub mod tests { - use std::{sync::Arc, task::Poll}; - - use bytes::{Buf, BufMut, BytesMut}; - use dubbo_base::Url; - use futures_util::future::poll_fn; - use http::StatusCode; - use http_body::Body; - use thiserror::Error; - use tower::ServiceExt; - use tower_service::Service; - - use crate::{ - boxed, - cluster::FailoverCluster, - codegen::{Invoker, RpcInvocation}, - empty_body, - invocation::Invocation, - triple::client::replay::ClonedBody, - }; - - use super::Directory; + type Response = S::Response; - #[derive(Error, Debug)] - #[error("{0}")] - struct NoResponseErr(String); + type Error = S::Error; - #[derive(Debug)] - struct MockDirectory; + type Future = S::Future; - impl Directory for MockDirectory { - fn list(&self, inv: Arc) -> Vec { - println!( - "get invoker list for {}", - inv.get_target_service_unique_name() - ); - - vec![ - Box::new(MockInvoker(1)), - Box::new(MockInvoker(2)), - Box::new(MockInvoker(3)), - Box::new(MockInvoker(4)), - Box::new(MockInvoker(5)), - ] - } - } - - #[derive(Debug)] - struct MockInvoker(u8); - - impl Invoker> for MockInvoker { - fn get_url(&self) -> dubbo_base::Url { - let str = format!( - "triple://127.0.0.1:8888/failover_cluster_service/{}", - self.0 - ); - Url::from_url(str.as_str()).unwrap() - } + fn poll_ready(&mut self, cx: &mut std::task::Context<'_>) -> std::task::Poll> { + self.inner.poll_ready(cx) } - - impl Service> for MockInvoker { - type Response = http::Response; - - type Error = crate::Error; - - type Future = crate::BoxFuture, crate::Error>; - - fn poll_ready( - &mut self, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - Poll::Ready(Ok(())) - } - - fn call(&mut self, req: http::Request) -> Self::Future { - let inv = req.extensions().get::(); - if inv.is_none() { - return Box::pin(async move { - let response = http::Response::builder() - .status(StatusCode::OK) - .body(empty_body()) - .unwrap(); - - return Ok(response); - }); - } - - let inv = inv.unwrap(); - let method_name = inv.get_method_name(); - if method_name.eq("invoker_request") { - return Box::pin(async move { - let response = http::Response::builder() - .status(StatusCode::OK) - .body(boxed("invoker response".to_owned())) - .unwrap(); - - return Ok(response); - }); - } - - let self_index = self.0; - if method_name.eq("failover_request") { - return Box::pin(async move { - let body = req.into_body(); - let mut pin_body = Box::pin(body); - let ret = poll_fn(|cx| pin_body.as_mut().poll_data(cx)).await; - - if ret.is_none() { - #[derive(Error, Debug)] - #[error("{0}")] - struct BodyIsNoneErr(&'static str); - - return Err(BodyIsNoneErr("body must not be null").into()); - } - - let ret = ret.unwrap(); - - if ret.is_err() { - #[derive(Error, Debug)] - #[error("{0}")] - struct BodyIsErr(&'static str); - return Err(BodyIsErr("body must be ok").into()); - } - - let mut ret = ret.unwrap(); - - let index = ret.get_u8(); - - if index == self_index { - let ret_msg = format!("failover cluster index: {} was invoked", self_index); - let response = http::Response::builder() - .status(StatusCode::OK) - .body(boxed(ret_msg)) - .unwrap(); - - return Ok(response); - } - - #[derive(Error, Debug)] - #[error("{0}")] - struct NotTargetInvoker(String); - - let ret_msg = format!( - "failover cluster index: {} was invoked, but is not target invoker {}", - self_index, index - ); - - println!("{}", ret_msg); - return Err(NotTargetInvoker(ret_msg).into()); - }); - } - - return Box::pin(async move { return Err(NoResponseErr(method_name).into()) }); - } - } - - #[tokio::test] - async fn test_failover_cluster() { - let mut cluster = FailoverCluster::new(Box::new(MockDirectory)); - let cluster = cluster.ready().await; - assert!(cluster.is_ok()); - - let cluster = cluster.unwrap(); - - let empty_stream = futures::stream::empty(); - let cloned_body = ClonedBody::new(empty_stream); - - let rpc_inv = RpcInvocation::default() - .with_service_unique_name("failover_cluster_service".to_owned()) - .with_method_name("invoker_request".to_owned()); - - let req = http::Request::builder() - .extension(rpc_inv) - .body(cloned_body) - .unwrap(); - - let ret = cluster.call(req).await; - assert!(ret.is_ok()); - - let ret = ret.unwrap(); - - assert_eq!(ret.status(), StatusCode::OK); - - let body = ret.into_body(); - - let mut pin = Box::pin(body); - let data = poll_fn(|cx| pin.as_mut().poll_data(cx)).await; - assert!(data.is_some()); - let data = data.unwrap(); - assert!(data.is_ok()); - let data = data.unwrap(); - - assert_eq!( - String::from_utf8(data.to_vec()).unwrap(), - "invoker response" - ) - } - - #[tokio::test] - async fn test_failover_request() { - let mut cluster = FailoverCluster::new(Box::new(MockDirectory)); - let cluster = cluster.ready().await; - assert!(cluster.is_ok()); - - let cluster = cluster.unwrap(); - - let once_stream = futures::stream::once(async { - let mut mut_bytes = BytesMut::default(); - mut_bytes.put_u8(5); - return Ok(mut_bytes.freeze()); - }); - let cloned_body = ClonedBody::new(once_stream); - - let rpc_inv = RpcInvocation::default() - .with_service_unique_name("failover_cluster_service".to_owned()) - .with_method_name("failover_request".to_owned()); - - let req = http::Request::builder() - .extension(rpc_inv) - .body(cloned_body) - .unwrap(); - - let ret = cluster.call(req).await; - assert!(ret.is_ok()); - - let ret = ret.unwrap(); - - assert_eq!(ret.status(), StatusCode::OK); - - let body = ret.into_body(); - - let mut pin = Box::pin(body); - let data = poll_fn(|cx| pin.as_mut().poll_data(cx)).await; - assert!(data.is_some()); - let data = data.unwrap(); - assert!(data.is_ok()); - let data = data.unwrap(); - - let resp_str = String::from_utf8(data.to_vec()).unwrap(); - println!("{}", resp_str); - assert_eq!(resp_str, "failover cluster index: 5 was invoked") + + fn call(&mut self, req: Request) -> Self::Future { + let (parts, body) = req.into_parts(); + let clone_body = CloneBody::new(body); + let req = Request::from_parts(parts, clone_body); + self.inner.call(req) } } diff --git a/dubbo/src/codegen.rs b/dubbo/src/codegen.rs index 412feb91..38f8f1dc 100644 --- a/dubbo/src/codegen.rs +++ b/dubbo/src/codegen.rs @@ -27,11 +27,9 @@ pub use hyper::Body as hyperBody; pub use tower_service::Service; pub use super::{ - cluster::directory::RegistryDirectory, empty_body, invocation::{IntoStreamingRequest, Request, Response, RpcInvocation}, protocol::{triple::triple_invoker::TripleInvoker, Invoker}, - registry::{BoxRegistry, Registry}, triple::{ client::TripleClient, codec::{prost::ProstCodec, Codec}, @@ -46,7 +44,7 @@ pub use super::{ pub use crate::{ filter::{service::FilterService, Filter}, triple::{ - client::builder::{ClientBoxService, ClientBuilder}, + client::builder::ClientBuilder, server::builder::ServerBuilder, transport::connection::Connection, }, diff --git a/dubbo/src/directory/mod.rs b/dubbo/src/directory/mod.rs new file mode 100644 index 00000000..0861d32a --- /dev/null +++ b/dubbo/src/directory/mod.rs @@ -0,0 +1,298 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + use core::panic; +use std::{ + hash::Hash, + task::{Context, Poll}, collections::HashMap, sync::{Arc, Mutex}, +}; + +use crate::{StdError, codegen::RpcInvocation, invocation::Invocation, registry::n_registry::Registry, invoker::NewInvoker, svc::NewService, param::Param}; +use futures_util::future::{poll_fn, self}; +use tokio::{sync::{watch, Notify, mpsc::channel}, select}; +use tokio_stream::wrappers::ReceiverStream; +use tower::{ + discover::{Change, Discover}, buffer::Buffer, +}; + +use tower_service::Service; + +type BufferedDirectory = Buffer, StdError>>>, ()>; + +pub struct NewCachedDirectory +where + N: Registry + Clone + Send + Sync + 'static, +{ + inner: CachedDirectory, RpcInvocation> +} + + +pub struct CachedDirectory +where + // NewDirectory + N: NewService +{ + inner: N, + cache: Arc>> +} + + +pub struct NewDirectory { + // registry + inner: N, +} + + + +#[derive(Clone)] +pub struct Directory +where + D: Discover +{ + rx: watch::Receiver>, + close: Arc +} + + + +impl NewCachedDirectory +where + N: Registry + Clone + Send + Sync + 'static, +{ + + pub fn layer() -> impl tower_layer::Layer { + tower_layer::layer_fn(|inner: N| { + NewCachedDirectory { + // inner is registry + inner: CachedDirectory::new(NewDirectory::new(inner)), + } + }) + } +} + + +impl NewService for NewCachedDirectory +where + T: Param, + // service registry + N: Registry + Clone + Send + Sync + 'static, +{ + type Service = BufferedDirectory; + + fn new_service(&self, target: T) -> Self::Service { + + self.inner.new_service(target.param()) + } +} + + +impl CachedDirectory +where + N: NewService +{ + + pub fn new(inner: N) -> Self { + CachedDirectory { + inner, + cache: Default::default() + } + } +} + + +impl NewService for CachedDirectory +where + T: Param, + // NewDirectory + N: NewService, + // Buffered directory + N::Service: Clone +{ + type Service = N::Service; + + fn new_service(&self, target: T) -> Self::Service { + let rpc_invocation = target.param(); + let service_name = rpc_invocation.get_target_service_unique_name(); + let mut cache = self.cache.lock().expect("cached directory lock failed."); + let value = cache.get(&service_name).map(|val|val.clone()); + match value { + None => { + let new_service = self.inner.new_service(target); + cache.insert(service_name, new_service.clone()); + new_service + }, + Some(value) => value + } + } +} + + +impl NewDirectory { + + pub fn new(inner: N) -> Self { + NewDirectory { + inner + } + } +} + +impl NewService for NewDirectory +where + T: Param, + // service registry + N: Registry + Clone + Send + Sync + 'static, +{ + type Service = BufferedDirectory; + + fn new_service(&self, target: T) -> Self::Service { + + let service_name = target.param().get_target_service_unique_name(); + + let registry = self.inner.clone(); + + let (tx, rx) = channel(1024); + + tokio::spawn(async move { + + let receiver = registry.subscribe(service_name).await; + match receiver { + Err(e) => { + // error!("discover stream error: {}", e); + + }, + Ok(mut receiver) => { + loop { + let change = receiver.recv().await; + match change { + None => { + // debug!("discover stream closed."); + break; + }, + Some(change) => { + let _ = tx.send(change); + } + } + } + } + } + + }); + + Buffer::new(Directory::new(ReceiverStream::new(rx)), 1024) + } + +} + + +impl Directory +where + // Discover + D: Discover + Unpin + Send + 'static, + // the key may be dubbo url + D::Key: Hash + Eq + Clone + Send, + // invoker new service + D::Service: NewService<()> + Clone + Send + Sync, +{ + + pub fn new(discover: D) -> Self { + + let mut discover = Box::pin(discover); + + let (tx, rx) = watch::channel(Vec::new()); + let close = Arc::new(Notify::new()); + let close_clone = close.clone(); + + tokio::spawn(async move { + let mut cache: HashMap = HashMap::new(); + + loop { + let changed = select! { + _ = close_clone.notified() => { + // info!("discover stream closed.") + return; + }, + changed = poll_fn(|cx| discover.as_mut().poll_discover(cx)) => { + changed + } + }; + let Some(changed) = changed else { + // debug!("discover stream closed."); + break; + }; + + match changed { + Err(e) => { + // error!("discover stream error: {}", e); + continue; + }, + Ok(changed) => match changed { + Change::Insert(k, v) => { + cache.insert(k, v); + }, + Change::Remove(k) => { + cache.remove(&k); + } + } + } + + let vec: Vec = cache.values().map(|v|v.clone()).collect(); + let _ = tx.send(vec); + } + + }); + Directory { + rx, + close + } + } +} + + +impl Service<()> for Directory +where + // Discover + D: Discover + Unpin + Send, + // the key may be dubbo url + D::Key: Hash + Eq + Clone + Send, + // invoker new service + D::Service: NewService<()> + Clone + Send + Sync, +{ + type Response = watch::Receiver>; + + type Error = StdError; + + type Future = future::Ready>; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, _: ()) -> Self::Future { + future::ok(self.rx.clone()) + } +} + +impl Drop for Directory +where + D: Discover, +{ + fn drop(&mut self) { + if Arc::strong_count(&self.close) == 1 { + self.close.notify_one(); + } + } +} \ No newline at end of file diff --git a/dubbo/src/invoker/mod.rs b/dubbo/src/invoker/mod.rs new file mode 100644 index 00000000..81bcb68f --- /dev/null +++ b/dubbo/src/invoker/mod.rs @@ -0,0 +1,75 @@ +use dubbo_base::Url; +use tower_service::Service; + +use crate::{codegen::TripleInvoker, param::Param, svc::NewService}; + +#[derive(Clone)] +pub struct NewInvoker { + url: Url +} + +pub enum InvokerComponent { + TripleInvoker(TripleInvoker) +} + + +impl NewInvoker { + pub fn new(url: Url) -> Self { + Self { + url + } + } +} + +impl From for NewInvoker { + fn from(url: String) -> Self { + Self { + url: Url::from_url(&url).unwrap() + } + } +} + +impl Param for NewInvoker { + fn param(&self) -> Url { + self.url.clone() + } +} + +impl NewService<()> for NewInvoker { + type Service = InvokerComponent; + fn new_service(&self, _: ()) -> Self::Service { + // todo create another invoker + InvokerComponent::TripleInvoker(TripleInvoker::new(self.url.clone())) + } +} + + +impl Service> for InvokerComponent +where + B: http_body::Body + Unpin + Send + 'static, + B::Error: Into, + B::Data: Send + Unpin, +{ + type Response = http::Response; + + type Error = crate::Error; + + type Future = crate::BoxFuture; + + fn call(&mut self, req: http::Request) -> Self::Future { + match self { + InvokerComponent::TripleInvoker(invoker) => invoker.call(req), + } + } + + fn poll_ready( + &mut self, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + match self { + InvokerComponent::TripleInvoker(invoker) => >>::poll_ready(invoker, cx), + } + } +} + +// InvokerComponent::TripleInvoker(invoker) => >>::poll_ready(invoker, cx), \ No newline at end of file diff --git a/dubbo/src/lib.rs b/dubbo/src/lib.rs index 63c09d3a..252e01c7 100644 --- a/dubbo/src/lib.rs +++ b/dubbo/src/lib.rs @@ -26,6 +26,12 @@ pub mod registry; pub mod status; pub mod triple; pub mod utils; +pub mod directory; +pub mod route; +pub mod loadbalancer; +pub mod invoker; +pub mod param; +pub mod svc; use http_body::Body; use std::{future::Future, pin::Pin}; diff --git a/dubbo/src/loadbalancer/mod.rs b/dubbo/src/loadbalancer/mod.rs new file mode 100644 index 00000000..822794cc --- /dev/null +++ b/dubbo/src/loadbalancer/mod.rs @@ -0,0 +1,102 @@ +use futures_core::future::BoxFuture; +use tower::{discover::ServiceList, ServiceExt}; +use tower_service::Service; + +use crate::{codegen::RpcInvocation, StdError, svc::NewService, param::Param}; + +pub struct NewLoadBalancer { + inner: N, +} + +#[derive(Clone)] +pub struct LoadBalancer { + inner: S, // Routes service +} + +impl NewLoadBalancer { + + pub fn layer() -> impl tower_layer::Layer{ + + tower_layer::layer_fn(|inner| { + + NewLoadBalancer { + inner // NewRoutes + } + }) + } +} + +impl NewService for NewLoadBalancer +where + T: Param + Clone, + // NewRoutes + N: NewService, +{ + + type Service = LoadBalancer; + + fn new_service(&self, target: T) -> Self::Service { + // Routes service + let svc = self.inner.new_service(target); + + LoadBalancer { + inner: svc + } + + } +} + +impl Service for LoadBalancer +where + Req: Send + 'static, + // Routes service + N: Service<(), Response = Vec> + Clone, + N::Error: Into + Send, + N::Future: Send + 'static, + // new invoker + Nsv: NewService<()> + Send, + Nsv::Service: Service + Send, + // invoker + >::Error: Into + Send, + >::Future: Send + 'static, +{ + + type Response = >::Response; + + type Error = StdError; + + type Future = BoxFuture<'static, Result>; + + fn poll_ready(&mut self, cx: &mut std::task::Context<'_>) -> std::task::Poll> { + self.inner.poll_ready(cx).map_err(Into::into) + + } + + fn call(&mut self, req: Req) -> Self::Future { + let routes = self.inner.call(()); + + let fut = async move { + let routes = routes.await; + + let routes: Vec = match routes { + Err(e) => return Err(Into::::into(e)), + Ok(routes) => routes + }; + + let service_list: Vec<_> = routes.iter().map(|inv| { + let invoker = inv.new_service(()); + tower::load::Constant::new(invoker, 1) + + }).collect(); + + let service_list = ServiceList::new(service_list); + + let p2c = tower::balance::p2c::Balance::new(service_list); + + + p2c.oneshot(req).await + }; + + Box::pin(fut) + } +} diff --git a/dubbo/src/param.rs b/dubbo/src/param.rs new file mode 100644 index 00000000..b57f98eb --- /dev/null +++ b/dubbo/src/param.rs @@ -0,0 +1,12 @@ +pub trait Param { + + fn param(&self) -> T; +} + + +impl Param for T { + + fn param(&self) -> T::Owned { + self.to_owned() + } +} diff --git a/dubbo/src/protocol/mod.rs b/dubbo/src/protocol/mod.rs index c941ae5d..9d35a525 100644 --- a/dubbo/src/protocol/mod.rs +++ b/dubbo/src/protocol/mod.rs @@ -16,17 +16,15 @@ */ use std::{ - fmt::Debug, task::{Context, Poll}, }; use async_trait::async_trait; +use aws_smithy_http::body::SdkBody; use tower_service::Service; use dubbo_base::Url; -use crate::triple::client::replay::ClonedBody; - pub mod server_desc; pub mod triple; @@ -43,18 +41,19 @@ pub trait Exporter { fn unexport(&self); } -pub trait Invoker: Debug + Service { +pub trait Invoker: Service { fn get_url(&self) -> Url; } pub type BoxExporter = Box; pub type BoxInvoker = Box< dyn Invoker< - http::Request, + http::Request, Response = http::Response, Error = crate::Error, Future = crate::BoxFuture, crate::Error>, - > + Send, + > + Send + + Sync, >; pub struct WrapperInvoker(T); diff --git a/dubbo/src/protocol/triple/triple_invoker.rs b/dubbo/src/protocol/triple/triple_invoker.rs index 9f9c7698..685c4344 100644 --- a/dubbo/src/protocol/triple/triple_invoker.rs +++ b/dubbo/src/protocol/triple/triple_invoker.rs @@ -22,19 +22,12 @@ use std::{ }; use tower_service::Service; -use crate::{ - protocol::Invoker, - triple::{ - client::{builder::ClientBoxService, replay::ClonedBody}, - transport::connection::Connection, - }, - utils::boxed_clone::BoxCloneService, -}; +use crate::triple::transport::{connection::Connection, self}; #[derive(Clone)] pub struct TripleInvoker { url: Url, - conn: ClientBoxService, + conn: Connection, } impl TripleInvoker { @@ -42,7 +35,7 @@ impl TripleInvoker { let uri = http::Uri::from_str(&url.to_url()).unwrap(); Self { url, - conn: BoxCloneService::new(Connection::new().with_host(uri)), + conn: Connection::new().with_host(uri), } } } @@ -53,14 +46,19 @@ impl Debug for TripleInvoker { } } -impl Service> for TripleInvoker { +impl Service> for TripleInvoker +where + B: http_body::Body + Unpin + Send + 'static, + B::Error: Into, + B::Data: Send + Unpin, +{ type Response = http::Response; type Error = crate::Error; type Future = crate::BoxFuture; - fn call(&mut self, req: http::Request) -> Self::Future { + fn call(&mut self, req: http::Request) -> Self::Future { self.conn.call(req) } @@ -68,12 +66,7 @@ impl Service> for TripleInvoker { &mut self, cx: &mut std::task::Context<'_>, ) -> std::task::Poll> { - self.conn.poll_ready(cx) + >>::poll_ready(&mut self.conn, cx) } } -impl Invoker> for TripleInvoker { - fn get_url(&self) -> Url { - self.url.clone() - } -} diff --git a/dubbo/src/registry/mod.rs b/dubbo/src/registry/mod.rs index 2a95452a..ab2297f3 100644 --- a/dubbo/src/registry/mod.rs +++ b/dubbo/src/registry/mod.rs @@ -20,6 +20,7 @@ pub mod integration; pub mod memory_registry; pub mod protocol; pub mod types; +pub mod n_registry; use std::{ fmt::{Debug, Formatter}, @@ -28,6 +29,7 @@ use std::{ use dubbo_base::Url; + pub type RegistryNotifyListener = Arc; pub trait Registry { fn register(&mut self, url: Url) -> Result<(), crate::StdError>; diff --git a/dubbo/src/registry/n_registry.rs b/dubbo/src/registry/n_registry.rs new file mode 100644 index 00000000..69300f2a --- /dev/null +++ b/dubbo/src/registry/n_registry.rs @@ -0,0 +1,127 @@ +use std::sync::Arc; + +use async_trait::async_trait; +use dubbo_base::Url; +use tokio::sync::mpsc::{Receiver, channel}; +use tower::discover::Change; + + +use crate::{StdError, invoker::NewInvoker}; + +type DiscoverStream = Receiver, StdError>>; + +#[async_trait] +pub trait Registry { + + async fn register(&self, url: Url) -> Result<(), StdError>; + + async fn unregister(&self, url: Url) -> Result<(), StdError>; + + // todo service_name change to url + async fn subscribe(&self, service_name: String) -> Result; + + async fn unsubscribe(&self, url: Url) -> Result<(), StdError>; +} + +#[derive(Clone)] +pub struct ArcRegistry { + inner: Arc +} + + +pub enum RegistryComponent { + NacosRegistry, + ZookeeperRegistry, + StaticRegistry(StaticRegistry), +} + + +pub struct StaticRegistry { + urls: Vec +} + +impl ArcRegistry { + + pub fn new(registry: impl Registry + Send + Sync + 'static) -> Self { + Self { inner: Arc::new(registry) } + } +} + +#[async_trait] +impl Registry for ArcRegistry { + + async fn register(&self, url: Url) -> Result<(), StdError> { + self.register(url).await + } + + async fn unregister(&self, url: Url) -> Result<(), StdError> { + self.unregister(url).await + } + + async fn subscribe(&self, service_name: String) -> Result { + self.subscribe(service_name).await + } + + async fn unsubscribe(&self, url: Url) -> Result<(), StdError> { + self.unsubscribe(url).await + } +} + + + + +#[async_trait] +impl Registry for RegistryComponent { + async fn register(&self, url: Url) -> Result<(), StdError> { + todo!() + } + + async fn unregister(&self, url: Url) -> Result<(), StdError> { + todo!() + } + + async fn subscribe(&self, service_name: String) -> Result { + todo!() + } + + async fn unsubscribe(&self, url: Url) -> Result<(), StdError> { + todo!() + } +} + + +impl StaticRegistry { + + pub fn new(urls: Vec) -> Self { + Self { + urls + } + } +} + + +#[async_trait] +impl Registry for StaticRegistry { + async fn register(&self, url: Url) -> Result<(), StdError> { + todo!() + } + + async fn unregister(&self, url: Url) -> Result<(), StdError> { + todo!() + } + + async fn subscribe(&self, service_name: String) -> Result { + let (tx, rx) = channel(self.urls.len()); + for url in self.urls.iter() { + let invoker = NewInvoker::new(url.clone()); + let change = Ok(Change::Insert(service_name.clone(), invoker)); + tx.send(change).await?; + } + + Ok(rx) + } + + async fn unsubscribe(&self, url: Url) -> Result<(), StdError> { + todo!() + } +} \ No newline at end of file diff --git a/dubbo/src/route/mod.rs b/dubbo/src/route/mod.rs new file mode 100644 index 00000000..dfbe73e3 --- /dev/null +++ b/dubbo/src/route/mod.rs @@ -0,0 +1,196 @@ +use std::{sync::{Arc, Mutex}, collections::HashMap}; + +use futures_core::{Future, ready}; +use futures_util::future::Ready; +use pin_project::pin_project; +use tokio::{sync::watch, pin}; +use tower::{util::FutureService, buffer::Buffer}; +use tower_service::Service; + +use crate::{StdError, codegen::RpcInvocation, svc::NewService, param::Param, invocation::Invocation}; + +pub struct NewRoutes { + inner: N, +} + +pub struct NewRoutesCache +where + N: NewService +{ + inner: N, + cache: Arc>>, +} + +#[pin_project] +pub struct NewRoutesFuture { + #[pin] + inner: N, + target: T, +} + +#[derive(Clone)] +pub struct Routes { + target: T, + new_invokers: Vec, + invokers_receiver: watch::Receiver>, +} + +impl NewRoutes { + pub fn new(inner: N) -> Self { + Self { + inner, + } + } +} + + +impl NewService for NewRoutes +where + T: Param + Clone + Send + 'static, + // NewDirectory + N: NewService, + // Directory + N::Service: Service<(), Response = watch::Receiver>> + Unpin + Send + 'static, + >::Error: Into, + // new invoker service + Nsv: NewService<()> + Clone + Send + Sync + 'static, +{ + + type Service = Buffer>::Service, T>, Routes>, ()>; + + fn new_service(&self, target: T) -> Self::Service { + let inner = self.inner.new_service(target.clone()); + + Buffer::new(FutureService::new(NewRoutesFuture { + inner, + target, + }), 1024) + } +} +impl NewRoutesCache +where + N: NewService, + >::Service: Service<(), Response = watch::Receiver>> + Unpin + Send + 'static, + >::Error: Into, + Nsv: NewService<()> + Clone + Send + Sync + 'static, + +{ + pub fn layer() -> impl tower_layer::Layer>> { + tower_layer::layer_fn(|inner: N| { + NewRoutesCache::new(NewRoutes::new(inner)) + }) + } + + +} + + +impl NewRoutesCache +where + N: NewService +{ + pub fn new(inner: N) -> Self { + Self { + inner, + cache: Default::default(), + } + } +} + +impl NewService for NewRoutesCache +where + T: Param, + N: NewService, + N::Service: Clone, +{ + type Service = N::Service; + + fn new_service(&self, target: T) -> Self::Service { + let rpc_inv = target.param(); + let service_name = rpc_inv.get_target_service_unique_name(); + + let mut cache = self.cache.lock().expect("RoutesCache get lock failed"); + + let service = cache.get(&service_name); + match service { + Some(service) => service.clone(), + None => { + let service = self.inner.new_service(rpc_inv); + cache.insert(service_name, service.clone()); + service + } + } + } +} + + +impl Future for NewRoutesFuture +where + T: Param + Clone, + // Directory + N: Service<(), Response = watch::Receiver>> + Unpin, + N::Error: Into, + // new invoker service + Nsv: NewService<()> + Clone, +{ + type Output = Result, StdError>; + + fn poll(self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> std::task::Poll { + + let this = self.get_mut(); + + let target = this.target.clone(); + + let _ = ready!(this.inner.poll_ready(cx)).map_err(Into::into)?; + + + let call = this.inner.call(()); + pin!(call); + + let mut invokers_receiver = ready!(call.poll(cx).map_err(Into::into))?; + let new_invokers = { + let wait_for = invokers_receiver.wait_for(|invs|!invs.is_empty()); + pin!(wait_for); + + let changed = ready!(wait_for.poll(cx))?; + + changed.clone() + }; + + + std::task::Poll::Ready(Ok(Routes { + invokers_receiver, + new_invokers, + target, + })) + } +} + + + +impl Service<()> for Routes +where + T: Param + Clone, + // new invoker service + Nsv: NewService<()> + Clone, +{ + type Response = Vec; + + type Error = StdError; + + type Future = Ready>; + + fn poll_ready(&mut self, _: &mut std::task::Context<'_>) -> std::task::Poll> { + let has_change = self.invokers_receiver.has_changed()?; + if has_change { + self.new_invokers = self.invokers_receiver.borrow_and_update().clone(); + } + std::task::Poll::Ready(Ok(())) + } + + fn call(&mut self, _: ()) -> Self::Future { + // some router operator + // if new_invokers changed, send new invokers to routes_rx after router operator + futures_util::future::ok(self.new_invokers.clone()) + } +} \ No newline at end of file diff --git a/dubbo/src/svc.rs b/dubbo/src/svc.rs new file mode 100644 index 00000000..56dc3304 --- /dev/null +++ b/dubbo/src/svc.rs @@ -0,0 +1,88 @@ +use std::{sync::Arc, pin::Pin, marker::PhantomData}; + +use futures_core::Future; +use tower::ServiceExt; +use tower_service::Service; + +pub trait NewService { + + type Service; + + fn new_service(&self, target: T) -> Self::Service; + +} + + +pub struct ArcNewService { + inner: Arc + Send + Sync>, +} + + +impl ArcNewService { + pub fn layer() -> impl tower_layer::Layer + Clone + Copy + where + N: NewService + Send + Sync + 'static, + S: Send + 'static, + { + tower_layer::layer_fn(Self::new) + } + + pub fn new(inner: N) -> Self + where + N: NewService + Send + Sync + 'static, + S: Send + 'static, + { + Self { + inner: Arc::new(inner), + } + } +} + +impl Clone for ArcNewService { + fn clone(&self) -> Self { + Self { + inner: self.inner.clone(), + } + } +} + +impl NewService for ArcNewService { + type Service = S; + + fn new_service(&self, t: T) -> S { + self.inner.new_service(t) + } +} + +// inner: Box> + Send>>> + Send>, +pub struct BoxedService { + inner: N, + _mark: PhantomData +} + +impl BoxedService { + + pub fn layer() -> impl tower_layer::Layer{ + tower_layer::layer_fn(|inner: N| { + Self { + inner, + _mark: PhantomData + } + }) + } +} + + +// impl NewService for BoxedService +// where +// N: NewService, +// N::Service: Service + Send, +// >::Future: Send + 'static, +// { +// type Service = Box>::Response, Error = >::Error, Future = Pin>::Response, >::Error>> + Send>>> + Send>; + +// fn new_service(&self, target: T) -> Self::Service { +// let service = self.inner.new_service(target); +// Box::new(service.map_future(|f|Box::pin(f) as _)) +// } +// } diff --git a/dubbo/src/triple/client/builder.rs b/dubbo/src/triple/client/builder.rs index a9756f7e..89d7a00a 100644 --- a/dubbo/src/triple/client/builder.rs +++ b/dubbo/src/triple/client/builder.rs @@ -15,27 +15,28 @@ * limitations under the License. */ + use std::sync::Arc; use crate::{ - cluster::{directory::StaticDirectory, Cluster, Directory, MockCluster, MockDirectory}, - codegen::{RegistryDirectory, TripleInvoker}, - protocol::BoxInvoker, - utils::boxed_clone::BoxCloneService, + utils::boxed_clone::BoxCloneService, registry::n_registry::{RegistryComponent, StaticRegistry, ArcRegistry}, route::{NewRoutes, NewRoutesCache}, loadbalancer::NewLoadBalancer, cluster::{NewCluster, Cluster}, directory::NewCachedDirectory, svc::{ArcNewService, NewService, BoxedService}, StdError, codegen::RpcInvocation, BoxBody, }; -use dubbo_base::Url; -use super::replay::ClonedBody; +use aws_smithy_http::body::SdkBody; +use dubbo_base::Url; +use tower::ServiceBuilder; pub type ClientBoxService = - BoxCloneService, http::Response, crate::Error>; + BoxCloneService, http::Response, crate::Error>; + -#[allow(dead_code)] -#[derive(Clone, Debug, Default)] +pub type ServiceMK = Arc>>>>>; + +#[derive(Default)] pub struct ClientBuilder { pub timeout: Option, pub connector: &'static str, - directory: Option>>, + registry: Option, pub direct: bool, host: String, } @@ -45,7 +46,7 @@ impl ClientBuilder { ClientBuilder { timeout: None, connector: "", - directory: None, + registry: None, direct: false, host: "".to_string(), } @@ -55,7 +56,7 @@ impl ClientBuilder { Self { timeout: None, connector: "", - directory: Some(Arc::new(Box::new(StaticDirectory::new(&host)))), + registry: Some(ArcRegistry::new(RegistryComponent::StaticRegistry(StaticRegistry::new(vec![Url::from_url(host).unwrap()])))), direct: true, host: host.to_string(), } @@ -68,31 +69,23 @@ impl ClientBuilder { } } - /// host: http://0.0.0.0:8888 - pub fn with_directory(self, directory: Box) -> Self { - Self { - directory: Some(Arc::new(directory)), - ..self - } - } - - pub fn with_registry_directory(self, registry: RegistryDirectory) -> Self { - Self { - directory: Some(Arc::new(Box::new(registry))), + pub fn with_registry(self, registry: RegistryComponent) -> Self { + Self { + registry: Some(ArcRegistry::new(registry)), ..self } } pub fn with_host(self, host: &'static str) -> Self { Self { - directory: Some(Arc::new(Box::new(StaticDirectory::new(&host)))), + registry: Some(ArcRegistry::new(RegistryComponent::StaticRegistry(StaticRegistry::new(vec![Url::from_url(host).unwrap()])))), ..self } } pub fn with_connector(self, connector: &'static str) -> Self { Self { - connector: connector, + connector, ..self } } @@ -101,15 +94,18 @@ impl ClientBuilder { Self { direct, ..self } } - pub fn build(self, service_name: String) -> Option { - if self.direct { - return Some(Box::new(TripleInvoker::new( - Url::from_url(&self.host).unwrap(), - ))); - } + pub fn build(mut self) -> ServiceMK { + + + let registry = self.registry.take().expect("registry must not be empty"); - let cluster = MockCluster::default().join(Box::new(MockDirectory::new(service_name))); + let mk_service = ServiceBuilder::new() + .layer(NewCluster::layer()) + .layer(NewLoadBalancer::layer()) + .layer(NewRoutesCache::layer()) + .layer(NewCachedDirectory::layer()) + .service(registry); - return Some(cluster); + Arc::new(mk_service) } } diff --git a/dubbo/src/triple/client/mod.rs b/dubbo/src/triple/client/mod.rs index 013d9e30..7a8e0131 100644 --- a/dubbo/src/triple/client/mod.rs +++ b/dubbo/src/triple/client/mod.rs @@ -16,6 +16,6 @@ */ pub mod builder; -pub mod replay; pub mod triple; + pub use triple::TripleClient; diff --git a/dubbo/src/triple/client/replay.rs b/dubbo/src/triple/client/replay.rs deleted file mode 100644 index 195d7509..00000000 --- a/dubbo/src/triple/client/replay.rs +++ /dev/null @@ -1,441 +0,0 @@ -use std::{ - collections::VecDeque, - pin::Pin, - sync::{Arc, Mutex}, - task::{Context, Poll}, -}; - -use bytes::{Buf, BufMut, Bytes, BytesMut}; -use futures_core::{ready, stream::BoxStream, Stream}; -use futures_util::StreamExt; -use http_body::Body; -use pin_project::pin_project; - -use crate::status::Status; - -type BoxBytesStream = BoxStream<'static, Result>; -pub struct ClonedBytesStream { - shared: Arc>>, - owned: Option, - replay: bool, -} - -pub struct OwnedBytesStream { - bytes_stream: BoxBytesStream, - buf: InnerBuffer, -} - -#[derive(Clone)] -pub struct InnerBuffer { - bufs: VecDeque, - capacity: usize, -} - -impl ClonedBytesStream { - pub fn new(buffer_capacity: usize, stream: BoxBytesStream) -> Self { - Self { - shared: Arc::new(Mutex::new(None)), - owned: Some(OwnedBytesStream { - bytes_stream: stream, - buf: InnerBuffer { - bufs: Default::default(), - capacity: buffer_capacity, - }, - }), - replay: false, - } - } -} - -impl Clone for ClonedBytesStream { - fn clone(&self) -> Self { - Self { - shared: self.shared.clone(), - owned: None, - replay: true, - } - } -} - -impl Drop for ClonedBytesStream { - fn drop(&mut self) { - if let Some(owned) = self.owned.take() { - let lock = self.shared.lock(); - if let Ok(mut lock) = lock { - *lock = Some(owned); - } - } - } -} - -impl InnerBuffer { - pub fn push_bytes(&mut self, bytes: Bytes) -> Bytes { - self.bufs.push_back(bytes.clone()); - bytes - } - - pub fn is_capped(&self) -> bool { - self.capacity == 0 - } -} - -impl Buf for InnerBuffer { - fn remaining(&self) -> usize { - self.bufs.iter().map(|bytes| bytes.remaining()).sum() - } - - fn chunk(&self) -> &[u8] { - self.bufs.front().map(Buf::chunk).unwrap_or(&[]) - } - - fn chunks_vectored<'a>(&'a self, dst: &mut [std::io::IoSlice<'a>]) -> usize { - if dst.is_empty() { - return 0; - } - - let mut filled = 0; - - for bytes in self.bufs.iter() { - filled += bytes.chunks_vectored(&mut dst[filled..]) - } - - filled - } - - fn advance(&mut self, mut cnt: usize) { - while cnt > 0 { - let first = self.bufs.front_mut(); - if first.is_none() { - break; - } - let first = first.unwrap(); - let first_remaining = first.remaining(); - if first_remaining > cnt { - first.advance(cnt); - break; - } - - first.advance(first_remaining); - cnt = cnt - first_remaining; - self.bufs.pop_front(); - } - } - - fn copy_to_bytes(&mut self, len: usize) -> bytes::Bytes { - match self.bufs.front_mut() { - Some(buf) if len <= buf.remaining() => { - let bytes = buf.copy_to_bytes(len); - if buf.remaining() == 0 { - self.bufs.pop_front(); - } - bytes - } - _ => { - let mut bytes = BytesMut::with_capacity(len); - bytes.put(self.take(len)); - bytes.freeze() - } - } - } -} - -pub enum BytesData { - BufferedBytes(InnerBuffer), - OriginBytes(Bytes), -} - -impl Buf for BytesData { - fn remaining(&self) -> usize { - match self { - BytesData::BufferedBytes(bytes) => bytes.remaining(), - BytesData::OriginBytes(bytes) => bytes.remaining(), - } - } - - fn chunk(&self) -> &[u8] { - match self { - BytesData::BufferedBytes(bytes) => bytes.chunk(), - BytesData::OriginBytes(bytes) => bytes.chunk(), - } - } - - fn advance(&mut self, cnt: usize) { - match self { - BytesData::BufferedBytes(bytes) => bytes.advance(cnt), - BytesData::OriginBytes(bytes) => bytes.advance(cnt), - } - } - - fn copy_to_bytes(&mut self, len: usize) -> bytes::Bytes { - match self { - BytesData::BufferedBytes(bytes) => bytes.copy_to_bytes(len), - BytesData::OriginBytes(bytes) => bytes.copy_to_bytes(len), - } - } -} - -impl Stream for ClonedBytesStream { - type Item = Result; - - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let mut_self = self.get_mut(); - - let owned_bytes_stream = mut_self.owned.get_or_insert_with(|| { - let lock = mut_self.shared.lock(); - if let Err(e) = lock { - panic!("bytes streams get shared data lock failed. {}", e); - } - let mut data = lock.unwrap(); - - data.take().expect("cannot get shared bytes streams.") - }); - - if mut_self.replay { - mut_self.replay = false; - if owned_bytes_stream.buf.has_remaining() { - return Poll::Ready(Some(Ok(BytesData::BufferedBytes( - owned_bytes_stream.buf.clone(), - )))); - } - } - - let next = owned_bytes_stream.bytes_stream.poll_next_unpin(cx); - - let next = match ready!(next) { - Some(next) => match next { - Ok(next) => next, - Err(e) => return Poll::Ready(Some(Err(e))), - }, - _ => return Poll::Ready(None), - }; - - let len = next.len(); - owned_bytes_stream.buf.capacity = owned_bytes_stream.buf.capacity.saturating_sub(len); - let next = if owned_bytes_stream.buf.is_capped() { - if owned_bytes_stream.buf.has_remaining() { - owned_bytes_stream.buf.bufs = VecDeque::default(); - } - next - } else { - owned_bytes_stream.buf.push_bytes(next) - }; - - return Poll::Ready(Some(Ok(BytesData::OriginBytes(next)))); - } -} - -#[pin_project] -#[derive(Clone)] -pub struct ClonedBody(#[pin] ClonedBytesStream); - -impl ClonedBody { - pub fn new(inner_body: T) -> Self - where - T: Stream> + Send + 'static, - { - let inner_body = Box::pin(inner_body); - let inner_body = ClonedBytesStream::new(1024 * 64, inner_body); - ClonedBody(inner_body) - } -} - -impl Body for ClonedBody { - type Data = BytesData; - - type Error = Status; - - fn poll_data( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll>> { - self.project().0.poll_next(cx) - } - - fn poll_trailers( - self: Pin<&mut Self>, - _cx: &mut Context<'_>, - ) -> Poll, Self::Error>> { - Poll::Ready(Ok(None)) - } - - fn size_hint(&self) -> http_body::SizeHint { - http_body::SizeHint::default() - } -} - -#[cfg(test)] -pub mod tests { - use super::*; - - #[tokio::test] - async fn test_cloned_bytes_stream() { - let bytes1 = Ok(Bytes::from("hello".as_bytes())); - let bytes2 = Ok(Bytes::from(" world".as_bytes())); - let bytes3 = Ok(Bytes::from(", end test!".as_bytes())); - - let stream = futures_util::stream::iter(vec![bytes1, bytes2, bytes3]); - - let mut origin_stream = ClonedBytesStream::new(64 * 1024, Box::pin(stream)); - let hello_bytes = origin_stream.next().await; - - assert!(hello_bytes.is_some()); - - let hello_bytes = hello_bytes.unwrap(); - - assert!(hello_bytes.is_ok()); - - assert_eq!(bytes_data_to_str(hello_bytes.unwrap()), "hello"); - - let world_bytes = origin_stream.next().await; - - assert!(world_bytes.is_some()); - - let world_bytes = world_bytes.unwrap(); - - assert!(world_bytes.is_ok()); - - assert_eq!(bytes_data_to_str(world_bytes.unwrap()), " world"); - - let end_bytes = origin_stream.next().await; - - assert!(end_bytes.is_some()); - - let end_bytes = end_bytes.unwrap(); - - assert!(end_bytes.is_ok()); - - assert_eq!(bytes_data_to_str(end_bytes.unwrap()), ", end test!"); - - let none_bytes = origin_stream.next().await; - - assert!(none_bytes.is_none()); - } - - #[tokio::test] - async fn test_cloned_bytes_stream_and_replay() { - let bytes1 = Ok(Bytes::from("hello".as_bytes())); - let bytes2 = Ok(Bytes::from(" world".as_bytes())); - let bytes3 = Ok(Bytes::from(", end test!".as_bytes())); - - let stream = futures_util::stream::iter(vec![bytes1, bytes2, bytes3]); - - let mut origin_stream = ClonedBytesStream::new(64 * 1024, Box::pin(stream)); - origin_stream.next().await; - origin_stream.next().await; - origin_stream.next().await; - - let none_bytes = origin_stream.next().await; - assert!(none_bytes.is_none()); - - let mut clone_origin_stream = origin_stream.clone(); - - drop(origin_stream); - - let cached_bytes = clone_origin_stream.next().await; - assert!(cached_bytes.is_some()); - - let cached_bytes = cached_bytes.unwrap(); - - assert!(cached_bytes.is_ok()); - - assert_eq!( - bytes_data_to_str(cached_bytes.unwrap()), - "hello world, end test!" - ); - - let none_bytes = clone_origin_stream.next().await; - assert!(none_bytes.is_none()); - } - - #[tokio::test] - async fn test_replay_stream_continue_poll_next() { - let bytes1 = Ok(Bytes::from("hello".as_bytes())); - let bytes2 = Ok(Bytes::from(" world".as_bytes())); - let bytes3 = Ok(Bytes::from(", end test!".as_bytes())); - - let stream = futures_util::stream::iter(vec![bytes1, bytes2, bytes3]); - - let mut origin_stream = ClonedBytesStream::new(1024 * 64, Box::pin(stream)); - origin_stream.next().await; - origin_stream.next().await; - - let mut clone_origin_stream = origin_stream.clone(); - - drop(origin_stream); - - let cached_bytes = clone_origin_stream.next().await; - assert!(cached_bytes.is_some()); - - let cached_bytes = cached_bytes.unwrap(); - - assert!(cached_bytes.is_ok()); - - assert_eq!(bytes_data_to_str(cached_bytes.unwrap()), "hello world"); - - let next_bytes = clone_origin_stream.next().await; - - assert!(next_bytes.is_some()); - - let next_bytes = next_bytes.unwrap(); - - assert!(next_bytes.is_ok()); - - assert_eq!(bytes_data_to_str(next_bytes.unwrap()), ", end test!"); - - let none_bytes = clone_origin_stream.next().await; - assert!(none_bytes.is_none()); - } - - #[tokio::test] - async fn test_cloned_bytes_stream_reached_max_capacity() { - let bytes1 = Ok(Bytes::from("hello".as_bytes())); - let bytes2 = Ok(Bytes::from(" world".as_bytes())); - let bytes3 = Ok(Bytes::from(", end test!".as_bytes())); - - let stream = futures_util::stream::iter(vec![bytes1, bytes2, bytes3]); - - let mut origin_stream = ClonedBytesStream::new(5, Box::pin(stream)); - let hello_bytes = origin_stream.next().await; - - assert!(hello_bytes.is_some()); - - let hello_bytes = hello_bytes.unwrap(); - - assert!(hello_bytes.is_ok()); - - assert_eq!(bytes_data_to_str(hello_bytes.unwrap()), "hello"); - - let world_bytes = origin_stream.next().await; - - assert!(world_bytes.is_some()); - - let world_bytes = world_bytes.unwrap(); - - assert!(world_bytes.is_ok()); - - assert_eq!(bytes_data_to_str(world_bytes.unwrap()), " world"); - - let mut cloned_origin_stream = origin_stream.clone(); - - drop(origin_stream); - - let end_bytes = cloned_origin_stream.next().await; - - assert!(end_bytes.is_some()); - - let end_bytes = end_bytes.unwrap(); - - assert!(end_bytes.is_ok()); - - assert_eq!(bytes_data_to_str(end_bytes.unwrap()), ", end test!"); - - let none_bytes = cloned_origin_stream.next().await; - assert!(none_bytes.is_none()); - } - - fn bytes_data_to_str(mut bytes_data: BytesData) -> String { - let len = bytes_data.remaining(); - let bytes = bytes_data.copy_to_bytes(len); - String::from_utf8(bytes.to_vec()).unwrap() - } -} diff --git a/dubbo/src/triple/client/triple.rs b/dubbo/src/triple/client/triple.rs index ace7aafd..091e0207 100644 --- a/dubbo/src/triple/client/triple.rs +++ b/dubbo/src/triple/client/triple.rs @@ -15,40 +15,43 @@ * limitations under the License. */ -use std::str::FromStr; use futures_util::{future, stream, StreamExt, TryStreamExt}; +use aws_smithy_http::body::SdkBody; use http::HeaderValue; +use tower_service::Service; -use super::{builder::ClientBuilder, replay::ClonedBody}; +use super::builder::{ClientBuilder, ServiceMK}; use crate::codegen::RpcInvocation; +use crate::svc::NewService; use crate::{ - invocation::{IntoStreamingRequest, Invocation, Metadata, Request, Response}, + invocation::{IntoStreamingRequest, Metadata, Request, Response}, triple::{codec::Codec, compression::CompressionEncoding, decode::Decoding, encode::encode}, }; -#[derive(Debug, Default, Clone)] +#[derive(Clone)] pub struct TripleClient { pub(crate) send_compression_encoding: Option, - pub(crate) builder: Option, + pub(crate) mk: ServiceMK, } impl TripleClient { pub fn connect(host: String) -> Self { let builder = ClientBuilder::from_static(&host).with_direct(true); + let mk = builder.build(); TripleClient { send_compression_encoding: Some(CompressionEncoding::Gzip), - builder: Some(builder), + mk, } } pub fn new(builder: ClientBuilder) -> Self { TripleClient { send_compression_encoding: Some(CompressionEncoding::Gzip), - builder: Some(builder), + mk: builder.build(), } } @@ -56,9 +59,8 @@ impl TripleClient { &self, uri: http::Uri, path: http::uri::PathAndQuery, - body: ClonedBody, - invocation: RpcInvocation, - ) -> http::Request { + body: SdkBody, + ) -> http::Request { let mut parts = uri.into_parts(); parts.path_and_query = Some(path); @@ -110,8 +112,6 @@ impl TripleClient { http::HeaderValue::from_static("gzip"), ); - req.extensions_mut().insert(invocation); - // const ( // TripleContentType = "application/grpc+proto" // TripleUserAgent = "grpc-go/1.35.0-dev" @@ -140,26 +140,25 @@ impl TripleClient { M2: Send + Sync + 'static, { let req = req.map(|m| stream::once(future::ready(m))); - let en = encode( + let body_stream = encode( codec.encoder(), req.into_inner().map(Ok), self.send_compression_encoding, ) .into_stream(); - let body = ClonedBody::new(en); + let body = hyper::Body::wrap_stream(body_stream); + + + let mut invoker = self.mk.new_service(invocation); - let mut conn = self - .builder - .clone() - .unwrap() - .build(invocation.get_target_service_unique_name()) - .unwrap(); - let http_uri = http::Uri::from_str(&conn.get_url().to_url()).unwrap(); - let req = self.map_request(http_uri.clone(), path, body, invocation); + let request = http::Request::builder() + .body(body).unwrap(); - let response = conn - .call(req) + + + let response = invoker + .call(request) .await .map_err(|err| crate::status::Status::from_error(err.into())); @@ -210,21 +209,17 @@ impl TripleClient { self.send_compression_encoding, ) .into_stream(); + let body = hyper::Body::wrap_stream(en); + + let mut invoker = self.mk.new_service(invocation); - let body = ClonedBody::new(en); - let mut conn = self - .builder - .clone() - .unwrap() - .build(invocation.get_target_service_unique_name()) - .unwrap(); + let request = http::Request::builder() + .body(body).unwrap(); - let http_uri = http::Uri::from_str(&conn.get_url().to_url()).unwrap(); - let req = self.map_request(http_uri.clone(), path, body, invocation); - let response = conn - .call(req) + let response = invoker + .call(request) .await .map_err(|err| crate::status::Status::from_error(err.into())); @@ -259,21 +254,18 @@ impl TripleClient { self.send_compression_encoding, ) .into_stream(); - let body = ClonedBody::new(en); + let body = hyper::Body::wrap_stream(en); + let mut invoker = self.mk.new_service(invocation); + + + let request = http::Request::builder() + .body(body).unwrap(); - let mut conn = self - .builder - .clone() - .unwrap() - .build(invocation.get_target_service_unique_name()) - .unwrap(); - let http_uri = http::Uri::from_str(&conn.get_url().to_url()).unwrap(); - let req = self.map_request(http_uri.clone(), path, body, invocation); // let mut conn = Connection::new().with_host(http_uri); - let response = conn - .call(req) + let response = invoker + .call(request) .await .map_err(|err| crate::status::Status::from_error(err.into())); @@ -324,21 +316,16 @@ impl TripleClient { self.send_compression_encoding, ) .into_stream(); + let body = hyper::Body::wrap_stream(en); + let mut invoker = self.mk.new_service(invocation); - let body = ClonedBody::new(en); - let mut conn = self - .builder - .clone() - .unwrap() - .build(invocation.get_target_service_unique_name()) - .unwrap(); + let request = http::Request::builder() + .body(body).unwrap(); - let http_uri = http::Uri::from_str(&conn.get_url().to_url()).unwrap(); - let req = self.map_request(http_uri.clone(), path, body, invocation); - let response = conn - .call(req) + let response = invoker + .call(request) .await .map_err(|err| crate::status::Status::from_error(err.into())); diff --git a/examples/echo/src/generated/grpc.examples.echo.rs b/examples/echo/src/generated/grpc.examples.echo.rs index 0701ec3b..e756d5c7 100644 --- a/examples/echo/src/generated/grpc.examples.echo.rs +++ b/examples/echo/src/generated/grpc.examples.echo.rs @@ -36,12 +36,16 @@ pub mod echo_client { &mut self, request: Request, ) -> Result, dubbo::status::Status> { - let codec = - dubbo::codegen::ProstCodec::::default(); + let codec = dubbo::codegen::ProstCodec::< + super::EchoRequest, + super::EchoResponse, + >::default(); let invocation = RpcInvocation::default() .with_service_unique_name(String::from("grpc.examples.echo.Echo")) .with_method_name(String::from("UnaryEcho")); - let path = http::uri::PathAndQuery::from_static("/grpc.examples.echo.Echo/UnaryEcho"); + let path = http::uri::PathAndQuery::from_static( + "/grpc.examples.echo.Echo/UnaryEcho", + ); self.inner.unary(request, codec, path, invocation).await } /// ServerStreamingEcho is server side streaming. @@ -49,51 +53,51 @@ pub mod echo_client { &mut self, request: Request, ) -> Result>, dubbo::status::Status> { - let codec = - dubbo::codegen::ProstCodec::::default(); + let codec = dubbo::codegen::ProstCodec::< + super::EchoRequest, + super::EchoResponse, + >::default(); let invocation = RpcInvocation::default() .with_service_unique_name(String::from("grpc.examples.echo.Echo")) .with_method_name(String::from("ServerStreamingEcho")); let path = http::uri::PathAndQuery::from_static( "/grpc.examples.echo.Echo/ServerStreamingEcho", ); - self.inner - .server_streaming(request, codec, path, invocation) - .await + self.inner.server_streaming(request, codec, path, invocation).await } /// ClientStreamingEcho is client side streaming. pub async fn client_streaming_echo( &mut self, request: impl IntoStreamingRequest, ) -> Result, dubbo::status::Status> { - let codec = - dubbo::codegen::ProstCodec::::default(); + let codec = dubbo::codegen::ProstCodec::< + super::EchoRequest, + super::EchoResponse, + >::default(); let invocation = RpcInvocation::default() .with_service_unique_name(String::from("grpc.examples.echo.Echo")) .with_method_name(String::from("ClientStreamingEcho")); let path = http::uri::PathAndQuery::from_static( "/grpc.examples.echo.Echo/ClientStreamingEcho", ); - self.inner - .client_streaming(request, codec, path, invocation) - .await + self.inner.client_streaming(request, codec, path, invocation).await } /// BidirectionalStreamingEcho is bidi streaming. pub async fn bidirectional_streaming_echo( &mut self, request: impl IntoStreamingRequest, ) -> Result>, dubbo::status::Status> { - let codec = - dubbo::codegen::ProstCodec::::default(); + let codec = dubbo::codegen::ProstCodec::< + super::EchoRequest, + super::EchoResponse, + >::default(); let invocation = RpcInvocation::default() .with_service_unique_name(String::from("grpc.examples.echo.Echo")) .with_method_name(String::from("BidirectionalStreamingEcho")); let path = http::uri::PathAndQuery::from_static( "/grpc.examples.echo.Echo/BidirectionalStreamingEcho", ); - self.inner - .bidi_streaming(request, codec, path, invocation) - .await + self.inner.bidi_streaming(request, codec, path, invocation).await } } } @@ -110,7 +114,9 @@ pub mod echo_server { request: Request, ) -> Result, dubbo::status::Status>; ///Server streaming response type for the ServerStreamingEcho method. - type ServerStreamingEchoStream: futures_util::Stream> + type ServerStreamingEchoStream: futures_util::Stream< + Item = Result, + > + Send + 'static; /// ServerStreamingEcho is server side streaming. @@ -124,14 +130,19 @@ pub mod echo_server { request: Request>, ) -> Result, dubbo::status::Status>; ///Server streaming response type for the BidirectionalStreamingEcho method. - type BidirectionalStreamingEchoStream: futures_util::Stream> + type BidirectionalStreamingEchoStream: futures_util::Stream< + Item = Result, + > + Send + 'static; /// BidirectionalStreamingEcho is bidi streaming. async fn bidirectional_streaming_echo( &self, request: Request>, - ) -> Result, dubbo::status::Status>; + ) -> Result< + Response, + dubbo::status::Status, + >; } /// Echo is the echo service. #[derive(Debug)] @@ -161,7 +172,10 @@ pub mod echo_server { type Response = http::Response; type Error = std::convert::Infallible; type Future = BoxFuture; - fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { + fn poll_ready( + &mut self, + _cx: &mut Context<'_>, + ) -> Poll> { Poll::Ready(Ok(())) } fn call(&mut self, req: http::Request) -> Self::Future { @@ -174,18 +188,26 @@ pub mod echo_server { } impl UnarySvc for UnaryEchoServer { type Response = super::EchoResponse; - type Future = BoxFuture, dubbo::status::Status>; - fn call(&mut self, request: Request) -> Self::Future { + type Future = BoxFuture< + Response, + dubbo::status::Status, + >; + fn call( + &mut self, + request: Request, + ) -> Self::Future { let inner = self.inner.0.clone(); let fut = async move { inner.unary_echo(request).await }; Box::pin(fut) } } let fut = async move { - let mut server = TripleServer::new(dubbo::codegen::ProstCodec::< - super::EchoResponse, - super::EchoRequest, - >::default()); + let mut server = TripleServer::new( + dubbo::codegen::ProstCodec::< + super::EchoResponse, + super::EchoRequest, + >::default(), + ); let res = server.unary(UnaryEchoServer { inner }, req).await; Ok(res) }; @@ -196,22 +218,32 @@ pub mod echo_server { struct ServerStreamingEchoServer { inner: _Inner, } - impl ServerStreamingSvc for ServerStreamingEchoServer { + impl ServerStreamingSvc + for ServerStreamingEchoServer { type Response = super::EchoResponse; type ResponseStream = T::ServerStreamingEchoStream; - type Future = - BoxFuture, dubbo::status::Status>; - fn call(&mut self, request: Request) -> Self::Future { + type Future = BoxFuture< + Response, + dubbo::status::Status, + >; + fn call( + &mut self, + request: Request, + ) -> Self::Future { let inner = self.inner.0.clone(); - let fut = async move { inner.server_streaming_echo(request).await }; + let fut = async move { + inner.server_streaming_echo(request).await + }; Box::pin(fut) } } let fut = async move { - let mut server = TripleServer::new(dubbo::codegen::ProstCodec::< - super::EchoResponse, - super::EchoRequest, - >::default()); + let mut server = TripleServer::new( + dubbo::codegen::ProstCodec::< + super::EchoResponse, + super::EchoRequest, + >::default(), + ); let res = server .server_streaming(ServerStreamingEchoServer { inner }, req) .await; @@ -224,23 +256,31 @@ pub mod echo_server { struct ClientStreamingEchoServer { inner: _Inner, } - impl ClientStreamingSvc for ClientStreamingEchoServer { + impl ClientStreamingSvc + for ClientStreamingEchoServer { type Response = super::EchoResponse; - type Future = BoxFuture, dubbo::status::Status>; + type Future = BoxFuture< + Response, + dubbo::status::Status, + >; fn call( &mut self, request: Request>, ) -> Self::Future { let inner = self.inner.0.clone(); - let fut = async move { inner.client_streaming_echo(request).await }; + let fut = async move { + inner.client_streaming_echo(request).await + }; Box::pin(fut) } } let fut = async move { - let mut server = TripleServer::new(dubbo::codegen::ProstCodec::< - super::EchoResponse, - super::EchoRequest, - >::default()); + let mut server = TripleServer::new( + dubbo::codegen::ProstCodec::< + super::EchoResponse, + super::EchoRequest, + >::default(), + ); let res = server .client_streaming(ClientStreamingEchoServer { inner }, req) .await; @@ -253,41 +293,56 @@ pub mod echo_server { struct BidirectionalStreamingEchoServer { inner: _Inner, } - impl StreamingSvc for BidirectionalStreamingEchoServer { + impl StreamingSvc + for BidirectionalStreamingEchoServer { type Response = super::EchoResponse; type ResponseStream = T::BidirectionalStreamingEchoStream; - type Future = - BoxFuture, dubbo::status::Status>; + type Future = BoxFuture< + Response, + dubbo::status::Status, + >; fn call( &mut self, request: Request>, ) -> Self::Future { let inner = self.inner.0.clone(); - let fut = - async move { inner.bidirectional_streaming_echo(request).await }; + let fut = async move { + inner.bidirectional_streaming_echo(request).await + }; Box::pin(fut) } } let fut = async move { - let mut server = TripleServer::new(dubbo::codegen::ProstCodec::< - super::EchoResponse, - super::EchoRequest, - >::default()); + let mut server = TripleServer::new( + dubbo::codegen::ProstCodec::< + super::EchoResponse, + super::EchoRequest, + >::default(), + ); let res = server - .bidi_streaming(BidirectionalStreamingEchoServer { inner }, req) + .bidi_streaming( + BidirectionalStreamingEchoServer { + inner, + }, + req, + ) .await; Ok(res) }; Box::pin(fut) } - _ => Box::pin(async move { - Ok(http::Response::builder() - .status(200) - .header("grpc-status", "12") - .header("content-type", "application/grpc") - .body(empty_body()) - .unwrap()) - }), + _ => { + Box::pin(async move { + Ok( + http::Response::builder() + .status(200) + .header("grpc-status", "12") + .header("content-type", "application/grpc") + .body(empty_body()) + .unwrap(), + ) + }) + } } } } diff --git a/examples/greeter/src/greeter/client.rs b/examples/greeter/src/greeter/client.rs index eed3e522..1d59cdfa 100644 --- a/examples/greeter/src/greeter/client.rs +++ b/examples/greeter/src/greeter/client.rs @@ -35,20 +35,7 @@ async fn main() { dubbo_logger::init(); let mut builder = ClientBuilder::new(); - - if let Ok(zk_servers) = env::var("ZOOKEEPER_SERVERS") { - let zkr = ZookeeperRegistry::new(&zk_servers); - let directory = RegistryDirectory::new(Box::new(zkr)); - builder = builder.with_directory(Box::new(directory)); - } else if let Ok(nacos_url_str) = env::var("NACOS_URL") { - // NACOS_URL=nacos://mse-96efa264-p.nacos-ans.mse.aliyuncs.com - let nacos_url = Url::from_url(&nacos_url_str).unwrap(); - let registry = NacosRegistry::new(nacos_url); - let directory = RegistryDirectory::new(Box::new(registry)); - builder = builder.with_directory(Box::new(directory)); - } else { - builder = builder.with_host("http://127.0.0.1:8888"); - } + builder.with_host("http://127.0.0.1:8888"); let mut cli = GreeterClient::new(builder); diff --git a/registry/nacos/Cargo.toml b/registry/nacos/Cargo.toml index 2437bc0b..798253a6 100644 --- a/registry/nacos/Cargo.toml +++ b/registry/nacos/Cargo.toml @@ -9,7 +9,7 @@ repository = "https://github.com/apache/dubbo-rust.git" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -nacos-sdk = { version = "0.2.3", features = ["naming", "auth-by-http"] } +nacos-sdk = { version = "0.3", features = ["naming", "auth-by-http"] } dubbo.workspace = true serde_json.workspace = true serde = { workspace = true, features = ["derive"] } From a0c751699d01db8e8fab653243186451e00e18e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=AF=9B=E6=96=87=E8=B6=85?= Date: Thu, 23 Nov 2023 10:46:37 +0800 Subject: [PATCH 36/48] Tst: local test passed (#166) * Tst: local test passed * Enhance: remove unnecessary key * Enhance: add BUFFER SIZE const variable --- dubbo-build/src/client.rs | 2 +- dubbo/src/cluster/mod.rs | 16 +- dubbo/src/directory/mod.rs | 148 +++++--------- dubbo/src/{cluster => invoker}/clone_body.rs | 50 ++--- .../src/{cluster => invoker}/clone_invoker.rs | 30 +-- dubbo/src/invoker/mod.rs | 76 +------ dubbo/src/loadbalancer/mod.rs | 37 ++-- dubbo/src/protocol/triple/triple_invoker.rs | 105 ++++++++-- dubbo/src/registry/n_registry.rs | 21 +- dubbo/src/route/mod.rs | 192 +++++++----------- dubbo/src/triple/client/builder.rs | 6 +- dubbo/src/triple/client/triple.rs | 4 + dubbo/src/triple/transport/connection.rs | 72 ++++--- .../echo/src/generated/grpc.examples.echo.rs | 2 +- examples/greeter/src/greeter/client.rs | 12 +- examples/greeter/src/greeter/server.rs | 8 +- 16 files changed, 351 insertions(+), 430 deletions(-) rename dubbo/src/{cluster => invoker}/clone_body.rs (91%) rename dubbo/src/{cluster => invoker}/clone_invoker.rs (90%) diff --git a/dubbo-build/src/client.rs b/dubbo-build/src/client.rs index 418bc10e..b222f752 100644 --- a/dubbo-build/src/client.rs +++ b/dubbo-build/src/client.rs @@ -65,7 +65,7 @@ pub fn generate( #service_doc #(#struct_attributes)* - #[derive(Debug, Clone, Default)] + #[derive(Clone)] pub struct #service_ident { inner: TripleClient, } diff --git a/dubbo/src/cluster/mod.rs b/dubbo/src/cluster/mod.rs index 47394e2e..0b1654a0 100644 --- a/dubbo/src/cluster/mod.rs +++ b/dubbo/src/cluster/mod.rs @@ -19,12 +19,10 @@ use http::Request; use tower_service::Service; -use crate::{codegen::RpcInvocation, StdError, svc::NewService, param::Param}; +use crate::{codegen::RpcInvocation, svc::NewService, param::Param, invoker::clone_body::CloneBody}; + +use self::failover::Failover; -use self::{failover::Failover, clone_body::CloneBody}; - -mod clone_body; -mod clone_invoker; mod failover; pub struct NewCluster { @@ -65,11 +63,9 @@ where } } -impl Service> for Cluster +impl Service> for Cluster where - S: Service>>, - B: http_body::Body + Unpin, - B::Error: Into, + S: Service>, { type Response = S::Response; @@ -82,7 +78,7 @@ where self.inner.poll_ready(cx) } - fn call(&mut self, req: Request) -> Self::Future { + fn call(&mut self, req: Request) -> Self::Future { let (parts, body) = req.into_parts(); let clone_body = CloneBody::new(body); let req = Request::from_parts(parts, clone_body); diff --git a/dubbo/src/directory/mod.rs b/dubbo/src/directory/mod.rs index 0861d32a..a4cf4669 100644 --- a/dubbo/src/directory/mod.rs +++ b/dubbo/src/directory/mod.rs @@ -15,15 +15,15 @@ * limitations under the License. */ - use core::panic; use std::{ - hash::Hash, - task::{Context, Poll}, collections::HashMap, sync::{Arc, Mutex}, + task::{Context, Poll}, collections::HashMap, sync::{Arc, Mutex}, pin::Pin, }; -use crate::{StdError, codegen::RpcInvocation, invocation::Invocation, registry::n_registry::Registry, invoker::NewInvoker, svc::NewService, param::Param}; -use futures_util::future::{poll_fn, self}; -use tokio::{sync::{watch, Notify, mpsc::channel}, select}; +use crate::{StdError, codegen::{RpcInvocation, TripleInvoker}, invocation::Invocation, registry::n_registry::Registry, invoker::{NewInvoker,clone_invoker::CloneInvoker}, svc::NewService, param::Param}; +use dubbo_logger::tracing::debug; +use futures_core::ready; +use futures_util::future; +use tokio::sync::mpsc::channel; use tokio_stream::wrappers::ReceiverStream; use tower::{ discover::{Change, Discover}, buffer::Buffer, @@ -31,7 +31,7 @@ use tower::{ use tower_service::Service; -type BufferedDirectory = Buffer, StdError>>>, ()>; +type BufferedDirectory = Buffer, StdError>>>, ()>; pub struct NewCachedDirectory where @@ -57,14 +57,10 @@ pub struct NewDirectory { } - -#[derive(Clone)] -pub struct Directory -where - D: Discover -{ - rx: watch::Receiver>, - close: Arc +pub struct Directory { + directory: HashMap>, + discover: D, + new_invoker: NewInvoker, } @@ -143,6 +139,8 @@ where impl NewDirectory { + const MAX_DIRECTORY_BUFFER_SIZE: usize = 16; + pub fn new(inner: N) -> Self { NewDirectory { inner @@ -150,6 +148,8 @@ impl NewDirectory { } } + + impl NewService for NewDirectory where T: Param, @@ -157,6 +157,8 @@ where N: Registry + Clone + Send + Sync + 'static, { type Service = BufferedDirectory; + + fn new_service(&self, target: T) -> Self::Service { @@ -164,26 +166,27 @@ where let registry = self.inner.clone(); - let (tx, rx) = channel(1024); + let (tx, rx) = channel(Self::MAX_DIRECTORY_BUFFER_SIZE); tokio::spawn(async move { - let receiver = registry.subscribe(service_name).await; + debug!("discover start!"); match receiver { Err(e) => { // error!("discover stream error: {}", e); - + debug!("discover stream error"); }, Ok(mut receiver) => { loop { let change = receiver.recv().await; + debug!("receive change: {:?}", change); match change { None => { - // debug!("discover stream closed."); + debug!("discover stream closed."); break; }, Some(change) => { - let _ = tx.send(change); + let _ = tx.send(change).await; } } } @@ -192,71 +195,20 @@ where }); - Buffer::new(Directory::new(ReceiverStream::new(rx)), 1024) + Buffer::new(Directory::new(ReceiverStream::new(rx)), Self::MAX_DIRECTORY_BUFFER_SIZE) } } -impl Directory -where - // Discover - D: Discover + Unpin + Send + 'static, - // the key may be dubbo url - D::Key: Hash + Eq + Clone + Send, - // invoker new service - D::Service: NewService<()> + Clone + Send + Sync, -{ +impl Directory { pub fn new(discover: D) -> Self { - let mut discover = Box::pin(discover); - - let (tx, rx) = watch::channel(Vec::new()); - let close = Arc::new(Notify::new()); - let close_clone = close.clone(); - - tokio::spawn(async move { - let mut cache: HashMap = HashMap::new(); - - loop { - let changed = select! { - _ = close_clone.notified() => { - // info!("discover stream closed.") - return; - }, - changed = poll_fn(|cx| discover.as_mut().poll_discover(cx)) => { - changed - } - }; - let Some(changed) = changed else { - // debug!("discover stream closed."); - break; - }; - - match changed { - Err(e) => { - // error!("discover stream error: {}", e); - continue; - }, - Ok(changed) => match changed { - Change::Insert(k, v) => { - cache.insert(k, v); - }, - Change::Remove(k) => { - cache.remove(&k); - } - } - } - - let vec: Vec = cache.values().map(|v|v.clone()).collect(); - let _ = tx.send(vec); - } - - }); Directory { - rx, - close + directory: Default::default(), + discover, + new_invoker: NewInvoker, } } } @@ -265,34 +217,40 @@ where impl Service<()> for Directory where // Discover - D: Discover + Unpin + Send, - // the key may be dubbo url - D::Key: Hash + Eq + Clone + Send, - // invoker new service - D::Service: NewService<()> + Clone + Send + Sync, + D: Discover + Unpin + Send, + D::Error: Into { - type Response = watch::Receiver>; + type Response = Vec>; type Error = StdError; type Future = future::Ready>; fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) + + loop { + let pin_discover = Pin::new(&mut self.discover); + let change = ready!(pin_discover.poll_discover(cx)).transpose().map_err(|e| e.into())?; + match change { + Some(Change::Remove(key)) => { + debug!("remove key: {}", key); + self.directory.remove(&key); + }, + Some(Change::Insert(key, _)) => { + debug!("insert key: {}", key); + let invoker = self.new_invoker.new_service(key.clone()); + self.directory.insert(key, invoker); + }, + None => { + debug!("stream closed"); + return Poll::Ready(Ok(())); + } + } + } } fn call(&mut self, _: ()) -> Self::Future { - future::ok(self.rx.clone()) - } -} - -impl Drop for Directory -where - D: Discover, -{ - fn drop(&mut self) { - if Arc::strong_count(&self.close) == 1 { - self.close.notify_one(); - } + let vec = self.directory.values().map(|val|val.clone()).collect::>>(); + future::ok(vec) } } \ No newline at end of file diff --git a/dubbo/src/cluster/clone_body.rs b/dubbo/src/invoker/clone_body.rs similarity index 91% rename from dubbo/src/cluster/clone_body.rs rename to dubbo/src/invoker/clone_body.rs index 1d6b65a8..5ce2e1f9 100644 --- a/dubbo/src/cluster/clone_body.rs +++ b/dubbo/src/invoker/clone_body.rs @@ -14,32 +14,31 @@ use pin_project::pin_project; use thiserror::Error; use crate::StdError; - #[derive(Error, Debug)] #[error("buffered body reach max capacity.")] pub struct ReachMaxCapacityError; -pub struct BufferedBody { - shared: Arc>>>, - owned: Option>, +pub struct BufferedBody { + shared: Arc>>, + owned: Option, replay_body: bool, replay_trailers: bool, is_empty: bool, size_hint: http_body::SizeHint, } -pub struct OwnedBufferedBody { - body: B, +pub struct OwnedBufferedBody { + body: hyper::Body, trailers: Option, buf: InnerBuffer, } -impl BufferedBody { +impl BufferedBody { - pub fn new(body: B, buf_size: usize) -> Self { + pub fn new(body: hyper::Body, buf_size: usize) -> Self { let size_hint = body.size_hint(); let is_empty = body.is_end_stream(); BufferedBody { @@ -61,7 +60,7 @@ impl BufferedBody { } -impl Clone for BufferedBody { +impl Clone for BufferedBody { fn clone(&self) -> Self { Self { @@ -75,7 +74,7 @@ impl Clone for BufferedBody { } } -impl Drop for BufferedBody { +impl Drop for BufferedBody { fn drop(&mut self) { if let Some(owned) = self.owned.take() { let lock = self.shared.lock(); @@ -86,11 +85,8 @@ impl Drop for BufferedBody { } } -impl Body for BufferedBody -where - B: http_body::Body + Unpin, - B::Error: Into, -{ +impl Body for BufferedBody { + type Data = BytesData; type Error = StdError; @@ -328,24 +324,16 @@ impl Buf for BytesData { } #[pin_project] -pub struct CloneBody(#[pin] BufferedBody); - -impl CloneBody -where - B: http_body::Body + Unpin, - B::Error: Into, -{ - pub fn new(inner_body: B) -> Self { +pub struct CloneBody(#[pin] BufferedBody); + +impl CloneBody { + pub fn new(inner_body: hyper::Body) -> Self { let inner_body = BufferedBody::new(inner_body, 1024 * 64); CloneBody(inner_body) } } -impl Body for CloneBody -where - B: http_body::Body + Unpin, - B::Error: Into, -{ +impl Body for CloneBody{ type Data = BytesData; @@ -371,11 +359,7 @@ where } -impl Clone for CloneBody -where - B: http_body::Body + Unpin, - B::Error: Into, -{ +impl Clone for CloneBody { fn clone(&self) -> Self { Self(self.0.clone()) } diff --git a/dubbo/src/cluster/clone_invoker.rs b/dubbo/src/invoker/clone_invoker.rs similarity index 90% rename from dubbo/src/cluster/clone_invoker.rs rename to dubbo/src/invoker/clone_invoker.rs index 20e18118..fe621b87 100644 --- a/dubbo/src/cluster/clone_invoker.rs +++ b/dubbo/src/invoker/clone_invoker.rs @@ -12,6 +12,8 @@ use tower_service::Service; use crate::StdError; +use super::clone_body::CloneBody; + enum Inner { Invalid, Ready(S), @@ -149,43 +151,42 @@ impl Drop for ReadyService { } } -pub struct CloneInvoker +pub struct CloneInvoker where - Inv: Service + Send + 'static, + Inv: Service> + Send + 'static, Inv::Error: Into + Send + Sync + 'static, Inv::Future: Send, - Req: Send { - inner: Buffer, Req>, + inner: Buffer, http::Request>, rx: Receiver, poll: ReusableBoxFuture<'static, ObserveState>, polling: bool, } -impl CloneInvoker +impl CloneInvoker where - Inv: Service + Send + 'static, + Inv: Service> + Send + 'static, Inv::Error: Into + Send + Sync + 'static, Inv::Future: Send, - Req: Send + 'static { + const MAX_INVOKER_BUFFER_SIZE: usize = 16; + pub fn new(invoker: Inv) -> Self { let (ready_service, rx) = ReadyService::new(invoker); - let buffer: Buffer, Req> = Buffer::new(ready_service, 1024); + let buffer: Buffer, http::Request> = Buffer::new(ready_service, Self::MAX_INVOKER_BUFFER_SIZE); Self { inner: buffer, rx, polling: false, poll: ReusableBoxFuture::new(futures::future::pending()) } } } -impl Service for CloneInvoker +impl Service> for CloneInvoker where - Inv: Service + Send + 'static, + Inv: Service> + Send + 'static, Inv::Error: Into + Send + Sync + 'static, Inv::Future: Send, - Req: Send + 'static { type Response = Inv::Response; @@ -229,18 +230,17 @@ where } } - fn call(&mut self, req: Req) -> Self::Future { + fn call(&mut self, req: http::Request) -> Self::Future { Box::pin(self.inner.call(req)) } } -impl Clone for CloneInvoker +impl Clone for CloneInvoker where - Inv: Service + Send + 'static, + Inv: Service> + Send + 'static, Inv::Error: Into + Send + Sync + 'static, Inv::Future: Send, - Req: Send { fn clone(&self) -> Self { Self { inner: self.inner.clone(), rx: self.rx.clone(), polling: false, poll: ReusableBoxFuture::new(futures::future::pending())} diff --git a/dubbo/src/invoker/mod.rs b/dubbo/src/invoker/mod.rs index 81bcb68f..a8179eee 100644 --- a/dubbo/src/invoker/mod.rs +++ b/dubbo/src/invoker/mod.rs @@ -1,75 +1,21 @@ use dubbo_base::Url; -use tower_service::Service; -use crate::{codegen::TripleInvoker, param::Param, svc::NewService}; +use crate::{codegen::TripleInvoker, svc::NewService, invoker::clone_invoker::CloneInvoker}; -#[derive(Clone)] -pub struct NewInvoker { - url: Url -} +pub mod clone_body; +pub mod clone_invoker; -pub enum InvokerComponent { - TripleInvoker(TripleInvoker) -} +pub struct NewInvoker; -impl NewInvoker { - pub fn new(url: Url) -> Self { - Self { - url - } - } -} - -impl From for NewInvoker { - fn from(url: String) -> Self { - Self { - url: Url::from_url(&url).unwrap() - } - } -} - -impl Param for NewInvoker { - fn param(&self) -> Url { - self.url.clone() - } -} - -impl NewService<()> for NewInvoker { - type Service = InvokerComponent; - fn new_service(&self, _: ()) -> Self::Service { - // todo create another invoker - InvokerComponent::TripleInvoker(TripleInvoker::new(self.url.clone())) - } -} - - -impl Service> for InvokerComponent -where - B: http_body::Body + Unpin + Send + 'static, - B::Error: Into, - B::Data: Send + Unpin, -{ - type Response = http::Response; - type Error = crate::Error; +impl NewService for NewInvoker { + type Service = CloneInvoker; - type Future = crate::BoxFuture; + fn new_service(&self, url: String) -> Self::Service { + // todo create another invoker by url protocol - fn call(&mut self, req: http::Request) -> Self::Future { - match self { - InvokerComponent::TripleInvoker(invoker) => invoker.call(req), - } + let url = Url::from_url(&url).unwrap(); + CloneInvoker::new(TripleInvoker::new(url)) } - - fn poll_ready( - &mut self, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - match self { - InvokerComponent::TripleInvoker(invoker) => >>::poll_ready(invoker, cx), - } - } -} - -// InvokerComponent::TripleInvoker(invoker) => >>::poll_ready(invoker, cx), \ No newline at end of file +} \ No newline at end of file diff --git a/dubbo/src/loadbalancer/mod.rs b/dubbo/src/loadbalancer/mod.rs index 822794cc..334350e3 100644 --- a/dubbo/src/loadbalancer/mod.rs +++ b/dubbo/src/loadbalancer/mod.rs @@ -1,8 +1,12 @@ use futures_core::future::BoxFuture; -use tower::{discover::ServiceList, ServiceExt}; +use tower::ServiceExt; +use tower::discover::ServiceList; use tower_service::Service; -use crate::{codegen::RpcInvocation, StdError, svc::NewService, param::Param}; +use crate::invoker::clone_body::CloneBody; +use crate::{codegen::RpcInvocation, StdError, svc::NewService, param::Param, invoker::clone_invoker::CloneInvoker}; + +use crate::protocol::triple::triple_invoker::TripleInvoker; pub struct NewLoadBalancer { inner: N, @@ -40,28 +44,21 @@ where let svc = self.inner.new_service(target); LoadBalancer { - inner: svc + inner: svc, } } } -impl Service for LoadBalancer +impl Service> for LoadBalancer where - Req: Send + 'static, // Routes service - N: Service<(), Response = Vec> + Clone, + N: Service<(), Response = Vec>> + Clone, N::Error: Into + Send, N::Future: Send + 'static, - // new invoker - Nsv: NewService<()> + Send, - Nsv::Service: Service + Send, - // invoker - >::Error: Into + Send, - >::Future: Send + 'static, { - type Response = >::Response; + type Response = as Service>>::Response; type Error = StdError; @@ -69,30 +66,28 @@ where fn poll_ready(&mut self, cx: &mut std::task::Context<'_>) -> std::task::Poll> { self.inner.poll_ready(cx).map_err(Into::into) - + } - fn call(&mut self, req: Req) -> Self::Future { + fn call(&mut self, req: http::Request) -> Self::Future { let routes = self.inner.call(()); let fut = async move { let routes = routes.await; - let routes: Vec = match routes { + let routes: Vec> = match routes { Err(e) => return Err(Into::::into(e)), Ok(routes) => routes }; - - let service_list: Vec<_> = routes.iter().map(|inv| { - let invoker = inv.new_service(()); + + let service_list: Vec<_> = routes.into_iter().map(|invoker| { tower::load::Constant::new(invoker, 1) - }).collect(); let service_list = ServiceList::new(service_list); let p2c = tower::balance::p2c::Balance::new(service_list); - + p2c.oneshot(req).await }; diff --git a/dubbo/src/protocol/triple/triple_invoker.rs b/dubbo/src/protocol/triple/triple_invoker.rs index 685c4344..71eae903 100644 --- a/dubbo/src/protocol/triple/triple_invoker.rs +++ b/dubbo/src/protocol/triple/triple_invoker.rs @@ -16,15 +16,15 @@ */ use dubbo_base::Url; +use http::{Uri, HeaderValue}; use std::{ fmt::{Debug, Formatter}, str::FromStr, }; use tower_service::Service; -use crate::triple::transport::{connection::Connection, self}; +use crate::{triple::transport::{connection::Connection, self}, invoker::clone_body::CloneBody}; -#[derive(Clone)] pub struct TripleInvoker { url: Url, conn: Connection, @@ -35,7 +35,7 @@ impl TripleInvoker { let uri = http::Uri::from_str(&url.to_url()).unwrap(); Self { url, - conn: Connection::new().with_host(uri), + conn: Connection::new().with_host(uri).build(), } } } @@ -46,27 +46,104 @@ impl Debug for TripleInvoker { } } -impl Service> for TripleInvoker -where - B: http_body::Body + Unpin + Send + 'static, - B::Error: Into, - B::Data: Send + Unpin, -{ +impl TripleInvoker { + pub fn map_request( + &self, + req: http::Request, + ) -> http::Request { + + let (parts, body) = req.into_parts(); + + let path_and_query = parts.headers.get("path").unwrap().to_str().unwrap(); + + let authority = self.url.clone().get_ip_port(); + + let uri = Uri::builder().scheme("http").authority(authority).path_and_query(path_and_query).build().unwrap(); + + let mut req = hyper::Request::builder() + .version(http::Version::HTTP_2) + .uri(uri.clone()) + .method("POST") + .body(body) + .unwrap(); + + // *req.version_mut() = http::Version::HTTP_2; + req.headers_mut() + .insert("method", HeaderValue::from_static("POST")); + req.headers_mut().insert( + "scheme", + HeaderValue::from_str(uri.scheme_str().unwrap()).unwrap(), + ); + req.headers_mut() + .insert("path", HeaderValue::from_str(uri.path()).unwrap()); + req.headers_mut().insert( + "authority", + HeaderValue::from_str(uri.authority().unwrap().as_str()).unwrap(), + ); + req.headers_mut().insert( + "content-type", + HeaderValue::from_static("application/grpc+proto"), + ); + req.headers_mut() + .insert("user-agent", HeaderValue::from_static("dubbo-rust/0.1.0")); + req.headers_mut() + .insert("te", HeaderValue::from_static("trailers")); + req.headers_mut().insert( + "tri-service-version", + HeaderValue::from_static("dubbo-rust/0.1.0"), + ); + req.headers_mut() + .insert("tri-service-group", HeaderValue::from_static("cluster")); + req.headers_mut().insert( + "tri-unit-info", + HeaderValue::from_static("dubbo-rust/0.1.0"), + ); + // if let Some(_encoding) = self.send_compression_encoding { + + // } + + req.headers_mut() + .insert("grpc-encoding", http::HeaderValue::from_static("gzip")); + + req.headers_mut().insert( + "grpc-accept-encoding", + http::HeaderValue::from_static("gzip"), + ); + + // // const ( + // // TripleContentType = "application/grpc+proto" + // // TripleUserAgent = "grpc-go/1.35.0-dev" + // // TripleServiceVersion = "tri-service-version" + // // TripleAttachement = "tri-attachment" + // // TripleServiceGroup = "tri-service-group" + // // TripleRequestID = "tri-req-id" + // // TripleTraceID = "tri-trace-traceid" + // // TripleTraceRPCID = "tri-trace-rpcid" + // // TripleTraceProtoBin = "tri-trace-proto-bin" + // // TripleUnitInfo = "tri-unit-info" + // // ) + req + } +} + +impl Service> for TripleInvoker { type Response = http::Response; type Error = crate::Error; type Future = crate::BoxFuture; - fn call(&mut self, req: http::Request) -> Self::Future { - self.conn.call(req) - } - fn poll_ready( &mut self, cx: &mut std::task::Context<'_>, ) -> std::task::Poll> { - >>::poll_ready(&mut self.conn, cx) + >>::poll_ready(&mut self.conn, cx) + } + + fn call(&mut self, req: http::Request) -> Self::Future { + let req = self.map_request(req); + + self.conn.call(req) } } diff --git a/dubbo/src/registry/n_registry.rs b/dubbo/src/registry/n_registry.rs index 69300f2a..9b6dca58 100644 --- a/dubbo/src/registry/n_registry.rs +++ b/dubbo/src/registry/n_registry.rs @@ -6,9 +6,9 @@ use tokio::sync::mpsc::{Receiver, channel}; use tower::discover::Change; -use crate::{StdError, invoker::NewInvoker}; +use crate::StdError; -type DiscoverStream = Receiver, StdError>>; +type DiscoverStream = Receiver, StdError>>; #[async_trait] pub trait Registry { @@ -51,19 +51,19 @@ impl ArcRegistry { impl Registry for ArcRegistry { async fn register(&self, url: Url) -> Result<(), StdError> { - self.register(url).await + self.inner.register(url).await } async fn unregister(&self, url: Url) -> Result<(), StdError> { - self.unregister(url).await + self.inner.unregister(url).await } async fn subscribe(&self, service_name: String) -> Result { - self.subscribe(service_name).await + self.inner.subscribe(service_name).await } async fn unsubscribe(&self, url: Url) -> Result<(), StdError> { - self.unsubscribe(url).await + self.inner.unsubscribe(url).await } } @@ -81,7 +81,11 @@ impl Registry for RegistryComponent { } async fn subscribe(&self, service_name: String) -> Result { - todo!() + match self { + RegistryComponent::NacosRegistry => todo!(), + RegistryComponent::ZookeeperRegistry => todo!(), + RegistryComponent::StaticRegistry(registry) => registry.subscribe(service_name).await, + } } async fn unsubscribe(&self, url: Url) -> Result<(), StdError> { @@ -113,8 +117,7 @@ impl Registry for StaticRegistry { async fn subscribe(&self, service_name: String) -> Result { let (tx, rx) = channel(self.urls.len()); for url in self.urls.iter() { - let invoker = NewInvoker::new(url.clone()); - let change = Ok(Change::Insert(service_name.clone(), invoker)); + let change = Ok(Change::Insert(url.to_url(), ())); tx.send(change).await?; } diff --git a/dubbo/src/route/mod.rs b/dubbo/src/route/mod.rs index dfbe73e3..cff3ccee 100644 --- a/dubbo/src/route/mod.rs +++ b/dubbo/src/route/mod.rs @@ -1,38 +1,35 @@ -use std::{sync::{Arc, Mutex}, collections::HashMap}; +use std::pin::Pin; +use dubbo_logger::tracing::debug; use futures_core::{Future, ready}; -use futures_util::future::Ready; -use pin_project::pin_project; -use tokio::{sync::watch, pin}; +use futures_util::{future::Ready, FutureExt, TryFutureExt}; use tower::{util::FutureService, buffer::Buffer}; use tower_service::Service; -use crate::{StdError, codegen::RpcInvocation, svc::NewService, param::Param, invocation::Invocation}; +use crate::{StdError, codegen::{RpcInvocation, TripleInvoker}, svc::NewService, param::Param, invoker::clone_invoker::CloneInvoker}; pub struct NewRoutes { inner: N, } -pub struct NewRoutesCache -where - N: NewService -{ - inner: N, - cache: Arc>>, -} -#[pin_project] -pub struct NewRoutesFuture { - #[pin] - inner: N, +pub struct NewRoutesFuture { + inner: RoutesFutureInnerState, target: T, } + + +pub enum RoutesFutureInnerState { + Service(S), + Future(Pin>, StdError>> + Send + 'static>>), + Ready(Vec>), +} + #[derive(Clone)] -pub struct Routes { +pub struct Routes { target: T, - new_invokers: Vec, - invokers_receiver: watch::Receiver>, + invokers: Vec> } impl NewRoutes { @@ -43,154 +40,101 @@ impl NewRoutes { } } +impl NewRoutes { + const MAX_ROUTE_BUFFER_SIZE: usize = 16; -impl NewService for NewRoutes -where - T: Param + Clone + Send + 'static, - // NewDirectory - N: NewService, - // Directory - N::Service: Service<(), Response = watch::Receiver>> + Unpin + Send + 'static, - >::Error: Into, - // new invoker service - Nsv: NewService<()> + Clone + Send + Sync + 'static, -{ - - type Service = Buffer>::Service, T>, Routes>, ()>; - - fn new_service(&self, target: T) -> Self::Service { - let inner = self.inner.new_service(target.clone()); - - Buffer::new(FutureService::new(NewRoutesFuture { - inner, - target, - }), 1024) - } -} -impl NewRoutesCache -where - N: NewService, - >::Service: Service<(), Response = watch::Receiver>> + Unpin + Send + 'static, - >::Error: Into, - Nsv: NewService<()> + Clone + Send + Sync + 'static, - -{ - pub fn layer() -> impl tower_layer::Layer>> { + pub fn layer() -> impl tower_layer::Layer { tower_layer::layer_fn(|inner: N| { - NewRoutesCache::new(NewRoutes::new(inner)) + NewRoutes::new(inner) }) } - - } -impl NewRoutesCache +impl NewService for NewRoutes where - N: NewService -{ - pub fn new(inner: N) -> Self { - Self { - inner, - cache: Default::default(), - } - } -} + T: Param + Clone + Send + Unpin + 'static, + // NewDirectory + N: NewService, + // Directory + N::Service: Service<(), Response = Vec>> + Unpin + Send + 'static, + >::Error: Into, + >::Future: Send + 'static, +{ -impl NewService for NewRoutesCache -where - T: Param, - N: NewService, - N::Service: Clone, -{ - type Service = N::Service; + type Service = Buffer>::Service, T>, Routes>, ()>; fn new_service(&self, target: T) -> Self::Service { - let rpc_inv = target.param(); - let service_name = rpc_inv.get_target_service_unique_name(); - - let mut cache = self.cache.lock().expect("RoutesCache get lock failed"); - - let service = cache.get(&service_name); - match service { - Some(service) => service.clone(), - None => { - let service = self.inner.new_service(rpc_inv); - cache.insert(service_name, service.clone()); - service - } - } + let inner = self.inner.new_service(target.clone()); + + Buffer::new(FutureService::new(NewRoutesFuture { + inner: RoutesFutureInnerState::Service(inner), + target, + }), Self::MAX_ROUTE_BUFFER_SIZE) } } -impl Future for NewRoutesFuture +impl Future for NewRoutesFuture where - T: Param + Clone, + T: Param + Clone + Unpin, // Directory - N: Service<(), Response = watch::Receiver>> + Unpin, + N: Service<(), Response = Vec>> + Unpin, N::Error: Into, - // new invoker service - Nsv: NewService<()> + Clone, + N::Future: Send + 'static, { - type Output = Result, StdError>; + type Output = Result, StdError>; fn poll(self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> std::task::Poll { let this = self.get_mut(); - let target = this.target.clone(); - - let _ = ready!(this.inner.poll_ready(cx)).map_err(Into::into)?; - - - let call = this.inner.call(()); - pin!(call); - - let mut invokers_receiver = ready!(call.poll(cx).map_err(Into::into))?; - let new_invokers = { - let wait_for = invokers_receiver.wait_for(|invs|!invs.is_empty()); - pin!(wait_for); - - let changed = ready!(wait_for.poll(cx))?; - - changed.clone() - }; - - - std::task::Poll::Ready(Ok(Routes { - invokers_receiver, - new_invokers, - target, - })) + loop { + match this.inner { + RoutesFutureInnerState::Service(ref mut service) => { + debug!("RoutesFutureInnerState::Service"); + let _ = ready!(service.poll_ready(cx)).map_err(Into::into)?; + let fut = service.call(()).map_err(|e|e.into()).boxed(); + this.inner = RoutesFutureInnerState::Future(fut); + }, + RoutesFutureInnerState::Future(ref mut futures) => { + debug!("RoutesFutureInnerState::Future"); + let invokers = ready!(futures.as_mut().poll(cx))?; + this.inner = RoutesFutureInnerState::Ready(invokers); + }, + RoutesFutureInnerState::Ready(ref invokers) => { + debug!("RoutesFutureInnerState::Ready"); + let target = this.target.clone(); + return std::task::Poll::Ready(Ok(Routes { + invokers: invokers.clone(), + target, + })); + }, + } + } + } } -impl Service<()> for Routes +impl Service<()> for Routes where T: Param + Clone, - // new invoker service - Nsv: NewService<()> + Clone, { - type Response = Vec; + type Response = Vec>; type Error = StdError; type Future = Ready>; fn poll_ready(&mut self, _: &mut std::task::Context<'_>) -> std::task::Poll> { - let has_change = self.invokers_receiver.has_changed()?; - if has_change { - self.new_invokers = self.invokers_receiver.borrow_and_update().clone(); - } std::task::Poll::Ready(Ok(())) } fn call(&mut self, _: ()) -> Self::Future { // some router operator // if new_invokers changed, send new invokers to routes_rx after router operator - futures_util::future::ok(self.new_invokers.clone()) + futures_util::future::ok(self.invokers.clone()) } } \ No newline at end of file diff --git a/dubbo/src/triple/client/builder.rs b/dubbo/src/triple/client/builder.rs index 89d7a00a..ed080217 100644 --- a/dubbo/src/triple/client/builder.rs +++ b/dubbo/src/triple/client/builder.rs @@ -19,7 +19,7 @@ use std::sync::Arc; use crate::{ - utils::boxed_clone::BoxCloneService, registry::n_registry::{RegistryComponent, StaticRegistry, ArcRegistry}, route::{NewRoutes, NewRoutesCache}, loadbalancer::NewLoadBalancer, cluster::{NewCluster, Cluster}, directory::NewCachedDirectory, svc::{ArcNewService, NewService, BoxedService}, StdError, codegen::RpcInvocation, BoxBody, + utils::boxed_clone::BoxCloneService, registry::n_registry::{RegistryComponent, StaticRegistry, ArcRegistry}, route::NewRoutes, loadbalancer::NewLoadBalancer, cluster::{NewCluster, Cluster}, directory::NewCachedDirectory, svc::{ArcNewService, NewService, BoxedService}, StdError, codegen::RpcInvocation, BoxBody, }; use aws_smithy_http::body::SdkBody; @@ -30,7 +30,7 @@ pub type ClientBoxService = BoxCloneService, http::Response, crate::Error>; -pub type ServiceMK = Arc>>>>>; +pub type ServiceMK = Arc>>>>; #[derive(Default)] pub struct ClientBuilder { @@ -102,7 +102,7 @@ impl ClientBuilder { let mk_service = ServiceBuilder::new() .layer(NewCluster::layer()) .layer(NewLoadBalancer::layer()) - .layer(NewRoutesCache::layer()) + .layer(NewRoutes::layer()) .layer(NewCachedDirectory::layer()) .service(registry); diff --git a/dubbo/src/triple/client/triple.rs b/dubbo/src/triple/client/triple.rs index 091e0207..9bcf144d 100644 --- a/dubbo/src/triple/client/triple.rs +++ b/dubbo/src/triple/client/triple.rs @@ -153,6 +153,7 @@ impl TripleClient { let request = http::Request::builder() + .header("path", path.to_string()) .body(body).unwrap(); @@ -215,6 +216,7 @@ impl TripleClient { let request = http::Request::builder() + .header("path", path.to_string()) .body(body).unwrap(); @@ -259,6 +261,7 @@ impl TripleClient { let request = http::Request::builder() + .header("path", path.to_string()) .body(body).unwrap(); @@ -321,6 +324,7 @@ impl TripleClient { let request = http::Request::builder() + .header("path", path.to_string()) .body(body).unwrap(); diff --git a/dubbo/src/triple/transport/connection.rs b/dubbo/src/triple/transport/connection.rs index 1b978750..3bbaa179 100644 --- a/dubbo/src/triple/transport/connection.rs +++ b/dubbo/src/triple/transport/connection.rs @@ -15,19 +15,18 @@ * limitations under the License. */ -use std::task::Poll; - -use dubbo_logger::tracing::debug; use hyper::client::{conn::Builder, service::Connect}; use tower_service::Service; -use crate::{boxed, triple::transport::connector::get_connector}; +use crate::{boxed, triple::transport::connector::get_connector, StdError, invoker::clone_body::CloneBody}; + +type HyperConnect = Connect, CloneBody, http::Uri>; -#[derive(Debug, Clone)] pub struct Connection { host: hyper::Uri, connector: &'static str, builder: Builder, + connect: Option, } impl Default for Connection { @@ -42,6 +41,7 @@ impl Connection { host: hyper::Uri::default(), connector: "http", builder: Builder::new(), + connect: None, } } @@ -59,14 +59,19 @@ impl Connection { self.builder = builder; self } + + pub fn build(mut self) -> Self { + let builder = self.builder.clone().http2_only(true).to_owned(); + let hyper_connect: HyperConnect = Connect::new(get_connector(self.connector), builder); + self.connect = Some(hyper_connect); + self + + } } -impl Service> for Connection -where - ReqBody: http_body::Body + Unpin + Send + 'static, - ReqBody::Data: Send + Unpin, - ReqBody::Error: Into, -{ +impl Service> for Connection { + + type Response = http::Response; type Error = crate::Error; @@ -75,25 +80,36 @@ where fn poll_ready( &mut self, - _cx: &mut std::task::Context<'_>, + cx: &mut std::task::Context<'_>, ) -> std::task::Poll> { - Poll::Ready(Ok(())) + match self.connect { + None => { + panic!("connection must be built before use") + }, + Some(ref mut connect) => { + connect.poll_ready(cx).map_err(|e|e.into()) + } + } } - fn call(&mut self, req: http::Request) -> Self::Future { - let builder = self.builder.clone().http2_only(true).to_owned(); - let mut connector = Connect::new(get_connector(self.connector), builder); - let uri = self.host.clone(); - let fut = async move { - debug!("send base call to {}", uri); - let mut con = connector.call(uri).await.unwrap(); - - con.call(req) - .await - .map_err(|err| err.into()) - .map(|res| res.map(boxed)) - }; - - Box::pin(fut) + fn call(&mut self, req: http::Request) -> Self::Future { + + match self.connect { + None => { + panic!("connection must be built before use") + }, + Some(ref mut connect) => { + let uri = self.host.clone(); + let call_fut = connect.call(uri); + let fut = async move { + let mut con = call_fut.await.unwrap(); + con.call(req).await + .map_err(|err| err.into()) + .map(|res| res.map(boxed)) + }; + + return Box::pin(fut) + } + } } } diff --git a/examples/echo/src/generated/grpc.examples.echo.rs b/examples/echo/src/generated/grpc.examples.echo.rs index e756d5c7..07c58fe9 100644 --- a/examples/echo/src/generated/grpc.examples.echo.rs +++ b/examples/echo/src/generated/grpc.examples.echo.rs @@ -17,7 +17,7 @@ pub mod echo_client { #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] use dubbo::codegen::*; /// Echo is the echo service. - #[derive(Debug, Clone, Default)] + #[derive(Clone)] pub struct EchoClient { inner: TripleClient, } diff --git a/examples/greeter/src/greeter/client.rs b/examples/greeter/src/greeter/client.rs index 1d59cdfa..afa1b205 100644 --- a/examples/greeter/src/greeter/client.rs +++ b/examples/greeter/src/greeter/client.rs @@ -21,21 +21,17 @@ pub mod protos { } use std::env; - + use dubbo::codegen::*; -use dubbo_base::Url; use futures_util::StreamExt; use protos::{greeter_client::GreeterClient, GreeterRequest}; -use registry_nacos::NacosRegistry; -use registry_zookeeper::ZookeeperRegistry; #[tokio::main] async fn main() { dubbo_logger::init(); - - let mut builder = ClientBuilder::new(); - builder.with_host("http://127.0.0.1:8888"); + + let builder = ClientBuilder::new().with_host("http://127.0.0.1:8888"); let mut cli = GreeterClient::new(builder); @@ -47,7 +43,7 @@ async fn main() { .await; let resp = match resp { Ok(resp) => resp, - Err(err) => return println!("{:?}", err), + Err(err) => return println!("response error: {:?}", err), }; let (_parts, body) = resp.into_parts(); println!("Response: {:?}", body); diff --git a/examples/greeter/src/greeter/server.rs b/examples/greeter/src/greeter/server.rs index 94e4e53b..c3bc4c5f 100644 --- a/examples/greeter/src/greeter/server.rs +++ b/examples/greeter/src/greeter/server.rs @@ -22,7 +22,7 @@ use futures_util::{Stream, StreamExt}; use tokio::sync::mpsc; use tokio_stream::wrappers::ReceiverStream; -use dubbo::{codegen::*, Dubbo}; +use dubbo::{codegen::*, Dubbo, registry::memory_registry::MemoryRegistry}; use dubbo_config::RootConfig; use dubbo_logger::{ tracing::{info, span}, @@ -50,7 +50,7 @@ async fn main() { register_server(GreeterServerImpl { name: "greeter".to_string(), }); - let zkr = ZookeeperRegistry::default(); + // let zkr: ZookeeperRegistry = ZookeeperRegistry::default(); let r = RootConfig::new(); let r = match r.load() { Ok(config) => config, @@ -58,7 +58,9 @@ async fn main() { }; let mut f = Dubbo::new() .with_config(r) - .add_registry("zookeeper", Box::new(zkr)); + .add_registry("memory_registry", Box::new(MemoryRegistry::new())); + + f.start().await; } From e7f2791e79802fb387f977d244163fab041142fa Mon Sep 17 00:00:00 2001 From: yangyang <962032265@qq.com> Date: Thu, 23 Nov 2023 14:39:41 +0800 Subject: [PATCH 37/48] style(dubbo): cargo fmt --all --- common/base/src/lib.rs | 2 +- dubbo/src/cluster/failover.rs | 23 ++- dubbo/src/cluster/mod.rs | 38 +++-- dubbo/src/codegen.rs | 3 +- dubbo/src/directory/mod.rs | 171 ++++++++++---------- dubbo/src/invoker/clone_body.rs | 50 ++---- dubbo/src/invoker/clone_invoker.rs | 124 +++++++------- dubbo/src/invoker/mod.rs | 6 +- dubbo/src/lib.rs | 12 +- dubbo/src/loadbalancer/mod.rs | 62 ++++--- dubbo/src/param.rs | 5 +- dubbo/src/protocol/mod.rs | 4 +- dubbo/src/protocol/triple/triple_invoker.rs | 36 +++-- dubbo/src/registry/mod.rs | 3 +- dubbo/src/registry/n_registry.rs | 32 ++-- dubbo/src/route/mod.rs | 108 +++++++------ dubbo/src/svc.rs | 22 +-- dubbo/src/triple/client/builder.rs | 41 ++--- dubbo/src/triple/client/triple.rs | 28 ++-- dubbo/src/triple/transport/connection.rs | 31 ++-- 20 files changed, 381 insertions(+), 420 deletions(-) diff --git a/common/base/src/lib.rs b/common/base/src/lib.rs index cb3bc09a..b97b342f 100644 --- a/common/base/src/lib.rs +++ b/common/base/src/lib.rs @@ -23,4 +23,4 @@ pub mod node; pub mod url; pub use node::Node; -pub use url::Url; \ No newline at end of file +pub use url::Url; diff --git a/dubbo/src/cluster/failover.rs b/dubbo/src/cluster/failover.rs index 1a8149c6..bd079bc5 100644 --- a/dubbo/src/cluster/failover.rs +++ b/dubbo/src/cluster/failover.rs @@ -2,18 +2,17 @@ use std::task::Poll; use futures_util::future; use http::Request; -use tower::{ServiceExt, retry::Retry, util::Oneshot}; +use tower::{retry::Retry, util::Oneshot, ServiceExt}; use tower_service::Service; - + use crate::StdError; pub struct Failover { - inner: N // loadbalancer service + inner: N, // loadbalancer service } #[derive(Clone)] pub struct FailoverPolicy; - impl Failover { pub fn new(inner: N) -> Self { @@ -25,14 +24,13 @@ impl tower::retry::Policy, Res, E> for FailoverPolicy where B: http_body::Body + Clone, { - type Future = future::Ready; fn retry(&self, req: &Request, result: Result<&Res, &E>) -> Option { //TODO some error handling or logging match result { Ok(_) => None, - Err(_) => Some(future::ready(self.clone())) + Err(_) => Some(future::ready(self.clone())), } } @@ -43,21 +41,18 @@ where *clone.headers_mut() = req.headers().clone(); *clone.version_mut() = req.version(); - Some(clone) } } - - -impl Service> for Failover +impl Service> for Failover where // B is CloneBody B: http_body::Body + Clone, // loadbalancer service - N: Service> + Clone + 'static , + N: Service> + Clone + 'static, N::Error: Into, - N::Future: Send + N::Future: Send, { type Response = N::Response; @@ -68,9 +63,9 @@ where fn poll_ready(&mut self, cx: &mut std::task::Context<'_>) -> Poll> { self.inner.poll_ready(cx) } - + fn call(&mut self, req: Request) -> Self::Future { let retry = Retry::new(FailoverPolicy, self.inner.clone()); retry.oneshot(req) } -} \ No newline at end of file +} diff --git a/dubbo/src/cluster/mod.rs b/dubbo/src/cluster/mod.rs index 0b1654a0..1a20c160 100644 --- a/dubbo/src/cluster/mod.rs +++ b/dubbo/src/cluster/mod.rs @@ -15,11 +15,12 @@ * limitations under the License. */ - use http::Request; use tower_service::Service; -use crate::{codegen::RpcInvocation, svc::NewService, param::Param, invoker::clone_body::CloneBody}; +use crate::{ + codegen::RpcInvocation, invoker::clone_body::CloneBody, param::Param, svc::NewService, +}; use self::failover::Failover; @@ -30,12 +31,10 @@ pub struct NewCluster { } pub struct Cluster { - inner: S // failover service + inner: S, // failover service } - impl NewCluster { - pub fn layer() -> impl tower_layer::Layer { tower_layer::layer_fn(|inner: N| { NewCluster { @@ -43,45 +42,44 @@ impl NewCluster { } }) } +} -} - -impl NewService for NewCluster +impl NewService for NewCluster where - T: Param, + T: Param, // new loadbalancer service S: NewService, -{ - +{ type Service = Cluster>; - - fn new_service(&self, target: T) -> Self::Service { + fn new_service(&self, target: T) -> Self::Service { Cluster { - inner: Failover::new(self.inner.new_service(target)) + inner: Failover::new(self.inner.new_service(target)), } } } - -impl Service> for Cluster + +impl Service> for Cluster where S: Service>, { - type Response = S::Response; type Error = S::Error; type Future = S::Future; - fn poll_ready(&mut self, cx: &mut std::task::Context<'_>) -> std::task::Poll> { + fn poll_ready( + &mut self, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { self.inner.poll_ready(cx) } - + fn call(&mut self, req: Request) -> Self::Future { let (parts, body) = req.into_parts(); let clone_body = CloneBody::new(body); - let req = Request::from_parts(parts, clone_body); + let req = Request::from_parts(parts, clone_body); self.inner.call(req) } } diff --git a/dubbo/src/codegen.rs b/dubbo/src/codegen.rs index 38f8f1dc..452f560d 100644 --- a/dubbo/src/codegen.rs +++ b/dubbo/src/codegen.rs @@ -44,8 +44,7 @@ pub use super::{ pub use crate::{ filter::{service::FilterService, Filter}, triple::{ - client::builder::ClientBuilder, - server::builder::ServerBuilder, + client::builder::ClientBuilder, server::builder::ServerBuilder, transport::connection::Connection, }, }; diff --git a/dubbo/src/directory/mod.rs b/dubbo/src/directory/mod.rs index a4cf4669..7b6aaf73 100644 --- a/dubbo/src/directory/mod.rs +++ b/dubbo/src/directory/mod.rs @@ -16,107 +16,109 @@ */ use std::{ - task::{Context, Poll}, collections::HashMap, sync::{Arc, Mutex}, pin::Pin, + collections::HashMap, + pin::Pin, + sync::{Arc, Mutex}, + task::{Context, Poll}, +}; + +use crate::{ + codegen::{RpcInvocation, TripleInvoker}, + invocation::Invocation, + invoker::{clone_invoker::CloneInvoker, NewInvoker}, + param::Param, + registry::n_registry::Registry, + svc::NewService, + StdError, }; - -use crate::{StdError, codegen::{RpcInvocation, TripleInvoker}, invocation::Invocation, registry::n_registry::Registry, invoker::{NewInvoker,clone_invoker::CloneInvoker}, svc::NewService, param::Param}; use dubbo_logger::tracing::debug; use futures_core::ready; use futures_util::future; use tokio::sync::mpsc::channel; use tokio_stream::wrappers::ReceiverStream; use tower::{ - discover::{Change, Discover}, buffer::Buffer, + buffer::Buffer, + discover::{Change, Discover}, }; use tower_service::Service; -type BufferedDirectory = Buffer, StdError>>>, ()>; +type BufferedDirectory = + Buffer, StdError>>>, ()>; pub struct NewCachedDirectory where N: Registry + Clone + Send + Sync + 'static, { - inner: CachedDirectory, RpcInvocation> + inner: CachedDirectory, RpcInvocation>, } - -pub struct CachedDirectory +pub struct CachedDirectory where - // NewDirectory - N: NewService + // NewDirectory + N: NewService, { inner: N, - cache: Arc>> + cache: Arc>>, } - pub struct NewDirectory { // registry inner: N, } - pub struct Directory { directory: HashMap>, discover: D, new_invoker: NewInvoker, } - - -impl NewCachedDirectory +impl NewCachedDirectory where N: Registry + Clone + Send + Sync + 'static, { - pub fn layer() -> impl tower_layer::Layer { tower_layer::layer_fn(|inner: N| { NewCachedDirectory { - // inner is registry - inner: CachedDirectory::new(NewDirectory::new(inner)), + // inner is registry + inner: CachedDirectory::new(NewDirectory::new(inner)), } }) } } - impl NewService for NewCachedDirectory where T: Param, // service registry N: Registry + Clone + Send + Sync + 'static, { - type Service = BufferedDirectory; + type Service = BufferedDirectory; fn new_service(&self, target: T) -> Self::Service { - self.inner.new_service(target.param()) } -} - - -impl CachedDirectory +} + +impl CachedDirectory where - N: NewService + N: NewService, { - pub fn new(inner: N) -> Self { CachedDirectory { inner, - cache: Default::default() + cache: Default::default(), } - } -} - - -impl NewService for CachedDirectory + } +} + +impl NewService for CachedDirectory where T: Param, // NewDirectory N: NewService, // Buffered directory - N::Service: Clone + N::Service: Clone, { type Service = N::Service; @@ -124,44 +126,35 @@ where let rpc_invocation = target.param(); let service_name = rpc_invocation.get_target_service_unique_name(); let mut cache = self.cache.lock().expect("cached directory lock failed."); - let value = cache.get(&service_name).map(|val|val.clone()); + let value = cache.get(&service_name).map(|val| val.clone()); match value { None => { let new_service = self.inner.new_service(target); cache.insert(service_name, new_service.clone()); new_service - }, - Some(value) => value - } + } + Some(value) => value, + } } } - impl NewDirectory { - const MAX_DIRECTORY_BUFFER_SIZE: usize = 16; pub fn new(inner: N) -> Self { - NewDirectory { - inner - } + NewDirectory { inner } } -} - - +} -impl NewService for NewDirectory +impl NewService for NewDirectory where T: Param, // service registry - N: Registry + Clone + Send + Sync + 'static, + N: Registry + Clone + Send + Sync + 'static, { - type Service = BufferedDirectory; + type Service = BufferedDirectory; - - fn new_service(&self, target: T) -> Self::Service { - let service_name = target.param().get_target_service_unique_name(); let registry = self.inner.clone(); @@ -175,36 +168,32 @@ where Err(e) => { // error!("discover stream error: {}", e); debug!("discover stream error"); - }, - Ok(mut receiver) => { - loop { - let change = receiver.recv().await; - debug!("receive change: {:?}", change); - match change { - None => { - debug!("discover stream closed."); - break; - }, - Some(change) => { - let _ = tx.send(change).await; - } + } + Ok(mut receiver) => loop { + let change = receiver.recv().await; + debug!("receive change: {:?}", change); + match change { + None => { + debug!("discover stream closed."); + break; + } + Some(change) => { + let _ = tx.send(change).await; } } - } + }, } + }); - }); - - Buffer::new(Directory::new(ReceiverStream::new(rx)), Self::MAX_DIRECTORY_BUFFER_SIZE) - } - -} - - -impl Directory { + Buffer::new( + Directory::new(ReceiverStream::new(rx)), + Self::MAX_DIRECTORY_BUFFER_SIZE, + ) + } +} +impl Directory { pub fn new(discover: D) -> Self { - Directory { directory: Default::default(), discover, @@ -213,13 +202,12 @@ impl Directory { } } - -impl Service<()> for Directory +impl Service<()> for Directory where // Discover - D: Discover + Unpin + Send, - D::Error: Into -{ + D: Discover + Unpin + Send, + D::Error: Into, +{ type Response = Vec>; type Error = StdError; @@ -227,21 +215,22 @@ where type Future = future::Ready>; fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { - loop { let pin_discover = Pin::new(&mut self.discover); - let change = ready!(pin_discover.poll_discover(cx)).transpose().map_err(|e| e.into())?; + let change = ready!(pin_discover.poll_discover(cx)) + .transpose() + .map_err(|e| e.into())?; match change { Some(Change::Remove(key)) => { debug!("remove key: {}", key); self.directory.remove(&key); - }, + } Some(Change::Insert(key, _)) => { debug!("insert key: {}", key); let invoker = self.new_invoker.new_service(key.clone()); self.directory.insert(key, invoker); - }, - None => { + } + None => { debug!("stream closed"); return Poll::Ready(Ok(())); } @@ -250,7 +239,11 @@ where } fn call(&mut self, _: ()) -> Self::Future { - let vec = self.directory.values().map(|val|val.clone()).collect::>>(); + let vec = self + .directory + .values() + .map(|val| val.clone()) + .collect::>>(); future::ok(vec) } -} \ No newline at end of file +} diff --git a/dubbo/src/invoker/clone_body.rs b/dubbo/src/invoker/clone_body.rs index 5ce2e1f9..4de8f899 100644 --- a/dubbo/src/invoker/clone_body.rs +++ b/dubbo/src/invoker/clone_body.rs @@ -7,20 +7,20 @@ use std::{ use bytes::{Buf, BufMut, Bytes, BytesMut}; use futures_core::ready; - + use http::HeaderMap; use http_body::Body; use pin_project::pin_project; use thiserror::Error; use crate::StdError; - + #[derive(Error, Debug)] -#[error("buffered body reach max capacity.")] +#[error("buffered body reach max capacity.")] pub struct ReachMaxCapacityError; pub struct BufferedBody { - shared: Arc>>, + shared: Arc>>, owned: Option, replay_body: bool, replay_trailers: bool, @@ -34,10 +34,7 @@ pub struct OwnedBufferedBody { buf: InnerBuffer, } - - impl BufferedBody { - pub fn new(body: hyper::Body, buf_size: usize) -> Self { let size_hint = body.size_hint(); let is_empty = body.is_end_stream(); @@ -57,11 +54,9 @@ impl BufferedBody { size_hint, } } - } impl Clone for BufferedBody { - fn clone(&self) -> Self { Self { shared: self.shared.clone(), @@ -86,7 +81,6 @@ impl Drop for BufferedBody { } impl Body for BufferedBody { - type Data = BytesData; type Error = StdError; @@ -106,14 +100,10 @@ impl Body for BufferedBody { data.take().expect("cannot get shared buffered body.") }); - - if mut_self.replay_body { mut_self.replay_body = false; if owned_body.buf.has_remaining() { - return Poll::Ready(Some(Ok(BytesData::BufferedBytes( - owned_body.buf.clone(), - )))); + return Poll::Ready(Some(Ok(BytesData::BufferedBytes(owned_body.buf.clone())))); } if owned_body.buf.is_capped() { @@ -150,10 +140,8 @@ impl Body for BufferedBody { } else { owned_body.buf.push_bytes(data.copy_to_bytes(len)) }; - - Poll::Ready(Some(Ok(BytesData::OriginBytes(data)))) - + Poll::Ready(Some(Ok(BytesData::OriginBytes(data)))) } fn poll_trailers( @@ -170,7 +158,7 @@ impl Body for BufferedBody { data.take().expect("cannot get shared buffered body.") }); - + if mut_self.replay_trailers { mut_self.replay_trailers = false; if let Some(ref trailers) = owned_body.trailers { @@ -184,7 +172,7 @@ impl Body for BufferedBody { owned_body.trailers = trailers.clone(); trailers }); - return Poll::Ready(trailers.map_err(|e|e.into())); + return Poll::Ready(trailers.map_err(|e| e.into())); } Poll::Ready(Ok(None)) @@ -195,9 +183,11 @@ impl Body for BufferedBody { return true; } - let is_end = self.owned.as_ref() - .map(|owned|owned.body.is_end_stream()) - .unwrap_or(false); + let is_end = self + .owned + .as_ref() + .map(|owned| owned.body.is_end_stream()) + .unwrap_or(false); !self.replay_body && !self.replay_trailers && is_end } @@ -205,12 +195,8 @@ impl Body for BufferedBody { fn size_hint(&self) -> http_body::SizeHint { self.size_hint.clone() } - - } - - #[derive(Clone)] pub struct InnerBuffer { bufs: VecDeque, @@ -328,13 +314,12 @@ pub struct CloneBody(#[pin] BufferedBody); impl CloneBody { pub fn new(inner_body: hyper::Body) -> Self { - let inner_body = BufferedBody::new(inner_body, 1024 * 64); + let inner_body = BufferedBody::new(inner_body, 1024 * 64); CloneBody(inner_body) } } -impl Body for CloneBody{ - +impl Body for CloneBody { type Data = BytesData; type Error = StdError; @@ -350,7 +335,7 @@ impl Body for CloneBody{ self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll, Self::Error>> { - self.project().0.poll_trailers(cx) + self.project().0.poll_trailers(cx) } fn size_hint(&self) -> http_body::SizeHint { @@ -358,9 +343,8 @@ impl Body for CloneBody{ } } - impl Clone for CloneBody { fn clone(&self) -> Self { Self(self.0.clone()) } -} \ No newline at end of file +} diff --git a/dubbo/src/invoker/clone_invoker.rs b/dubbo/src/invoker/clone_invoker.rs index fe621b87..c1fa00d8 100644 --- a/dubbo/src/invoker/clone_invoker.rs +++ b/dubbo/src/invoker/clone_invoker.rs @@ -1,76 +1,81 @@ -use std::{task::Poll, pin::Pin, mem}; +use std::{mem, pin::Pin, task::Poll}; use dubbo_logger::tracing::debug; -use futures_core::{Future, TryFuture, ready, future::BoxFuture}; +use futures_core::{future::BoxFuture, ready, Future, TryFuture}; use futures_util::FutureExt; use pin_project::pin_project; use thiserror::Error; -use tokio::{task::JoinHandle, sync::{watch::{Sender, Receiver}, self}}; +use tokio::{ + sync::{ + self, + watch::{Receiver, Sender}, + }, + task::JoinHandle, +}; use tokio_util::sync::ReusableBoxFuture; -use tower::{ServiceExt, buffer::Buffer}; +use tower::{buffer::Buffer, ServiceExt}; use tower_service::Service; use crate::StdError; use super::clone_body::CloneBody; - + enum Inner { Invalid, Ready(S), Pending(JoinHandle>), -} +} #[derive(Debug, Error)] #[error("the inner service has not got ready yet!")] struct InnerServiceNotReadyErr; - - #[pin_project(project = InnerServiceCallingResponseProj)] enum InnerServiceCallingResponse { Call(#[pin] Fut), - Fail + Fail, } -impl Future for InnerServiceCallingResponse +impl Future for InnerServiceCallingResponse where Fut: TryFuture, - Fut::Error: Into + Fut::Error: Into, { type Output = Result; - + fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll { - match self.project() { - InnerServiceCallingResponseProj::Call(call) => call.try_poll(cx).map_err(Into::into), - InnerServiceCallingResponseProj::Fail => Poll::Ready(Err(InnerServiceNotReadyErr.into())) - } + match self.project() { + InnerServiceCallingResponseProj::Call(call) => call.try_poll(cx).map_err(Into::into), + InnerServiceCallingResponseProj::Fail => { + Poll::Ready(Err(InnerServiceNotReadyErr.into())) + } + } } } - + #[derive(Clone)] enum ObserveState { Ready, Pending, } - struct ReadyService { inner: Inner, - tx: Sender + tx: Sender, } - impl ReadyService { - fn new(inner: S) -> (Self, Receiver) { let (tx, rx) = sync::watch::channel(ObserveState::Ready); - let ready_service = Self { inner: Inner::Ready(inner), tx}; + let ready_service = Self { + inner: Inner::Ready(inner), + tx, + }; (ready_service, rx) } - } -impl Service for ReadyService +impl Service for ReadyService where S: Service + Send + 'static, >::Error: Into, @@ -85,16 +90,14 @@ where loop { match mem::replace(&mut self.inner, Inner::Invalid) { Inner::Ready(mut svc) => { - let poll_ready = svc.poll_ready(cx); + let poll_ready = svc.poll_ready(cx); match poll_ready { Poll::Pending => { self.inner = Inner::Pending(tokio::spawn(async move { let poll_ready = svc.ready().await; match poll_ready { Ok(_) => Ok(svc), - Err(err) => { - Err((svc, err.into())) - } + Err(err) => Err((svc, err.into())), } })); @@ -108,17 +111,17 @@ where return Poll::Ready(ret.map_err(Into::into)); } } - }, + } Inner::Pending(mut join_handle) => { if let Poll::Ready(res) = join_handle.poll_unpin(cx) { let (svc, res) = match res { Err(join_err) => panic!("ReadyService panicked: {join_err}"), Ok(Err((svc, err))) => (svc, Poll::Ready(Err(err))), - Ok(Ok(svc)) => (svc, Poll::Ready(Ok(()))) + Ok(Ok(svc)) => (svc, Poll::Ready(Ok(()))), }; self.inner = Inner::Ready(svc); - + let _ = self.tx.send(ObserveState::Ready); return res; } else { @@ -127,31 +130,29 @@ where let _ = self.tx.send(ObserveState::Pending); return Poll::Pending; } - - }, - Inner::Invalid => panic!("ReadyService panicked: inner state is invalid") + } + Inner::Invalid => panic!("ReadyService panicked: inner state is invalid"), } } } fn call(&mut self, req: Req) -> Self::Future { - match self.inner { - Inner::Ready(ref mut svc) => InnerServiceCallingResponse::Call(svc.call(req)), - _ => InnerServiceCallingResponse::Fail - } + match self.inner { + Inner::Ready(ref mut svc) => InnerServiceCallingResponse::Call(svc.call(req)), + _ => InnerServiceCallingResponse::Fail, + } } } - impl Drop for ReadyService { fn drop(&mut self) { - if let Inner::Pending(ref handler) = self.inner { + if let Inner::Pending(ref handler) = self.inner { handler.abort(); - } + } } } -pub struct CloneInvoker +pub struct CloneInvoker where Inv: Service> + Send + 'static, Inv::Error: Into + Send + Sync + 'static, @@ -163,40 +164,44 @@ where polling: bool, } -impl CloneInvoker +impl CloneInvoker where Inv: Service> + Send + 'static, Inv::Error: Into + Send + Sync + 'static, Inv::Future: Send, { - const MAX_INVOKER_BUFFER_SIZE: usize = 16; - + pub fn new(invoker: Inv) -> Self { - let (ready_service, rx) = ReadyService::new(invoker); - let buffer: Buffer, http::Request> = Buffer::new(ready_service, Self::MAX_INVOKER_BUFFER_SIZE); + let buffer: Buffer, http::Request> = + Buffer::new(ready_service, Self::MAX_INVOKER_BUFFER_SIZE); - Self { inner: buffer, rx, polling: false, poll: ReusableBoxFuture::new(futures::future::pending()) } + Self { + inner: buffer, + rx, + polling: false, + poll: ReusableBoxFuture::new(futures::future::pending()), + } } } -impl Service> for CloneInvoker +impl Service> for CloneInvoker where Inv: Service> + Send + 'static, Inv::Error: Into + Send + Sync + 'static, Inv::Future: Send, { type Response = Inv::Response; - + type Error = StdError; - + type Future = BoxFuture<'static, Result>; fn poll_ready(&mut self, cx: &mut std::task::Context<'_>) -> Poll> { loop { - if !self.polling { + if !self.polling { match self.rx.borrow().clone() { ObserveState::Ready => return self.inner.poll_ready(cx), ObserveState::Pending => { @@ -206,7 +211,7 @@ where loop { let current_state = rx.borrow_and_update().clone(); if matches!(current_state, ObserveState::Ready) { - return current_state; + return current_state; } if let Err(_) = rx.changed().await { debug!("the readyService has already shutdown!"); @@ -216,7 +221,6 @@ where }); } } - } let state = ready!(self.poll.poll_unpin(cx)); @@ -235,14 +239,18 @@ where } } - -impl Clone for CloneInvoker +impl Clone for CloneInvoker where Inv: Service> + Send + 'static, Inv::Error: Into + Send + Sync + 'static, Inv::Future: Send, { fn clone(&self) -> Self { - Self { inner: self.inner.clone(), rx: self.rx.clone(), polling: false, poll: ReusableBoxFuture::new(futures::future::pending())} + Self { + inner: self.inner.clone(), + rx: self.rx.clone(), + polling: false, + poll: ReusableBoxFuture::new(futures::future::pending()), + } } -} \ No newline at end of file +} diff --git a/dubbo/src/invoker/mod.rs b/dubbo/src/invoker/mod.rs index a8179eee..92b8b462 100644 --- a/dubbo/src/invoker/mod.rs +++ b/dubbo/src/invoker/mod.rs @@ -1,14 +1,12 @@ use dubbo_base::Url; -use crate::{codegen::TripleInvoker, svc::NewService, invoker::clone_invoker::CloneInvoker}; +use crate::{codegen::TripleInvoker, invoker::clone_invoker::CloneInvoker, svc::NewService}; pub mod clone_body; pub mod clone_invoker; - pub struct NewInvoker; - impl NewService for NewInvoker { type Service = CloneInvoker; @@ -18,4 +16,4 @@ impl NewService for NewInvoker { let url = Url::from_url(&url).unwrap(); CloneInvoker::new(TripleInvoker::new(url)) } -} \ No newline at end of file +} diff --git a/dubbo/src/lib.rs b/dubbo/src/lib.rs index 252e01c7..d397b42b 100644 --- a/dubbo/src/lib.rs +++ b/dubbo/src/lib.rs @@ -18,20 +18,20 @@ pub mod cluster; pub mod codegen; pub mod context; +pub mod directory; pub mod filter; mod framework; pub mod invocation; +pub mod invoker; +pub mod loadbalancer; +pub mod param; pub mod protocol; pub mod registry; +pub mod route; pub mod status; +pub mod svc; pub mod triple; pub mod utils; -pub mod directory; -pub mod route; -pub mod loadbalancer; -pub mod invoker; -pub mod param; -pub mod svc; use http_body::Body; use std::{future::Future, pin::Pin}; diff --git a/dubbo/src/loadbalancer/mod.rs b/dubbo/src/loadbalancer/mod.rs index 334350e3..4e26781d 100644 --- a/dubbo/src/loadbalancer/mod.rs +++ b/dubbo/src/loadbalancer/mod.rs @@ -1,16 +1,20 @@ use futures_core::future::BoxFuture; -use tower::ServiceExt; -use tower::discover::ServiceList; +use tower::{discover::ServiceList, ServiceExt}; use tower_service::Service; -use crate::invoker::clone_body::CloneBody; -use crate::{codegen::RpcInvocation, StdError, svc::NewService, param::Param, invoker::clone_invoker::CloneInvoker}; +use crate::{ + codegen::RpcInvocation, + invoker::{clone_body::CloneBody, clone_invoker::CloneInvoker}, + param::Param, + svc::NewService, + StdError, +}; use crate::protocol::triple::triple_invoker::TripleInvoker; - + pub struct NewLoadBalancer { inner: N, -} +} #[derive(Clone)] pub struct LoadBalancer { @@ -18,80 +22,74 @@ pub struct LoadBalancer { } impl NewLoadBalancer { - - pub fn layer() -> impl tower_layer::Layer{ - + pub fn layer() -> impl tower_layer::Layer { tower_layer::layer_fn(|inner| { - NewLoadBalancer { - inner // NewRoutes + inner, // NewRoutes } }) } } -impl NewService for NewLoadBalancer +impl NewService for NewLoadBalancer where T: Param + Clone, // NewRoutes - N: NewService, + N: NewService, { - type Service = LoadBalancer; fn new_service(&self, target: T) -> Self::Service { // Routes service let svc = self.inner.new_service(target); - LoadBalancer { - inner: svc, - } - + LoadBalancer { inner: svc } } } -impl Service> for LoadBalancer +impl Service> for LoadBalancer where // Routes service N: Service<(), Response = Vec>> + Clone, N::Error: Into + Send, - N::Future: Send + 'static, + N::Future: Send + 'static, { - type Response = as Service>>::Response; type Error = StdError; type Future = BoxFuture<'static, Result>; - fn poll_ready(&mut self, cx: &mut std::task::Context<'_>) -> std::task::Poll> { + fn poll_ready( + &mut self, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { self.inner.poll_ready(cx).map_err(Into::into) - } fn call(&mut self, req: http::Request) -> Self::Future { - let routes = self.inner.call(()); + let routes = self.inner.call(()); let fut = async move { let routes = routes.await; let routes: Vec> = match routes { Err(e) => return Err(Into::::into(e)), - Ok(routes) => routes + Ok(routes) => routes, }; - - let service_list: Vec<_> = routes.into_iter().map(|invoker| { - tower::load::Constant::new(invoker, 1) - }).collect(); + + let service_list: Vec<_> = routes + .into_iter() + .map(|invoker| tower::load::Constant::new(invoker, 1)) + .collect(); let service_list = ServiceList::new(service_list); - + let p2c = tower::balance::p2c::Balance::new(service_list); - p2c.oneshot(req).await }; - + Box::pin(fut) } } diff --git a/dubbo/src/param.rs b/dubbo/src/param.rs index b57f98eb..bef50419 100644 --- a/dubbo/src/param.rs +++ b/dubbo/src/param.rs @@ -1,12 +1,9 @@ pub trait Param { - fn param(&self) -> T; } - impl Param for T { - fn param(&self) -> T::Owned { - self.to_owned() + self.to_owned() } } diff --git a/dubbo/src/protocol/mod.rs b/dubbo/src/protocol/mod.rs index 9d35a525..7dbf1f3f 100644 --- a/dubbo/src/protocol/mod.rs +++ b/dubbo/src/protocol/mod.rs @@ -15,9 +15,7 @@ * limitations under the License. */ -use std::{ - task::{Context, Poll}, -}; +use std::task::{Context, Poll}; use async_trait::async_trait; use aws_smithy_http::body::SdkBody; diff --git a/dubbo/src/protocol/triple/triple_invoker.rs b/dubbo/src/protocol/triple/triple_invoker.rs index 71eae903..c8451e3c 100644 --- a/dubbo/src/protocol/triple/triple_invoker.rs +++ b/dubbo/src/protocol/triple/triple_invoker.rs @@ -16,14 +16,17 @@ */ use dubbo_base::Url; -use http::{Uri, HeaderValue}; +use http::{HeaderValue, Uri}; use std::{ fmt::{Debug, Formatter}, str::FromStr, }; use tower_service::Service; -use crate::{triple::transport::{connection::Connection, self}, invoker::clone_body::CloneBody}; +use crate::{ + invoker::clone_body::CloneBody, + triple::transport::{self, connection::Connection}, +}; pub struct TripleInvoker { url: Url, @@ -47,18 +50,19 @@ impl Debug for TripleInvoker { } impl TripleInvoker { - pub fn map_request( - &self, - req: http::Request, - ) -> http::Request { - + pub fn map_request(&self, req: http::Request) -> http::Request { let (parts, body) = req.into_parts(); let path_and_query = parts.headers.get("path").unwrap().to_str().unwrap(); - let authority = self.url.clone().get_ip_port(); - - let uri = Uri::builder().scheme("http").authority(authority).path_and_query(path_and_query).build().unwrap(); + let authority = self.url.clone().get_ip_port(); + + let uri = Uri::builder() + .scheme("http") + .authority(authority) + .path_and_query(path_and_query) + .build() + .unwrap(); let mut req = hyper::Request::builder() .version(http::Version::HTTP_2) @@ -99,12 +103,12 @@ impl TripleInvoker { HeaderValue::from_static("dubbo-rust/0.1.0"), ); // if let Some(_encoding) = self.send_compression_encoding { - + // } req.headers_mut() - .insert("grpc-encoding", http::HeaderValue::from_static("gzip")); - + .insert("grpc-encoding", http::HeaderValue::from_static("gzip")); + req.headers_mut().insert( "grpc-accept-encoding", http::HeaderValue::from_static("gzip"), @@ -137,7 +141,10 @@ impl Service> for TripleInvoker { &mut self, cx: &mut std::task::Context<'_>, ) -> std::task::Poll> { - >>::poll_ready(&mut self.conn, cx) + >>::poll_ready( + &mut self.conn, + cx, + ) } fn call(&mut self, req: http::Request) -> Self::Future { @@ -146,4 +153,3 @@ impl Service> for TripleInvoker { self.conn.call(req) } } - diff --git a/dubbo/src/registry/mod.rs b/dubbo/src/registry/mod.rs index ab2297f3..d8ff6ef3 100644 --- a/dubbo/src/registry/mod.rs +++ b/dubbo/src/registry/mod.rs @@ -18,9 +18,9 @@ #![allow(unused_variables, dead_code, missing_docs)] pub mod integration; pub mod memory_registry; +pub mod n_registry; pub mod protocol; pub mod types; -pub mod n_registry; use std::{ fmt::{Debug, Formatter}, @@ -29,7 +29,6 @@ use std::{ use dubbo_base::Url; - pub type RegistryNotifyListener = Arc; pub trait Registry { fn register(&mut self, url: Url) -> Result<(), crate::StdError>; diff --git a/dubbo/src/registry/n_registry.rs b/dubbo/src/registry/n_registry.rs index 9b6dca58..928c9b3c 100644 --- a/dubbo/src/registry/n_registry.rs +++ b/dubbo/src/registry/n_registry.rs @@ -2,19 +2,17 @@ use std::sync::Arc; use async_trait::async_trait; use dubbo_base::Url; -use tokio::sync::mpsc::{Receiver, channel}; +use tokio::sync::mpsc::{channel, Receiver}; use tower::discover::Change; - use crate::StdError; type DiscoverStream = Receiver, StdError>>; #[async_trait] pub trait Registry { - async fn register(&self, url: Url) -> Result<(), StdError>; - + async fn unregister(&self, url: Url) -> Result<(), StdError>; // todo service_name change to url @@ -25,31 +23,29 @@ pub trait Registry { #[derive(Clone)] pub struct ArcRegistry { - inner: Arc + inner: Arc, } - pub enum RegistryComponent { NacosRegistry, ZookeeperRegistry, StaticRegistry(StaticRegistry), } - pub struct StaticRegistry { - urls: Vec + urls: Vec, } impl ArcRegistry { - pub fn new(registry: impl Registry + Send + Sync + 'static) -> Self { - Self { inner: Arc::new(registry) } + Self { + inner: Arc::new(registry), + } } } #[async_trait] impl Registry for ArcRegistry { - async fn register(&self, url: Url) -> Result<(), StdError> { self.inner.register(url).await } @@ -67,9 +63,6 @@ impl Registry for ArcRegistry { } } - - - #[async_trait] impl Registry for RegistryComponent { async fn register(&self, url: Url) -> Result<(), StdError> { @@ -93,17 +86,12 @@ impl Registry for RegistryComponent { } } - impl StaticRegistry { - pub fn new(urls: Vec) -> Self { - Self { - urls - } + Self { urls } } } - #[async_trait] impl Registry for StaticRegistry { async fn register(&self, url: Url) -> Result<(), StdError> { @@ -119,7 +107,7 @@ impl Registry for StaticRegistry { for url in self.urls.iter() { let change = Ok(Change::Insert(url.to_url(), ())); tx.send(change).await?; - } + } Ok(rx) } @@ -127,4 +115,4 @@ impl Registry for StaticRegistry { async fn unsubscribe(&self, url: Url) -> Result<(), StdError> { todo!() } -} \ No newline at end of file +} diff --git a/dubbo/src/route/mod.rs b/dubbo/src/route/mod.rs index cff3ccee..c2448642 100644 --- a/dubbo/src/route/mod.rs +++ b/dubbo/src/route/mod.rs @@ -1,42 +1,51 @@ use std::pin::Pin; use dubbo_logger::tracing::debug; -use futures_core::{Future, ready}; +use futures_core::{ready, Future}; use futures_util::{future::Ready, FutureExt, TryFutureExt}; -use tower::{util::FutureService, buffer::Buffer}; +use tower::{buffer::Buffer, util::FutureService}; use tower_service::Service; - -use crate::{StdError, codegen::{RpcInvocation, TripleInvoker}, svc::NewService, param::Param, invoker::clone_invoker::CloneInvoker}; + +use crate::{ + codegen::{RpcInvocation, TripleInvoker}, + invoker::clone_invoker::CloneInvoker, + param::Param, + svc::NewService, + StdError, +}; pub struct NewRoutes { inner: N, -} - +} pub struct NewRoutesFuture { inner: RoutesFutureInnerState, target: T, } - pub enum RoutesFutureInnerState { Service(S), - Future(Pin>, StdError>> + Send + 'static>>), + Future( + Pin< + Box< + dyn Future>, StdError>> + + Send + + 'static, + >, + >, + ), Ready(Vec>), } - #[derive(Clone)] pub struct Routes { target: T, - invokers: Vec> + invokers: Vec>, } impl NewRoutes { pub fn new(inner: N) -> Self { - Self { - inner, - } + Self { inner } } } @@ -44,40 +53,39 @@ impl NewRoutes { const MAX_ROUTE_BUFFER_SIZE: usize = 16; pub fn layer() -> impl tower_layer::Layer { - tower_layer::layer_fn(|inner: N| { - NewRoutes::new(inner) - }) + tower_layer::layer_fn(|inner: N| NewRoutes::new(inner)) } } - -impl NewService for NewRoutes +impl NewService for NewRoutes where - T: Param + Clone + Send + Unpin + 'static, + T: Param + Clone + Send + Unpin + 'static, // NewDirectory N: NewService, - // Directory + // Directory N::Service: Service<(), Response = Vec>> + Unpin + Send + 'static, - >::Error: Into, + >::Error: Into, >::Future: Send + 'static, -{ - - type Service = Buffer>::Service, T>, Routes>, ()>; +{ + type Service = + Buffer>::Service, T>, Routes>, ()>; fn new_service(&self, target: T) -> Self::Service { let inner = self.inner.new_service(target.clone()); - Buffer::new(FutureService::new(NewRoutesFuture { - inner: RoutesFutureInnerState::Service(inner), - target, - }), Self::MAX_ROUTE_BUFFER_SIZE) + Buffer::new( + FutureService::new(NewRoutesFuture { + inner: RoutesFutureInnerState::Service(inner), + target, + }), + Self::MAX_ROUTE_BUFFER_SIZE, + ) } } - -impl Future for NewRoutesFuture +impl Future for NewRoutesFuture where - T: Param + Clone + Unpin, + T: Param + Clone + Unpin, // Directory N: Service<(), Response = Vec>> + Unpin, N::Error: Into, @@ -85,8 +93,10 @@ where { type Output = Result, StdError>; - fn poll(self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> std::task::Poll { - + fn poll( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll { let this = self.get_mut(); loop { @@ -94,14 +104,14 @@ where RoutesFutureInnerState::Service(ref mut service) => { debug!("RoutesFutureInnerState::Service"); let _ = ready!(service.poll_ready(cx)).map_err(Into::into)?; - let fut = service.call(()).map_err(|e|e.into()).boxed(); + let fut = service.call(()).map_err(|e| e.into()).boxed(); this.inner = RoutesFutureInnerState::Future(fut); - }, + } RoutesFutureInnerState::Future(ref mut futures) => { debug!("RoutesFutureInnerState::Future"); let invokers = ready!(futures.as_mut().poll(cx))?; this.inner = RoutesFutureInnerState::Ready(invokers); - }, + } RoutesFutureInnerState::Ready(ref invokers) => { debug!("RoutesFutureInnerState::Ready"); let target = this.target.clone(); @@ -109,32 +119,32 @@ where invokers: invokers.clone(), target, })); - }, + } } } - } } - - -impl Service<()> for Routes +impl Service<()> for Routes where - T: Param + Clone, -{ + T: Param + Clone, +{ type Response = Vec>; - + type Error = StdError; - - type Future = Ready>; - fn poll_ready(&mut self, _: &mut std::task::Context<'_>) -> std::task::Poll> { + type Future = Ready>; + + fn poll_ready( + &mut self, + _: &mut std::task::Context<'_>, + ) -> std::task::Poll> { std::task::Poll::Ready(Ok(())) } fn call(&mut self, _: ()) -> Self::Future { // some router operator // if new_invokers changed, send new invokers to routes_rx after router operator - futures_util::future::ok(self.invokers.clone()) + futures_util::future::ok(self.invokers.clone()) } -} \ No newline at end of file +} diff --git a/dubbo/src/svc.rs b/dubbo/src/svc.rs index 56dc3304..fb8f02b0 100644 --- a/dubbo/src/svc.rs +++ b/dubbo/src/svc.rs @@ -1,23 +1,19 @@ -use std::{sync::Arc, pin::Pin, marker::PhantomData}; +use std::{marker::PhantomData, pin::Pin, sync::Arc}; use futures_core::Future; use tower::ServiceExt; use tower_service::Service; pub trait NewService { - type Service; fn new_service(&self, target: T) -> Self::Service; - } - pub struct ArcNewService { inner: Arc + Send + Sync>, } - impl ArcNewService { pub fn layer() -> impl tower_layer::Layer + Clone + Copy where @@ -57,23 +53,19 @@ impl NewService for ArcNewService { // inner: Box> + Send>>> + Send>, pub struct BoxedService { inner: N, - _mark: PhantomData + _mark: PhantomData, } impl BoxedService { - - pub fn layer() -> impl tower_layer::Layer{ - tower_layer::layer_fn(|inner: N| { - Self { - inner, - _mark: PhantomData - } + pub fn layer() -> impl tower_layer::Layer { + tower_layer::layer_fn(|inner: N| Self { + inner, + _mark: PhantomData, }) } } - -// impl NewService for BoxedService +// impl NewService for BoxedService // where // N: NewService, // N::Service: Service + Send, diff --git a/dubbo/src/triple/client/builder.rs b/dubbo/src/triple/client/builder.rs index ed080217..10b7fc58 100644 --- a/dubbo/src/triple/client/builder.rs +++ b/dubbo/src/triple/client/builder.rs @@ -15,11 +15,18 @@ * limitations under the License. */ - use std::sync::Arc; use crate::{ - utils::boxed_clone::BoxCloneService, registry::n_registry::{RegistryComponent, StaticRegistry, ArcRegistry}, route::NewRoutes, loadbalancer::NewLoadBalancer, cluster::{NewCluster, Cluster}, directory::NewCachedDirectory, svc::{ArcNewService, NewService, BoxedService}, StdError, codegen::RpcInvocation, BoxBody, + cluster::{Cluster, NewCluster}, + codegen::RpcInvocation, + directory::NewCachedDirectory, + loadbalancer::NewLoadBalancer, + registry::n_registry::{ArcRegistry, RegistryComponent, StaticRegistry}, + route::NewRoutes, + svc::{ArcNewService, BoxedService, NewService}, + utils::boxed_clone::BoxCloneService, + BoxBody, StdError, }; use aws_smithy_http::body::SdkBody; @@ -29,7 +36,6 @@ use tower::ServiceBuilder; pub type ClientBoxService = BoxCloneService, http::Response, crate::Error>; - pub type ServiceMK = Arc>>>>; #[derive(Default)] @@ -56,7 +62,9 @@ impl ClientBuilder { Self { timeout: None, connector: "", - registry: Some(ArcRegistry::new(RegistryComponent::StaticRegistry(StaticRegistry::new(vec![Url::from_url(host).unwrap()])))), + registry: Some(ArcRegistry::new(RegistryComponent::StaticRegistry( + StaticRegistry::new(vec![Url::from_url(host).unwrap()]), + ))), direct: true, host: host.to_string(), } @@ -70,24 +78,23 @@ impl ClientBuilder { } pub fn with_registry(self, registry: RegistryComponent) -> Self { - Self { - registry: Some(ArcRegistry::new(registry)), + Self { + registry: Some(ArcRegistry::new(registry)), ..self } } pub fn with_host(self, host: &'static str) -> Self { Self { - registry: Some(ArcRegistry::new(RegistryComponent::StaticRegistry(StaticRegistry::new(vec![Url::from_url(host).unwrap()])))), + registry: Some(ArcRegistry::new(RegistryComponent::StaticRegistry( + StaticRegistry::new(vec![Url::from_url(host).unwrap()]), + ))), ..self } } pub fn with_connector(self, connector: &'static str) -> Self { - Self { - connector, - ..self - } + Self { connector, ..self } } pub fn with_direct(self, direct: bool) -> Self { @@ -95,16 +102,14 @@ impl ClientBuilder { } pub fn build(mut self) -> ServiceMK { - - let registry = self.registry.take().expect("registry must not be empty"); let mk_service = ServiceBuilder::new() - .layer(NewCluster::layer()) - .layer(NewLoadBalancer::layer()) - .layer(NewRoutes::layer()) - .layer(NewCachedDirectory::layer()) - .service(registry); + .layer(NewCluster::layer()) + .layer(NewLoadBalancer::layer()) + .layer(NewRoutes::layer()) + .layer(NewCachedDirectory::layer()) + .service(registry); Arc::new(mk_service) } diff --git a/dubbo/src/triple/client/triple.rs b/dubbo/src/triple/client/triple.rs index 9bcf144d..377dc435 100644 --- a/dubbo/src/triple/client/triple.rs +++ b/dubbo/src/triple/client/triple.rs @@ -15,7 +15,6 @@ * limitations under the License. */ - use futures_util::{future, stream, StreamExt, TryStreamExt}; use aws_smithy_http::body::SdkBody; @@ -25,9 +24,9 @@ use tower_service::Service; use super::builder::{ClientBuilder, ServiceMK}; use crate::codegen::RpcInvocation; -use crate::svc::NewService; use crate::{ invocation::{IntoStreamingRequest, Metadata, Request, Response}, + svc::NewService, triple::{codec::Codec, compression::CompressionEncoding, decode::Decoding, encode::encode}, }; @@ -148,15 +147,12 @@ impl TripleClient { .into_stream(); let body = hyper::Body::wrap_stream(body_stream); - let mut invoker = self.mk.new_service(invocation); - let request = http::Request::builder() .header("path", path.to_string()) - .body(body).unwrap(); - - + .body(body) + .unwrap(); let response = invoker .call(request) @@ -211,14 +207,13 @@ impl TripleClient { ) .into_stream(); let body = hyper::Body::wrap_stream(en); - - let mut invoker = self.mk.new_service(invocation); + let mut invoker = self.mk.new_service(invocation); let request = http::Request::builder() .header("path", path.to_string()) - .body(body).unwrap(); - + .body(body) + .unwrap(); let response = invoker .call(request) @@ -259,12 +254,10 @@ impl TripleClient { let body = hyper::Body::wrap_stream(en); let mut invoker = self.mk.new_service(invocation); - let request = http::Request::builder() .header("path", path.to_string()) - .body(body).unwrap(); - - + .body(body) + .unwrap(); // let mut conn = Connection::new().with_host(http_uri); let response = invoker @@ -322,11 +315,10 @@ impl TripleClient { let body = hyper::Body::wrap_stream(en); let mut invoker = self.mk.new_service(invocation); - let request = http::Request::builder() .header("path", path.to_string()) - .body(body).unwrap(); - + .body(body) + .unwrap(); let response = invoker .call(request) diff --git a/dubbo/src/triple/transport/connection.rs b/dubbo/src/triple/transport/connection.rs index 3bbaa179..cb0b9d71 100644 --- a/dubbo/src/triple/transport/connection.rs +++ b/dubbo/src/triple/transport/connection.rs @@ -18,9 +18,15 @@ use hyper::client::{conn::Builder, service::Connect}; use tower_service::Service; -use crate::{boxed, triple::transport::connector::get_connector, StdError, invoker::clone_body::CloneBody}; +use crate::{ + boxed, invoker::clone_body::CloneBody, triple::transport::connector::get_connector, StdError, +}; -type HyperConnect = Connect, CloneBody, http::Uri>; +type HyperConnect = Connect< + crate::utils::boxed_clone::BoxCloneService, + CloneBody, + http::Uri, +>; pub struct Connection { host: hyper::Uri, @@ -65,13 +71,10 @@ impl Connection { let hyper_connect: HyperConnect = Connect::new(get_connector(self.connector), builder); self.connect = Some(hyper_connect); self - } } impl Service> for Connection { - - type Response = http::Response; type Error = crate::Error; @@ -85,30 +88,28 @@ impl Service> for Connection { match self.connect { None => { panic!("connection must be built before use") - }, - Some(ref mut connect) => { - connect.poll_ready(cx).map_err(|e|e.into()) } + Some(ref mut connect) => connect.poll_ready(cx).map_err(|e| e.into()), } } fn call(&mut self, req: http::Request) -> Self::Future { - match self.connect { None => { panic!("connection must be built before use") - }, + } Some(ref mut connect) => { let uri = self.host.clone(); let call_fut = connect.call(uri); let fut = async move { - let mut con = call_fut.await.unwrap(); - con.call(req).await - .map_err(|err| err.into()) - .map(|res| res.map(boxed)) + let mut con = call_fut.await.unwrap(); + con.call(req) + .await + .map_err(|err| err.into()) + .map(|res| res.map(boxed)) }; - return Box::pin(fut) + return Box::pin(fut); } } } From 74f915e31e78f63e8703dc062bb2adae8ea54ccd Mon Sep 17 00:00:00 2001 From: yangyang <962032265@qq.com> Date: Thu, 7 Dec 2023 19:51:27 +0800 Subject: [PATCH 38/48] style(dubbo): cargo fix --lib -p dubbo --allow-dirty --- dubbo/src/cluster/failover.rs | 2 +- dubbo/src/directory/mod.rs | 2 +- dubbo/src/svc.rs | 8 ++++---- dubbo/src/triple/client/builder.rs | 5 +---- 4 files changed, 7 insertions(+), 10 deletions(-) diff --git a/dubbo/src/cluster/failover.rs b/dubbo/src/cluster/failover.rs index bd079bc5..8a00c9fb 100644 --- a/dubbo/src/cluster/failover.rs +++ b/dubbo/src/cluster/failover.rs @@ -26,7 +26,7 @@ where { type Future = future::Ready; - fn retry(&self, req: &Request, result: Result<&Res, &E>) -> Option { + fn retry(&self, _req: &Request, result: Result<&Res, &E>) -> Option { //TODO some error handling or logging match result { Ok(_) => None, diff --git a/dubbo/src/directory/mod.rs b/dubbo/src/directory/mod.rs index 7b6aaf73..84900aab 100644 --- a/dubbo/src/directory/mod.rs +++ b/dubbo/src/directory/mod.rs @@ -165,7 +165,7 @@ where let receiver = registry.subscribe(service_name).await; debug!("discover start!"); match receiver { - Err(e) => { + Err(_e) => { // error!("discover stream error: {}", e); debug!("discover stream error"); } diff --git a/dubbo/src/svc.rs b/dubbo/src/svc.rs index fb8f02b0..df4c071a 100644 --- a/dubbo/src/svc.rs +++ b/dubbo/src/svc.rs @@ -1,8 +1,8 @@ -use std::{marker::PhantomData, pin::Pin, sync::Arc}; +use std::{marker::PhantomData, sync::Arc}; + + + -use futures_core::Future; -use tower::ServiceExt; -use tower_service::Service; pub trait NewService { type Service; diff --git a/dubbo/src/triple/client/builder.rs b/dubbo/src/triple/client/builder.rs index 10b7fc58..c4e6e62c 100644 --- a/dubbo/src/triple/client/builder.rs +++ b/dubbo/src/triple/client/builder.rs @@ -18,15 +18,12 @@ use std::sync::Arc; use crate::{ - cluster::{Cluster, NewCluster}, - codegen::RpcInvocation, + cluster::NewCluster, directory::NewCachedDirectory, loadbalancer::NewLoadBalancer, registry::n_registry::{ArcRegistry, RegistryComponent, StaticRegistry}, route::NewRoutes, - svc::{ArcNewService, BoxedService, NewService}, utils::boxed_clone::BoxCloneService, - BoxBody, StdError, }; use aws_smithy_http::body::SdkBody; From 637c7f22612ef77fcc7eb6b7f7a78c39e394d48f Mon Sep 17 00:00:00 2001 From: yangyang <962032265@qq.com> Date: Fri, 8 Dec 2023 19:14:53 +0800 Subject: [PATCH 39/48] chore(github): update branch in pr --- .github/workflows/github-actions.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/github-actions.yml b/.github/workflows/github-actions.yml index 19d8c4d0..878a5e13 100644 --- a/.github/workflows/github-actions.yml +++ b/.github/workflows/github-actions.yml @@ -6,7 +6,9 @@ on: push: branches: ["*"] pull_request: - branches: ["*"] + branches: + - '*' + - 'refact/*' jobs: From 9b93de56f296da3c1c2a55915c700e36b86ac3bb Mon Sep 17 00:00:00 2001 From: Yang Yang <962032265@qq.com> Date: Sun, 10 Dec 2023 13:53:45 +0800 Subject: [PATCH 40/48] Mod: format code and fix some warnings (#167) * style(dubbo): cargo fmt --all * style(dubbo): cargo fix --lib -p dubbo --allow-dirty * chore(github): update branch in pr --- .github/workflows/github-actions.yml | 4 +- common/base/src/lib.rs | 2 +- dubbo/src/cluster/failover.rs | 25 ++- dubbo/src/cluster/mod.rs | 38 ++--- dubbo/src/codegen.rs | 3 +- dubbo/src/directory/mod.rs | 173 ++++++++++---------- dubbo/src/invoker/clone_body.rs | 50 ++---- dubbo/src/invoker/clone_invoker.rs | 124 +++++++------- dubbo/src/invoker/mod.rs | 6 +- dubbo/src/lib.rs | 12 +- dubbo/src/loadbalancer/mod.rs | 62 ++++--- dubbo/src/param.rs | 5 +- dubbo/src/protocol/mod.rs | 4 +- dubbo/src/protocol/triple/triple_invoker.rs | 36 ++-- dubbo/src/registry/mod.rs | 3 +- dubbo/src/registry/n_registry.rs | 32 ++-- dubbo/src/route/mod.rs | 108 ++++++------ dubbo/src/svc.rs | 28 ++-- dubbo/src/triple/client/builder.rs | 38 +++-- dubbo/src/triple/client/triple.rs | 28 ++-- dubbo/src/triple/transport/connection.rs | 31 ++-- 21 files changed, 386 insertions(+), 426 deletions(-) diff --git a/.github/workflows/github-actions.yml b/.github/workflows/github-actions.yml index 19d8c4d0..878a5e13 100644 --- a/.github/workflows/github-actions.yml +++ b/.github/workflows/github-actions.yml @@ -6,7 +6,9 @@ on: push: branches: ["*"] pull_request: - branches: ["*"] + branches: + - '*' + - 'refact/*' jobs: diff --git a/common/base/src/lib.rs b/common/base/src/lib.rs index cb3bc09a..b97b342f 100644 --- a/common/base/src/lib.rs +++ b/common/base/src/lib.rs @@ -23,4 +23,4 @@ pub mod node; pub mod url; pub use node::Node; -pub use url::Url; \ No newline at end of file +pub use url::Url; diff --git a/dubbo/src/cluster/failover.rs b/dubbo/src/cluster/failover.rs index 1a8149c6..8a00c9fb 100644 --- a/dubbo/src/cluster/failover.rs +++ b/dubbo/src/cluster/failover.rs @@ -2,18 +2,17 @@ use std::task::Poll; use futures_util::future; use http::Request; -use tower::{ServiceExt, retry::Retry, util::Oneshot}; +use tower::{retry::Retry, util::Oneshot, ServiceExt}; use tower_service::Service; - + use crate::StdError; pub struct Failover { - inner: N // loadbalancer service + inner: N, // loadbalancer service } #[derive(Clone)] pub struct FailoverPolicy; - impl Failover { pub fn new(inner: N) -> Self { @@ -25,14 +24,13 @@ impl tower::retry::Policy, Res, E> for FailoverPolicy where B: http_body::Body + Clone, { - type Future = future::Ready; - fn retry(&self, req: &Request, result: Result<&Res, &E>) -> Option { + fn retry(&self, _req: &Request, result: Result<&Res, &E>) -> Option { //TODO some error handling or logging match result { Ok(_) => None, - Err(_) => Some(future::ready(self.clone())) + Err(_) => Some(future::ready(self.clone())), } } @@ -43,21 +41,18 @@ where *clone.headers_mut() = req.headers().clone(); *clone.version_mut() = req.version(); - Some(clone) } } - - -impl Service> for Failover +impl Service> for Failover where // B is CloneBody B: http_body::Body + Clone, // loadbalancer service - N: Service> + Clone + 'static , + N: Service> + Clone + 'static, N::Error: Into, - N::Future: Send + N::Future: Send, { type Response = N::Response; @@ -68,9 +63,9 @@ where fn poll_ready(&mut self, cx: &mut std::task::Context<'_>) -> Poll> { self.inner.poll_ready(cx) } - + fn call(&mut self, req: Request) -> Self::Future { let retry = Retry::new(FailoverPolicy, self.inner.clone()); retry.oneshot(req) } -} \ No newline at end of file +} diff --git a/dubbo/src/cluster/mod.rs b/dubbo/src/cluster/mod.rs index 0b1654a0..1a20c160 100644 --- a/dubbo/src/cluster/mod.rs +++ b/dubbo/src/cluster/mod.rs @@ -15,11 +15,12 @@ * limitations under the License. */ - use http::Request; use tower_service::Service; -use crate::{codegen::RpcInvocation, svc::NewService, param::Param, invoker::clone_body::CloneBody}; +use crate::{ + codegen::RpcInvocation, invoker::clone_body::CloneBody, param::Param, svc::NewService, +}; use self::failover::Failover; @@ -30,12 +31,10 @@ pub struct NewCluster { } pub struct Cluster { - inner: S // failover service + inner: S, // failover service } - impl NewCluster { - pub fn layer() -> impl tower_layer::Layer { tower_layer::layer_fn(|inner: N| { NewCluster { @@ -43,45 +42,44 @@ impl NewCluster { } }) } +} -} - -impl NewService for NewCluster +impl NewService for NewCluster where - T: Param, + T: Param, // new loadbalancer service S: NewService, -{ - +{ type Service = Cluster>; - - fn new_service(&self, target: T) -> Self::Service { + fn new_service(&self, target: T) -> Self::Service { Cluster { - inner: Failover::new(self.inner.new_service(target)) + inner: Failover::new(self.inner.new_service(target)), } } } - -impl Service> for Cluster + +impl Service> for Cluster where S: Service>, { - type Response = S::Response; type Error = S::Error; type Future = S::Future; - fn poll_ready(&mut self, cx: &mut std::task::Context<'_>) -> std::task::Poll> { + fn poll_ready( + &mut self, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { self.inner.poll_ready(cx) } - + fn call(&mut self, req: Request) -> Self::Future { let (parts, body) = req.into_parts(); let clone_body = CloneBody::new(body); - let req = Request::from_parts(parts, clone_body); + let req = Request::from_parts(parts, clone_body); self.inner.call(req) } } diff --git a/dubbo/src/codegen.rs b/dubbo/src/codegen.rs index 38f8f1dc..452f560d 100644 --- a/dubbo/src/codegen.rs +++ b/dubbo/src/codegen.rs @@ -44,8 +44,7 @@ pub use super::{ pub use crate::{ filter::{service::FilterService, Filter}, triple::{ - client::builder::ClientBuilder, - server::builder::ServerBuilder, + client::builder::ClientBuilder, server::builder::ServerBuilder, transport::connection::Connection, }, }; diff --git a/dubbo/src/directory/mod.rs b/dubbo/src/directory/mod.rs index a4cf4669..84900aab 100644 --- a/dubbo/src/directory/mod.rs +++ b/dubbo/src/directory/mod.rs @@ -16,107 +16,109 @@ */ use std::{ - task::{Context, Poll}, collections::HashMap, sync::{Arc, Mutex}, pin::Pin, + collections::HashMap, + pin::Pin, + sync::{Arc, Mutex}, + task::{Context, Poll}, +}; + +use crate::{ + codegen::{RpcInvocation, TripleInvoker}, + invocation::Invocation, + invoker::{clone_invoker::CloneInvoker, NewInvoker}, + param::Param, + registry::n_registry::Registry, + svc::NewService, + StdError, }; - -use crate::{StdError, codegen::{RpcInvocation, TripleInvoker}, invocation::Invocation, registry::n_registry::Registry, invoker::{NewInvoker,clone_invoker::CloneInvoker}, svc::NewService, param::Param}; use dubbo_logger::tracing::debug; use futures_core::ready; use futures_util::future; use tokio::sync::mpsc::channel; use tokio_stream::wrappers::ReceiverStream; use tower::{ - discover::{Change, Discover}, buffer::Buffer, + buffer::Buffer, + discover::{Change, Discover}, }; use tower_service::Service; -type BufferedDirectory = Buffer, StdError>>>, ()>; +type BufferedDirectory = + Buffer, StdError>>>, ()>; pub struct NewCachedDirectory where N: Registry + Clone + Send + Sync + 'static, { - inner: CachedDirectory, RpcInvocation> + inner: CachedDirectory, RpcInvocation>, } - -pub struct CachedDirectory +pub struct CachedDirectory where - // NewDirectory - N: NewService + // NewDirectory + N: NewService, { inner: N, - cache: Arc>> + cache: Arc>>, } - pub struct NewDirectory { // registry inner: N, } - pub struct Directory { directory: HashMap>, discover: D, new_invoker: NewInvoker, } - - -impl NewCachedDirectory +impl NewCachedDirectory where N: Registry + Clone + Send + Sync + 'static, { - pub fn layer() -> impl tower_layer::Layer { tower_layer::layer_fn(|inner: N| { NewCachedDirectory { - // inner is registry - inner: CachedDirectory::new(NewDirectory::new(inner)), + // inner is registry + inner: CachedDirectory::new(NewDirectory::new(inner)), } }) } } - impl NewService for NewCachedDirectory where T: Param, // service registry N: Registry + Clone + Send + Sync + 'static, { - type Service = BufferedDirectory; + type Service = BufferedDirectory; fn new_service(&self, target: T) -> Self::Service { - self.inner.new_service(target.param()) } -} - - -impl CachedDirectory +} + +impl CachedDirectory where - N: NewService + N: NewService, { - pub fn new(inner: N) -> Self { CachedDirectory { inner, - cache: Default::default() + cache: Default::default(), } - } -} - - -impl NewService for CachedDirectory + } +} + +impl NewService for CachedDirectory where T: Param, // NewDirectory N: NewService, // Buffered directory - N::Service: Clone + N::Service: Clone, { type Service = N::Service; @@ -124,44 +126,35 @@ where let rpc_invocation = target.param(); let service_name = rpc_invocation.get_target_service_unique_name(); let mut cache = self.cache.lock().expect("cached directory lock failed."); - let value = cache.get(&service_name).map(|val|val.clone()); + let value = cache.get(&service_name).map(|val| val.clone()); match value { None => { let new_service = self.inner.new_service(target); cache.insert(service_name, new_service.clone()); new_service - }, - Some(value) => value - } + } + Some(value) => value, + } } } - impl NewDirectory { - const MAX_DIRECTORY_BUFFER_SIZE: usize = 16; pub fn new(inner: N) -> Self { - NewDirectory { - inner - } + NewDirectory { inner } } -} - - +} -impl NewService for NewDirectory +impl NewService for NewDirectory where T: Param, // service registry - N: Registry + Clone + Send + Sync + 'static, + N: Registry + Clone + Send + Sync + 'static, { - type Service = BufferedDirectory; + type Service = BufferedDirectory; - - fn new_service(&self, target: T) -> Self::Service { - let service_name = target.param().get_target_service_unique_name(); let registry = self.inner.clone(); @@ -172,39 +165,35 @@ where let receiver = registry.subscribe(service_name).await; debug!("discover start!"); match receiver { - Err(e) => { + Err(_e) => { // error!("discover stream error: {}", e); debug!("discover stream error"); - }, - Ok(mut receiver) => { - loop { - let change = receiver.recv().await; - debug!("receive change: {:?}", change); - match change { - None => { - debug!("discover stream closed."); - break; - }, - Some(change) => { - let _ = tx.send(change).await; - } + } + Ok(mut receiver) => loop { + let change = receiver.recv().await; + debug!("receive change: {:?}", change); + match change { + None => { + debug!("discover stream closed."); + break; + } + Some(change) => { + let _ = tx.send(change).await; } } - } + }, } + }); - }); - - Buffer::new(Directory::new(ReceiverStream::new(rx)), Self::MAX_DIRECTORY_BUFFER_SIZE) - } - -} - - -impl Directory { + Buffer::new( + Directory::new(ReceiverStream::new(rx)), + Self::MAX_DIRECTORY_BUFFER_SIZE, + ) + } +} +impl Directory { pub fn new(discover: D) -> Self { - Directory { directory: Default::default(), discover, @@ -213,13 +202,12 @@ impl Directory { } } - -impl Service<()> for Directory +impl Service<()> for Directory where // Discover - D: Discover + Unpin + Send, - D::Error: Into -{ + D: Discover + Unpin + Send, + D::Error: Into, +{ type Response = Vec>; type Error = StdError; @@ -227,21 +215,22 @@ where type Future = future::Ready>; fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { - loop { let pin_discover = Pin::new(&mut self.discover); - let change = ready!(pin_discover.poll_discover(cx)).transpose().map_err(|e| e.into())?; + let change = ready!(pin_discover.poll_discover(cx)) + .transpose() + .map_err(|e| e.into())?; match change { Some(Change::Remove(key)) => { debug!("remove key: {}", key); self.directory.remove(&key); - }, + } Some(Change::Insert(key, _)) => { debug!("insert key: {}", key); let invoker = self.new_invoker.new_service(key.clone()); self.directory.insert(key, invoker); - }, - None => { + } + None => { debug!("stream closed"); return Poll::Ready(Ok(())); } @@ -250,7 +239,11 @@ where } fn call(&mut self, _: ()) -> Self::Future { - let vec = self.directory.values().map(|val|val.clone()).collect::>>(); + let vec = self + .directory + .values() + .map(|val| val.clone()) + .collect::>>(); future::ok(vec) } -} \ No newline at end of file +} diff --git a/dubbo/src/invoker/clone_body.rs b/dubbo/src/invoker/clone_body.rs index 5ce2e1f9..4de8f899 100644 --- a/dubbo/src/invoker/clone_body.rs +++ b/dubbo/src/invoker/clone_body.rs @@ -7,20 +7,20 @@ use std::{ use bytes::{Buf, BufMut, Bytes, BytesMut}; use futures_core::ready; - + use http::HeaderMap; use http_body::Body; use pin_project::pin_project; use thiserror::Error; use crate::StdError; - + #[derive(Error, Debug)] -#[error("buffered body reach max capacity.")] +#[error("buffered body reach max capacity.")] pub struct ReachMaxCapacityError; pub struct BufferedBody { - shared: Arc>>, + shared: Arc>>, owned: Option, replay_body: bool, replay_trailers: bool, @@ -34,10 +34,7 @@ pub struct OwnedBufferedBody { buf: InnerBuffer, } - - impl BufferedBody { - pub fn new(body: hyper::Body, buf_size: usize) -> Self { let size_hint = body.size_hint(); let is_empty = body.is_end_stream(); @@ -57,11 +54,9 @@ impl BufferedBody { size_hint, } } - } impl Clone for BufferedBody { - fn clone(&self) -> Self { Self { shared: self.shared.clone(), @@ -86,7 +81,6 @@ impl Drop for BufferedBody { } impl Body for BufferedBody { - type Data = BytesData; type Error = StdError; @@ -106,14 +100,10 @@ impl Body for BufferedBody { data.take().expect("cannot get shared buffered body.") }); - - if mut_self.replay_body { mut_self.replay_body = false; if owned_body.buf.has_remaining() { - return Poll::Ready(Some(Ok(BytesData::BufferedBytes( - owned_body.buf.clone(), - )))); + return Poll::Ready(Some(Ok(BytesData::BufferedBytes(owned_body.buf.clone())))); } if owned_body.buf.is_capped() { @@ -150,10 +140,8 @@ impl Body for BufferedBody { } else { owned_body.buf.push_bytes(data.copy_to_bytes(len)) }; - - Poll::Ready(Some(Ok(BytesData::OriginBytes(data)))) - + Poll::Ready(Some(Ok(BytesData::OriginBytes(data)))) } fn poll_trailers( @@ -170,7 +158,7 @@ impl Body for BufferedBody { data.take().expect("cannot get shared buffered body.") }); - + if mut_self.replay_trailers { mut_self.replay_trailers = false; if let Some(ref trailers) = owned_body.trailers { @@ -184,7 +172,7 @@ impl Body for BufferedBody { owned_body.trailers = trailers.clone(); trailers }); - return Poll::Ready(trailers.map_err(|e|e.into())); + return Poll::Ready(trailers.map_err(|e| e.into())); } Poll::Ready(Ok(None)) @@ -195,9 +183,11 @@ impl Body for BufferedBody { return true; } - let is_end = self.owned.as_ref() - .map(|owned|owned.body.is_end_stream()) - .unwrap_or(false); + let is_end = self + .owned + .as_ref() + .map(|owned| owned.body.is_end_stream()) + .unwrap_or(false); !self.replay_body && !self.replay_trailers && is_end } @@ -205,12 +195,8 @@ impl Body for BufferedBody { fn size_hint(&self) -> http_body::SizeHint { self.size_hint.clone() } - - } - - #[derive(Clone)] pub struct InnerBuffer { bufs: VecDeque, @@ -328,13 +314,12 @@ pub struct CloneBody(#[pin] BufferedBody); impl CloneBody { pub fn new(inner_body: hyper::Body) -> Self { - let inner_body = BufferedBody::new(inner_body, 1024 * 64); + let inner_body = BufferedBody::new(inner_body, 1024 * 64); CloneBody(inner_body) } } -impl Body for CloneBody{ - +impl Body for CloneBody { type Data = BytesData; type Error = StdError; @@ -350,7 +335,7 @@ impl Body for CloneBody{ self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll, Self::Error>> { - self.project().0.poll_trailers(cx) + self.project().0.poll_trailers(cx) } fn size_hint(&self) -> http_body::SizeHint { @@ -358,9 +343,8 @@ impl Body for CloneBody{ } } - impl Clone for CloneBody { fn clone(&self) -> Self { Self(self.0.clone()) } -} \ No newline at end of file +} diff --git a/dubbo/src/invoker/clone_invoker.rs b/dubbo/src/invoker/clone_invoker.rs index fe621b87..c1fa00d8 100644 --- a/dubbo/src/invoker/clone_invoker.rs +++ b/dubbo/src/invoker/clone_invoker.rs @@ -1,76 +1,81 @@ -use std::{task::Poll, pin::Pin, mem}; +use std::{mem, pin::Pin, task::Poll}; use dubbo_logger::tracing::debug; -use futures_core::{Future, TryFuture, ready, future::BoxFuture}; +use futures_core::{future::BoxFuture, ready, Future, TryFuture}; use futures_util::FutureExt; use pin_project::pin_project; use thiserror::Error; -use tokio::{task::JoinHandle, sync::{watch::{Sender, Receiver}, self}}; +use tokio::{ + sync::{ + self, + watch::{Receiver, Sender}, + }, + task::JoinHandle, +}; use tokio_util::sync::ReusableBoxFuture; -use tower::{ServiceExt, buffer::Buffer}; +use tower::{buffer::Buffer, ServiceExt}; use tower_service::Service; use crate::StdError; use super::clone_body::CloneBody; - + enum Inner { Invalid, Ready(S), Pending(JoinHandle>), -} +} #[derive(Debug, Error)] #[error("the inner service has not got ready yet!")] struct InnerServiceNotReadyErr; - - #[pin_project(project = InnerServiceCallingResponseProj)] enum InnerServiceCallingResponse { Call(#[pin] Fut), - Fail + Fail, } -impl Future for InnerServiceCallingResponse +impl Future for InnerServiceCallingResponse where Fut: TryFuture, - Fut::Error: Into + Fut::Error: Into, { type Output = Result; - + fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll { - match self.project() { - InnerServiceCallingResponseProj::Call(call) => call.try_poll(cx).map_err(Into::into), - InnerServiceCallingResponseProj::Fail => Poll::Ready(Err(InnerServiceNotReadyErr.into())) - } + match self.project() { + InnerServiceCallingResponseProj::Call(call) => call.try_poll(cx).map_err(Into::into), + InnerServiceCallingResponseProj::Fail => { + Poll::Ready(Err(InnerServiceNotReadyErr.into())) + } + } } } - + #[derive(Clone)] enum ObserveState { Ready, Pending, } - struct ReadyService { inner: Inner, - tx: Sender + tx: Sender, } - impl ReadyService { - fn new(inner: S) -> (Self, Receiver) { let (tx, rx) = sync::watch::channel(ObserveState::Ready); - let ready_service = Self { inner: Inner::Ready(inner), tx}; + let ready_service = Self { + inner: Inner::Ready(inner), + tx, + }; (ready_service, rx) } - } -impl Service for ReadyService +impl Service for ReadyService where S: Service + Send + 'static, >::Error: Into, @@ -85,16 +90,14 @@ where loop { match mem::replace(&mut self.inner, Inner::Invalid) { Inner::Ready(mut svc) => { - let poll_ready = svc.poll_ready(cx); + let poll_ready = svc.poll_ready(cx); match poll_ready { Poll::Pending => { self.inner = Inner::Pending(tokio::spawn(async move { let poll_ready = svc.ready().await; match poll_ready { Ok(_) => Ok(svc), - Err(err) => { - Err((svc, err.into())) - } + Err(err) => Err((svc, err.into())), } })); @@ -108,17 +111,17 @@ where return Poll::Ready(ret.map_err(Into::into)); } } - }, + } Inner::Pending(mut join_handle) => { if let Poll::Ready(res) = join_handle.poll_unpin(cx) { let (svc, res) = match res { Err(join_err) => panic!("ReadyService panicked: {join_err}"), Ok(Err((svc, err))) => (svc, Poll::Ready(Err(err))), - Ok(Ok(svc)) => (svc, Poll::Ready(Ok(()))) + Ok(Ok(svc)) => (svc, Poll::Ready(Ok(()))), }; self.inner = Inner::Ready(svc); - + let _ = self.tx.send(ObserveState::Ready); return res; } else { @@ -127,31 +130,29 @@ where let _ = self.tx.send(ObserveState::Pending); return Poll::Pending; } - - }, - Inner::Invalid => panic!("ReadyService panicked: inner state is invalid") + } + Inner::Invalid => panic!("ReadyService panicked: inner state is invalid"), } } } fn call(&mut self, req: Req) -> Self::Future { - match self.inner { - Inner::Ready(ref mut svc) => InnerServiceCallingResponse::Call(svc.call(req)), - _ => InnerServiceCallingResponse::Fail - } + match self.inner { + Inner::Ready(ref mut svc) => InnerServiceCallingResponse::Call(svc.call(req)), + _ => InnerServiceCallingResponse::Fail, + } } } - impl Drop for ReadyService { fn drop(&mut self) { - if let Inner::Pending(ref handler) = self.inner { + if let Inner::Pending(ref handler) = self.inner { handler.abort(); - } + } } } -pub struct CloneInvoker +pub struct CloneInvoker where Inv: Service> + Send + 'static, Inv::Error: Into + Send + Sync + 'static, @@ -163,40 +164,44 @@ where polling: bool, } -impl CloneInvoker +impl CloneInvoker where Inv: Service> + Send + 'static, Inv::Error: Into + Send + Sync + 'static, Inv::Future: Send, { - const MAX_INVOKER_BUFFER_SIZE: usize = 16; - + pub fn new(invoker: Inv) -> Self { - let (ready_service, rx) = ReadyService::new(invoker); - let buffer: Buffer, http::Request> = Buffer::new(ready_service, Self::MAX_INVOKER_BUFFER_SIZE); + let buffer: Buffer, http::Request> = + Buffer::new(ready_service, Self::MAX_INVOKER_BUFFER_SIZE); - Self { inner: buffer, rx, polling: false, poll: ReusableBoxFuture::new(futures::future::pending()) } + Self { + inner: buffer, + rx, + polling: false, + poll: ReusableBoxFuture::new(futures::future::pending()), + } } } -impl Service> for CloneInvoker +impl Service> for CloneInvoker where Inv: Service> + Send + 'static, Inv::Error: Into + Send + Sync + 'static, Inv::Future: Send, { type Response = Inv::Response; - + type Error = StdError; - + type Future = BoxFuture<'static, Result>; fn poll_ready(&mut self, cx: &mut std::task::Context<'_>) -> Poll> { loop { - if !self.polling { + if !self.polling { match self.rx.borrow().clone() { ObserveState::Ready => return self.inner.poll_ready(cx), ObserveState::Pending => { @@ -206,7 +211,7 @@ where loop { let current_state = rx.borrow_and_update().clone(); if matches!(current_state, ObserveState::Ready) { - return current_state; + return current_state; } if let Err(_) = rx.changed().await { debug!("the readyService has already shutdown!"); @@ -216,7 +221,6 @@ where }); } } - } let state = ready!(self.poll.poll_unpin(cx)); @@ -235,14 +239,18 @@ where } } - -impl Clone for CloneInvoker +impl Clone for CloneInvoker where Inv: Service> + Send + 'static, Inv::Error: Into + Send + Sync + 'static, Inv::Future: Send, { fn clone(&self) -> Self { - Self { inner: self.inner.clone(), rx: self.rx.clone(), polling: false, poll: ReusableBoxFuture::new(futures::future::pending())} + Self { + inner: self.inner.clone(), + rx: self.rx.clone(), + polling: false, + poll: ReusableBoxFuture::new(futures::future::pending()), + } } -} \ No newline at end of file +} diff --git a/dubbo/src/invoker/mod.rs b/dubbo/src/invoker/mod.rs index a8179eee..92b8b462 100644 --- a/dubbo/src/invoker/mod.rs +++ b/dubbo/src/invoker/mod.rs @@ -1,14 +1,12 @@ use dubbo_base::Url; -use crate::{codegen::TripleInvoker, svc::NewService, invoker::clone_invoker::CloneInvoker}; +use crate::{codegen::TripleInvoker, invoker::clone_invoker::CloneInvoker, svc::NewService}; pub mod clone_body; pub mod clone_invoker; - pub struct NewInvoker; - impl NewService for NewInvoker { type Service = CloneInvoker; @@ -18,4 +16,4 @@ impl NewService for NewInvoker { let url = Url::from_url(&url).unwrap(); CloneInvoker::new(TripleInvoker::new(url)) } -} \ No newline at end of file +} diff --git a/dubbo/src/lib.rs b/dubbo/src/lib.rs index 252e01c7..d397b42b 100644 --- a/dubbo/src/lib.rs +++ b/dubbo/src/lib.rs @@ -18,20 +18,20 @@ pub mod cluster; pub mod codegen; pub mod context; +pub mod directory; pub mod filter; mod framework; pub mod invocation; +pub mod invoker; +pub mod loadbalancer; +pub mod param; pub mod protocol; pub mod registry; +pub mod route; pub mod status; +pub mod svc; pub mod triple; pub mod utils; -pub mod directory; -pub mod route; -pub mod loadbalancer; -pub mod invoker; -pub mod param; -pub mod svc; use http_body::Body; use std::{future::Future, pin::Pin}; diff --git a/dubbo/src/loadbalancer/mod.rs b/dubbo/src/loadbalancer/mod.rs index 334350e3..4e26781d 100644 --- a/dubbo/src/loadbalancer/mod.rs +++ b/dubbo/src/loadbalancer/mod.rs @@ -1,16 +1,20 @@ use futures_core::future::BoxFuture; -use tower::ServiceExt; -use tower::discover::ServiceList; +use tower::{discover::ServiceList, ServiceExt}; use tower_service::Service; -use crate::invoker::clone_body::CloneBody; -use crate::{codegen::RpcInvocation, StdError, svc::NewService, param::Param, invoker::clone_invoker::CloneInvoker}; +use crate::{ + codegen::RpcInvocation, + invoker::{clone_body::CloneBody, clone_invoker::CloneInvoker}, + param::Param, + svc::NewService, + StdError, +}; use crate::protocol::triple::triple_invoker::TripleInvoker; - + pub struct NewLoadBalancer { inner: N, -} +} #[derive(Clone)] pub struct LoadBalancer { @@ -18,80 +22,74 @@ pub struct LoadBalancer { } impl NewLoadBalancer { - - pub fn layer() -> impl tower_layer::Layer{ - + pub fn layer() -> impl tower_layer::Layer { tower_layer::layer_fn(|inner| { - NewLoadBalancer { - inner // NewRoutes + inner, // NewRoutes } }) } } -impl NewService for NewLoadBalancer +impl NewService for NewLoadBalancer where T: Param + Clone, // NewRoutes - N: NewService, + N: NewService, { - type Service = LoadBalancer; fn new_service(&self, target: T) -> Self::Service { // Routes service let svc = self.inner.new_service(target); - LoadBalancer { - inner: svc, - } - + LoadBalancer { inner: svc } } } -impl Service> for LoadBalancer +impl Service> for LoadBalancer where // Routes service N: Service<(), Response = Vec>> + Clone, N::Error: Into + Send, - N::Future: Send + 'static, + N::Future: Send + 'static, { - type Response = as Service>>::Response; type Error = StdError; type Future = BoxFuture<'static, Result>; - fn poll_ready(&mut self, cx: &mut std::task::Context<'_>) -> std::task::Poll> { + fn poll_ready( + &mut self, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { self.inner.poll_ready(cx).map_err(Into::into) - } fn call(&mut self, req: http::Request) -> Self::Future { - let routes = self.inner.call(()); + let routes = self.inner.call(()); let fut = async move { let routes = routes.await; let routes: Vec> = match routes { Err(e) => return Err(Into::::into(e)), - Ok(routes) => routes + Ok(routes) => routes, }; - - let service_list: Vec<_> = routes.into_iter().map(|invoker| { - tower::load::Constant::new(invoker, 1) - }).collect(); + + let service_list: Vec<_> = routes + .into_iter() + .map(|invoker| tower::load::Constant::new(invoker, 1)) + .collect(); let service_list = ServiceList::new(service_list); - + let p2c = tower::balance::p2c::Balance::new(service_list); - p2c.oneshot(req).await }; - + Box::pin(fut) } } diff --git a/dubbo/src/param.rs b/dubbo/src/param.rs index b57f98eb..bef50419 100644 --- a/dubbo/src/param.rs +++ b/dubbo/src/param.rs @@ -1,12 +1,9 @@ pub trait Param { - fn param(&self) -> T; } - impl Param for T { - fn param(&self) -> T::Owned { - self.to_owned() + self.to_owned() } } diff --git a/dubbo/src/protocol/mod.rs b/dubbo/src/protocol/mod.rs index 9d35a525..7dbf1f3f 100644 --- a/dubbo/src/protocol/mod.rs +++ b/dubbo/src/protocol/mod.rs @@ -15,9 +15,7 @@ * limitations under the License. */ -use std::{ - task::{Context, Poll}, -}; +use std::task::{Context, Poll}; use async_trait::async_trait; use aws_smithy_http::body::SdkBody; diff --git a/dubbo/src/protocol/triple/triple_invoker.rs b/dubbo/src/protocol/triple/triple_invoker.rs index 71eae903..c8451e3c 100644 --- a/dubbo/src/protocol/triple/triple_invoker.rs +++ b/dubbo/src/protocol/triple/triple_invoker.rs @@ -16,14 +16,17 @@ */ use dubbo_base::Url; -use http::{Uri, HeaderValue}; +use http::{HeaderValue, Uri}; use std::{ fmt::{Debug, Formatter}, str::FromStr, }; use tower_service::Service; -use crate::{triple::transport::{connection::Connection, self}, invoker::clone_body::CloneBody}; +use crate::{ + invoker::clone_body::CloneBody, + triple::transport::{self, connection::Connection}, +}; pub struct TripleInvoker { url: Url, @@ -47,18 +50,19 @@ impl Debug for TripleInvoker { } impl TripleInvoker { - pub fn map_request( - &self, - req: http::Request, - ) -> http::Request { - + pub fn map_request(&self, req: http::Request) -> http::Request { let (parts, body) = req.into_parts(); let path_and_query = parts.headers.get("path").unwrap().to_str().unwrap(); - let authority = self.url.clone().get_ip_port(); - - let uri = Uri::builder().scheme("http").authority(authority).path_and_query(path_and_query).build().unwrap(); + let authority = self.url.clone().get_ip_port(); + + let uri = Uri::builder() + .scheme("http") + .authority(authority) + .path_and_query(path_and_query) + .build() + .unwrap(); let mut req = hyper::Request::builder() .version(http::Version::HTTP_2) @@ -99,12 +103,12 @@ impl TripleInvoker { HeaderValue::from_static("dubbo-rust/0.1.0"), ); // if let Some(_encoding) = self.send_compression_encoding { - + // } req.headers_mut() - .insert("grpc-encoding", http::HeaderValue::from_static("gzip")); - + .insert("grpc-encoding", http::HeaderValue::from_static("gzip")); + req.headers_mut().insert( "grpc-accept-encoding", http::HeaderValue::from_static("gzip"), @@ -137,7 +141,10 @@ impl Service> for TripleInvoker { &mut self, cx: &mut std::task::Context<'_>, ) -> std::task::Poll> { - >>::poll_ready(&mut self.conn, cx) + >>::poll_ready( + &mut self.conn, + cx, + ) } fn call(&mut self, req: http::Request) -> Self::Future { @@ -146,4 +153,3 @@ impl Service> for TripleInvoker { self.conn.call(req) } } - diff --git a/dubbo/src/registry/mod.rs b/dubbo/src/registry/mod.rs index ab2297f3..d8ff6ef3 100644 --- a/dubbo/src/registry/mod.rs +++ b/dubbo/src/registry/mod.rs @@ -18,9 +18,9 @@ #![allow(unused_variables, dead_code, missing_docs)] pub mod integration; pub mod memory_registry; +pub mod n_registry; pub mod protocol; pub mod types; -pub mod n_registry; use std::{ fmt::{Debug, Formatter}, @@ -29,7 +29,6 @@ use std::{ use dubbo_base::Url; - pub type RegistryNotifyListener = Arc; pub trait Registry { fn register(&mut self, url: Url) -> Result<(), crate::StdError>; diff --git a/dubbo/src/registry/n_registry.rs b/dubbo/src/registry/n_registry.rs index 9b6dca58..928c9b3c 100644 --- a/dubbo/src/registry/n_registry.rs +++ b/dubbo/src/registry/n_registry.rs @@ -2,19 +2,17 @@ use std::sync::Arc; use async_trait::async_trait; use dubbo_base::Url; -use tokio::sync::mpsc::{Receiver, channel}; +use tokio::sync::mpsc::{channel, Receiver}; use tower::discover::Change; - use crate::StdError; type DiscoverStream = Receiver, StdError>>; #[async_trait] pub trait Registry { - async fn register(&self, url: Url) -> Result<(), StdError>; - + async fn unregister(&self, url: Url) -> Result<(), StdError>; // todo service_name change to url @@ -25,31 +23,29 @@ pub trait Registry { #[derive(Clone)] pub struct ArcRegistry { - inner: Arc + inner: Arc, } - pub enum RegistryComponent { NacosRegistry, ZookeeperRegistry, StaticRegistry(StaticRegistry), } - pub struct StaticRegistry { - urls: Vec + urls: Vec, } impl ArcRegistry { - pub fn new(registry: impl Registry + Send + Sync + 'static) -> Self { - Self { inner: Arc::new(registry) } + Self { + inner: Arc::new(registry), + } } } #[async_trait] impl Registry for ArcRegistry { - async fn register(&self, url: Url) -> Result<(), StdError> { self.inner.register(url).await } @@ -67,9 +63,6 @@ impl Registry for ArcRegistry { } } - - - #[async_trait] impl Registry for RegistryComponent { async fn register(&self, url: Url) -> Result<(), StdError> { @@ -93,17 +86,12 @@ impl Registry for RegistryComponent { } } - impl StaticRegistry { - pub fn new(urls: Vec) -> Self { - Self { - urls - } + Self { urls } } } - #[async_trait] impl Registry for StaticRegistry { async fn register(&self, url: Url) -> Result<(), StdError> { @@ -119,7 +107,7 @@ impl Registry for StaticRegistry { for url in self.urls.iter() { let change = Ok(Change::Insert(url.to_url(), ())); tx.send(change).await?; - } + } Ok(rx) } @@ -127,4 +115,4 @@ impl Registry for StaticRegistry { async fn unsubscribe(&self, url: Url) -> Result<(), StdError> { todo!() } -} \ No newline at end of file +} diff --git a/dubbo/src/route/mod.rs b/dubbo/src/route/mod.rs index cff3ccee..c2448642 100644 --- a/dubbo/src/route/mod.rs +++ b/dubbo/src/route/mod.rs @@ -1,42 +1,51 @@ use std::pin::Pin; use dubbo_logger::tracing::debug; -use futures_core::{Future, ready}; +use futures_core::{ready, Future}; use futures_util::{future::Ready, FutureExt, TryFutureExt}; -use tower::{util::FutureService, buffer::Buffer}; +use tower::{buffer::Buffer, util::FutureService}; use tower_service::Service; - -use crate::{StdError, codegen::{RpcInvocation, TripleInvoker}, svc::NewService, param::Param, invoker::clone_invoker::CloneInvoker}; + +use crate::{ + codegen::{RpcInvocation, TripleInvoker}, + invoker::clone_invoker::CloneInvoker, + param::Param, + svc::NewService, + StdError, +}; pub struct NewRoutes { inner: N, -} - +} pub struct NewRoutesFuture { inner: RoutesFutureInnerState, target: T, } - pub enum RoutesFutureInnerState { Service(S), - Future(Pin>, StdError>> + Send + 'static>>), + Future( + Pin< + Box< + dyn Future>, StdError>> + + Send + + 'static, + >, + >, + ), Ready(Vec>), } - #[derive(Clone)] pub struct Routes { target: T, - invokers: Vec> + invokers: Vec>, } impl NewRoutes { pub fn new(inner: N) -> Self { - Self { - inner, - } + Self { inner } } } @@ -44,40 +53,39 @@ impl NewRoutes { const MAX_ROUTE_BUFFER_SIZE: usize = 16; pub fn layer() -> impl tower_layer::Layer { - tower_layer::layer_fn(|inner: N| { - NewRoutes::new(inner) - }) + tower_layer::layer_fn(|inner: N| NewRoutes::new(inner)) } } - -impl NewService for NewRoutes +impl NewService for NewRoutes where - T: Param + Clone + Send + Unpin + 'static, + T: Param + Clone + Send + Unpin + 'static, // NewDirectory N: NewService, - // Directory + // Directory N::Service: Service<(), Response = Vec>> + Unpin + Send + 'static, - >::Error: Into, + >::Error: Into, >::Future: Send + 'static, -{ - - type Service = Buffer>::Service, T>, Routes>, ()>; +{ + type Service = + Buffer>::Service, T>, Routes>, ()>; fn new_service(&self, target: T) -> Self::Service { let inner = self.inner.new_service(target.clone()); - Buffer::new(FutureService::new(NewRoutesFuture { - inner: RoutesFutureInnerState::Service(inner), - target, - }), Self::MAX_ROUTE_BUFFER_SIZE) + Buffer::new( + FutureService::new(NewRoutesFuture { + inner: RoutesFutureInnerState::Service(inner), + target, + }), + Self::MAX_ROUTE_BUFFER_SIZE, + ) } } - -impl Future for NewRoutesFuture +impl Future for NewRoutesFuture where - T: Param + Clone + Unpin, + T: Param + Clone + Unpin, // Directory N: Service<(), Response = Vec>> + Unpin, N::Error: Into, @@ -85,8 +93,10 @@ where { type Output = Result, StdError>; - fn poll(self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> std::task::Poll { - + fn poll( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll { let this = self.get_mut(); loop { @@ -94,14 +104,14 @@ where RoutesFutureInnerState::Service(ref mut service) => { debug!("RoutesFutureInnerState::Service"); let _ = ready!(service.poll_ready(cx)).map_err(Into::into)?; - let fut = service.call(()).map_err(|e|e.into()).boxed(); + let fut = service.call(()).map_err(|e| e.into()).boxed(); this.inner = RoutesFutureInnerState::Future(fut); - }, + } RoutesFutureInnerState::Future(ref mut futures) => { debug!("RoutesFutureInnerState::Future"); let invokers = ready!(futures.as_mut().poll(cx))?; this.inner = RoutesFutureInnerState::Ready(invokers); - }, + } RoutesFutureInnerState::Ready(ref invokers) => { debug!("RoutesFutureInnerState::Ready"); let target = this.target.clone(); @@ -109,32 +119,32 @@ where invokers: invokers.clone(), target, })); - }, + } } } - } } - - -impl Service<()> for Routes +impl Service<()> for Routes where - T: Param + Clone, -{ + T: Param + Clone, +{ type Response = Vec>; - + type Error = StdError; - - type Future = Ready>; - fn poll_ready(&mut self, _: &mut std::task::Context<'_>) -> std::task::Poll> { + type Future = Ready>; + + fn poll_ready( + &mut self, + _: &mut std::task::Context<'_>, + ) -> std::task::Poll> { std::task::Poll::Ready(Ok(())) } fn call(&mut self, _: ()) -> Self::Future { // some router operator // if new_invokers changed, send new invokers to routes_rx after router operator - futures_util::future::ok(self.invokers.clone()) + futures_util::future::ok(self.invokers.clone()) } -} \ No newline at end of file +} diff --git a/dubbo/src/svc.rs b/dubbo/src/svc.rs index 56dc3304..df4c071a 100644 --- a/dubbo/src/svc.rs +++ b/dubbo/src/svc.rs @@ -1,23 +1,19 @@ -use std::{sync::Arc, pin::Pin, marker::PhantomData}; +use std::{marker::PhantomData, sync::Arc}; + + + -use futures_core::Future; -use tower::ServiceExt; -use tower_service::Service; pub trait NewService { - type Service; fn new_service(&self, target: T) -> Self::Service; - } - pub struct ArcNewService { inner: Arc + Send + Sync>, } - impl ArcNewService { pub fn layer() -> impl tower_layer::Layer + Clone + Copy where @@ -57,23 +53,19 @@ impl NewService for ArcNewService { // inner: Box> + Send>>> + Send>, pub struct BoxedService { inner: N, - _mark: PhantomData + _mark: PhantomData, } impl BoxedService { - - pub fn layer() -> impl tower_layer::Layer{ - tower_layer::layer_fn(|inner: N| { - Self { - inner, - _mark: PhantomData - } + pub fn layer() -> impl tower_layer::Layer { + tower_layer::layer_fn(|inner: N| Self { + inner, + _mark: PhantomData, }) } } - -// impl NewService for BoxedService +// impl NewService for BoxedService // where // N: NewService, // N::Service: Service + Send, diff --git a/dubbo/src/triple/client/builder.rs b/dubbo/src/triple/client/builder.rs index ed080217..c4e6e62c 100644 --- a/dubbo/src/triple/client/builder.rs +++ b/dubbo/src/triple/client/builder.rs @@ -15,11 +15,15 @@ * limitations under the License. */ - use std::sync::Arc; use crate::{ - utils::boxed_clone::BoxCloneService, registry::n_registry::{RegistryComponent, StaticRegistry, ArcRegistry}, route::NewRoutes, loadbalancer::NewLoadBalancer, cluster::{NewCluster, Cluster}, directory::NewCachedDirectory, svc::{ArcNewService, NewService, BoxedService}, StdError, codegen::RpcInvocation, BoxBody, + cluster::NewCluster, + directory::NewCachedDirectory, + loadbalancer::NewLoadBalancer, + registry::n_registry::{ArcRegistry, RegistryComponent, StaticRegistry}, + route::NewRoutes, + utils::boxed_clone::BoxCloneService, }; use aws_smithy_http::body::SdkBody; @@ -29,7 +33,6 @@ use tower::ServiceBuilder; pub type ClientBoxService = BoxCloneService, http::Response, crate::Error>; - pub type ServiceMK = Arc>>>>; #[derive(Default)] @@ -56,7 +59,9 @@ impl ClientBuilder { Self { timeout: None, connector: "", - registry: Some(ArcRegistry::new(RegistryComponent::StaticRegistry(StaticRegistry::new(vec![Url::from_url(host).unwrap()])))), + registry: Some(ArcRegistry::new(RegistryComponent::StaticRegistry( + StaticRegistry::new(vec![Url::from_url(host).unwrap()]), + ))), direct: true, host: host.to_string(), } @@ -70,24 +75,23 @@ impl ClientBuilder { } pub fn with_registry(self, registry: RegistryComponent) -> Self { - Self { - registry: Some(ArcRegistry::new(registry)), + Self { + registry: Some(ArcRegistry::new(registry)), ..self } } pub fn with_host(self, host: &'static str) -> Self { Self { - registry: Some(ArcRegistry::new(RegistryComponent::StaticRegistry(StaticRegistry::new(vec![Url::from_url(host).unwrap()])))), + registry: Some(ArcRegistry::new(RegistryComponent::StaticRegistry( + StaticRegistry::new(vec![Url::from_url(host).unwrap()]), + ))), ..self } } pub fn with_connector(self, connector: &'static str) -> Self { - Self { - connector, - ..self - } + Self { connector, ..self } } pub fn with_direct(self, direct: bool) -> Self { @@ -95,16 +99,14 @@ impl ClientBuilder { } pub fn build(mut self) -> ServiceMK { - - let registry = self.registry.take().expect("registry must not be empty"); let mk_service = ServiceBuilder::new() - .layer(NewCluster::layer()) - .layer(NewLoadBalancer::layer()) - .layer(NewRoutes::layer()) - .layer(NewCachedDirectory::layer()) - .service(registry); + .layer(NewCluster::layer()) + .layer(NewLoadBalancer::layer()) + .layer(NewRoutes::layer()) + .layer(NewCachedDirectory::layer()) + .service(registry); Arc::new(mk_service) } diff --git a/dubbo/src/triple/client/triple.rs b/dubbo/src/triple/client/triple.rs index 9bcf144d..377dc435 100644 --- a/dubbo/src/triple/client/triple.rs +++ b/dubbo/src/triple/client/triple.rs @@ -15,7 +15,6 @@ * limitations under the License. */ - use futures_util::{future, stream, StreamExt, TryStreamExt}; use aws_smithy_http::body::SdkBody; @@ -25,9 +24,9 @@ use tower_service::Service; use super::builder::{ClientBuilder, ServiceMK}; use crate::codegen::RpcInvocation; -use crate::svc::NewService; use crate::{ invocation::{IntoStreamingRequest, Metadata, Request, Response}, + svc::NewService, triple::{codec::Codec, compression::CompressionEncoding, decode::Decoding, encode::encode}, }; @@ -148,15 +147,12 @@ impl TripleClient { .into_stream(); let body = hyper::Body::wrap_stream(body_stream); - let mut invoker = self.mk.new_service(invocation); - let request = http::Request::builder() .header("path", path.to_string()) - .body(body).unwrap(); - - + .body(body) + .unwrap(); let response = invoker .call(request) @@ -211,14 +207,13 @@ impl TripleClient { ) .into_stream(); let body = hyper::Body::wrap_stream(en); - - let mut invoker = self.mk.new_service(invocation); + let mut invoker = self.mk.new_service(invocation); let request = http::Request::builder() .header("path", path.to_string()) - .body(body).unwrap(); - + .body(body) + .unwrap(); let response = invoker .call(request) @@ -259,12 +254,10 @@ impl TripleClient { let body = hyper::Body::wrap_stream(en); let mut invoker = self.mk.new_service(invocation); - let request = http::Request::builder() .header("path", path.to_string()) - .body(body).unwrap(); - - + .body(body) + .unwrap(); // let mut conn = Connection::new().with_host(http_uri); let response = invoker @@ -322,11 +315,10 @@ impl TripleClient { let body = hyper::Body::wrap_stream(en); let mut invoker = self.mk.new_service(invocation); - let request = http::Request::builder() .header("path", path.to_string()) - .body(body).unwrap(); - + .body(body) + .unwrap(); let response = invoker .call(request) diff --git a/dubbo/src/triple/transport/connection.rs b/dubbo/src/triple/transport/connection.rs index 3bbaa179..cb0b9d71 100644 --- a/dubbo/src/triple/transport/connection.rs +++ b/dubbo/src/triple/transport/connection.rs @@ -18,9 +18,15 @@ use hyper::client::{conn::Builder, service::Connect}; use tower_service::Service; -use crate::{boxed, triple::transport::connector::get_connector, StdError, invoker::clone_body::CloneBody}; +use crate::{ + boxed, invoker::clone_body::CloneBody, triple::transport::connector::get_connector, StdError, +}; -type HyperConnect = Connect, CloneBody, http::Uri>; +type HyperConnect = Connect< + crate::utils::boxed_clone::BoxCloneService, + CloneBody, + http::Uri, +>; pub struct Connection { host: hyper::Uri, @@ -65,13 +71,10 @@ impl Connection { let hyper_connect: HyperConnect = Connect::new(get_connector(self.connector), builder); self.connect = Some(hyper_connect); self - } } impl Service> for Connection { - - type Response = http::Response; type Error = crate::Error; @@ -85,30 +88,28 @@ impl Service> for Connection { match self.connect { None => { panic!("connection must be built before use") - }, - Some(ref mut connect) => { - connect.poll_ready(cx).map_err(|e|e.into()) } + Some(ref mut connect) => connect.poll_ready(cx).map_err(|e| e.into()), } } fn call(&mut self, req: http::Request) -> Self::Future { - match self.connect { None => { panic!("connection must be built before use") - }, + } Some(ref mut connect) => { let uri = self.host.clone(); let call_fut = connect.call(uri); let fut = async move { - let mut con = call_fut.await.unwrap(); - con.call(req).await - .map_err(|err| err.into()) - .map(|res| res.map(boxed)) + let mut con = call_fut.await.unwrap(); + con.call(req) + .await + .map_err(|err| err.into()) + .map(|res| res.map(boxed)) }; - return Box::pin(fut) + return Box::pin(fut); } } } From ab18d5777ac1b84bfd513f8255893d5d9712a74f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=AF=9B=E6=96=87=E8=B6=85?= Date: Thu, 28 Dec 2023 11:30:59 +0800 Subject: [PATCH 41/48] Rft: adapt nacos registry and zookeeper registry (#169) * Rft: adapt nacos registry and zookeeper registry Close #168 * Rft: adapt static registry * Rft: cargo fmt --- dubbo/src/directory/mod.rs | 47 +- dubbo/src/framework.rs | 11 +- dubbo/src/registry/mod.rs | 79 ++- dubbo/src/registry/n_registry.rs | 133 ++++- dubbo/src/registry/protocol.rs | 31 +- dubbo/src/registry/types.rs | 47 +- dubbo/src/svc.rs | 4 - dubbo/src/triple/client/builder.rs | 18 +- .../echo/src/generated/grpc.examples.echo.rs | 179 +++--- examples/greeter/src/greeter/client.rs | 12 +- examples/greeter/src/greeter/server.rs | 9 +- registry/nacos/Cargo.toml | 4 +- registry/nacos/src/lib.rs | 509 ++++++++++-------- registry/zookeeper/Cargo.toml | 5 +- registry/zookeeper/src/lib.rs | 413 +++++++------- 15 files changed, 773 insertions(+), 728 deletions(-) diff --git a/dubbo/src/directory/mod.rs b/dubbo/src/directory/mod.rs index 84900aab..f0d9ebe2 100644 --- a/dubbo/src/directory/mod.rs +++ b/dubbo/src/directory/mod.rs @@ -31,6 +31,7 @@ use crate::{ svc::NewService, StdError, }; +use dubbo_base::Url; use dubbo_logger::tracing::debug; use futures_core::ready; use futures_util::future; @@ -162,7 +163,11 @@ where let (tx, rx) = channel(Self::MAX_DIRECTORY_BUFFER_SIZE); tokio::spawn(async move { - let receiver = registry.subscribe(service_name).await; + // todo use dubbo url model generate subscribe url + // category:serviceInterface:version:group + let consumer_url = format!("consumer://{}/{}", "127.0.0.1:8888", service_name); + let subscribe_url = Url::from_url(&consumer_url).unwrap(); + let receiver = registry.subscribe(subscribe_url).await; debug!("discover start!"); match receiver { Err(_e) => { @@ -217,22 +222,32 @@ where fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { loop { let pin_discover = Pin::new(&mut self.discover); - let change = ready!(pin_discover.poll_discover(cx)) - .transpose() - .map_err(|e| e.into())?; - match change { - Some(Change::Remove(key)) => { - debug!("remove key: {}", key); - self.directory.remove(&key); - } - Some(Change::Insert(key, _)) => { - debug!("insert key: {}", key); - let invoker = self.new_invoker.new_service(key.clone()); - self.directory.insert(key, invoker); + + match pin_discover.poll_discover(cx) { + Poll::Pending => { + if self.directory.is_empty() { + return Poll::Pending; + } else { + return Poll::Ready(Ok(())); + } } - None => { - debug!("stream closed"); - return Poll::Ready(Ok(())); + Poll::Ready(change) => { + let change = change.transpose().map_err(|e| e.into())?; + match change { + Some(Change::Remove(key)) => { + debug!("remove key: {}", key); + self.directory.remove(&key); + } + Some(Change::Insert(key, _)) => { + debug!("insert key: {}", key); + let invoker = self.new_invoker.new_service(key.clone()); + self.directory.insert(key, invoker); + } + None => { + debug!("stream closed"); + return Poll::Ready(Ok(())); + } + } } } } diff --git a/dubbo/src/framework.rs b/dubbo/src/framework.rs index d595f38a..2666d259 100644 --- a/dubbo/src/framework.rs +++ b/dubbo/src/framework.rs @@ -25,9 +25,9 @@ use std::{ use crate::{ protocol::{BoxExporter, Protocol}, registry::{ + n_registry::{ArcRegistry, Registry}, protocol::RegistryProtocol, types::{Registries, RegistriesOperation}, - BoxRegistry, Registry, }, }; use dubbo_base::Url; @@ -60,14 +60,14 @@ impl Dubbo { self } - pub fn add_registry(mut self, registry_key: &str, registry: BoxRegistry) -> Self { + pub fn add_registry(mut self, registry_key: &str, registry: ArcRegistry) -> Self { if self.registries.is_none() { self.registries = Some(Arc::new(Mutex::new(HashMap::new()))); } self.registries .as_ref() .unwrap() - .insert(registry_key.to_string(), Arc::new(Mutex::new(registry))); + .insert(registry_key.to_string(), registry); self } @@ -130,12 +130,13 @@ impl Dubbo { async_vec.push(exporter); //TODO multiple registry if self.registries.is_some() { - self.registries + let _ = self + .registries .as_ref() .unwrap() .default_registry() .register(url.clone()) - .unwrap(); + .await; } } } diff --git a/dubbo/src/registry/mod.rs b/dubbo/src/registry/mod.rs index d8ff6ef3..b82fda8d 100644 --- a/dubbo/src/registry/mod.rs +++ b/dubbo/src/registry/mod.rs @@ -17,47 +17,46 @@ #![allow(unused_variables, dead_code, missing_docs)] pub mod integration; -pub mod memory_registry; pub mod n_registry; pub mod protocol; pub mod types; -use std::{ - fmt::{Debug, Formatter}, - sync::Arc, -}; - -use dubbo_base::Url; - -pub type RegistryNotifyListener = Arc; -pub trait Registry { - fn register(&mut self, url: Url) -> Result<(), crate::StdError>; - fn unregister(&mut self, url: Url) -> Result<(), crate::StdError>; - - fn subscribe(&self, url: Url, listener: RegistryNotifyListener) -> Result<(), crate::StdError>; - fn unsubscribe( - &self, - url: Url, - listener: RegistryNotifyListener, - ) -> Result<(), crate::StdError>; -} - -pub trait NotifyListener { - fn notify(&self, event: ServiceEvent); - fn notify_all(&self, event: ServiceEvent); -} - -#[derive(Debug)] -pub struct ServiceEvent { - pub key: String, - pub action: String, - pub service: Vec, -} - -pub type BoxRegistry = Box; - -impl Debug for BoxRegistry { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.write_str("BoxRegistry") - } -} +// use std::{ +// fmt::{Debug, Formatter}, +// sync::Arc, +// }; + +// use dubbo_base::Url; + +// pub type RegistryNotifyListener = Arc; +// pub trait Registry { +// fn register(&mut self, url: Url) -> Result<(), crate::StdError>; +// fn unregister(&mut self, url: Url) -> Result<(), crate::StdError>; + +// fn subscribe(&self, url: Url, listener: RegistryNotifyListener) -> Result<(), crate::StdError>; +// fn unsubscribe( +// &self, +// url: Url, +// listener: RegistryNotifyListener, +// ) -> Result<(), crate::StdError>; +// } + +// pub trait NotifyListener { +// fn notify(&self, event: ServiceEvent); +// fn notify_all(&self, event: ServiceEvent); +// } + +// #[derive(Debug)] +// pub struct ServiceEvent { +// pub key: String, +// pub action: String, +// pub service: Vec, +// } + +// pub type BoxRegistry = Box; + +// impl Debug for BoxRegistry { +// fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { +// f.write_str("BoxRegistry") +// } +// } diff --git a/dubbo/src/registry/n_registry.rs b/dubbo/src/registry/n_registry.rs index 928c9b3c..abcd56b8 100644 --- a/dubbo/src/registry/n_registry.rs +++ b/dubbo/src/registry/n_registry.rs @@ -1,13 +1,22 @@ -use std::sync::Arc; +use std::{ + collections::{HashMap, HashSet}, + sync::Arc, +}; use async_trait::async_trait; use dubbo_base::Url; -use tokio::sync::mpsc::{channel, Receiver}; +use thiserror::Error; +use tokio::sync::{ + mpsc::{self, Receiver}, + Mutex, +}; use tower::discover::Change; use crate::StdError; -type DiscoverStream = Receiver, StdError>>; +pub type ServiceChange = Change; +pub type DiscoverStream = Receiver>; +pub type BoxRegistry = Box; #[async_trait] pub trait Registry { @@ -15,8 +24,7 @@ pub trait Registry { async fn unregister(&self, url: Url) -> Result<(), StdError>; - // todo service_name change to url - async fn subscribe(&self, service_name: String) -> Result; + async fn subscribe(&self, url: Url) -> Result; async fn unsubscribe(&self, url: Url) -> Result<(), StdError>; } @@ -27,13 +35,19 @@ pub struct ArcRegistry { } pub enum RegistryComponent { - NacosRegistry, + NacosRegistry(ArcRegistry), ZookeeperRegistry, StaticRegistry(StaticRegistry), } +pub struct StaticServiceValues { + listeners: Vec>>, + urls: HashSet, +} + +#[derive(Default)] pub struct StaticRegistry { - urls: Vec, + urls: Mutex>, } impl ArcRegistry { @@ -54,8 +68,8 @@ impl Registry for ArcRegistry { self.inner.unregister(url).await } - async fn subscribe(&self, service_name: String) -> Result { - self.inner.subscribe(service_name).await + async fn subscribe(&self, url: Url) -> Result { + self.inner.subscribe(url).await } async fn unsubscribe(&self, url: Url) -> Result<(), StdError> { @@ -73,11 +87,11 @@ impl Registry for RegistryComponent { todo!() } - async fn subscribe(&self, service_name: String) -> Result { + async fn subscribe(&self, url: Url) -> Result { match self { - RegistryComponent::NacosRegistry => todo!(), + RegistryComponent::NacosRegistry(registry) => registry.subscribe(url).await, RegistryComponent::ZookeeperRegistry => todo!(), - RegistryComponent::StaticRegistry(registry) => registry.subscribe(service_name).await, + RegistryComponent::StaticRegistry(registry) => registry.subscribe(url).await, } } @@ -88,31 +102,102 @@ impl Registry for RegistryComponent { impl StaticRegistry { pub fn new(urls: Vec) -> Self { - Self { urls } + let mut map = HashMap::with_capacity(urls.len()); + + for url in urls { + let service_name = url.get_service_name(); + let static_values = map + .entry(service_name) + .or_insert_with(|| StaticServiceValues { + listeners: Vec::new(), + urls: HashSet::new(), + }); + let url = url.to_string(); + static_values.urls.insert(url.clone()); + } + + Self { + urls: Mutex::new(map), + } } } #[async_trait] impl Registry for StaticRegistry { async fn register(&self, url: Url) -> Result<(), StdError> { - todo!() + let service_name = url.get_service_name(); + let mut lock = self.urls.lock().await; + + let static_values = lock + .entry(service_name) + .or_insert_with(|| StaticServiceValues { + listeners: Vec::new(), + urls: HashSet::new(), + }); + let url = url.to_string(); + static_values.urls.insert(url.clone()); + + static_values.listeners.retain(|listener| { + let ret = listener.try_send(Ok(ServiceChange::Insert(url.clone(), ()))); + ret.is_ok() + }); + + Ok(()) } async fn unregister(&self, url: Url) -> Result<(), StdError> { - todo!() - } - - async fn subscribe(&self, service_name: String) -> Result { - let (tx, rx) = channel(self.urls.len()); - for url in self.urls.iter() { - let change = Ok(Change::Insert(url.to_url(), ())); - tx.send(change).await?; + let service_name = url.get_service_name(); + let mut lock = self.urls.lock().await; + + match lock.get_mut(&service_name) { + None => Ok(()), + Some(static_values) => { + let url = url.to_string(); + static_values.urls.remove(&url); + static_values.listeners.retain(|listener| { + let ret = listener.try_send(Ok(ServiceChange::Remove(url.clone()))); + ret.is_ok() + }); + if static_values.urls.is_empty() { + lock.remove(&service_name); + } + Ok(()) + } } + } - Ok(rx) + async fn subscribe(&self, url: Url) -> Result { + let service_name = url.get_service_name(); + + let change_rx = { + let mut lock = self.urls.lock().await; + let static_values = lock + .entry(service_name) + .or_insert_with(|| StaticServiceValues { + listeners: Vec::new(), + urls: HashSet::new(), + }); + + let (tx, change_rx) = mpsc::channel(64); + static_values.listeners.push(tx); + + for url in static_values.urls.iter() { + static_values.listeners.retain(|listener| { + let ret = listener.try_send(Ok(ServiceChange::Insert(url.clone(), ()))); + ret.is_ok() + }); + } + change_rx + }; + + Ok(change_rx) } async fn unsubscribe(&self, url: Url) -> Result<(), StdError> { - todo!() + Ok(()) } } + +#[derive(Error, Debug)] +#[error("static registry error: {0}")] +struct StaticRegistryError(String); diff --git a/dubbo/src/registry/protocol.rs b/dubbo/src/registry/protocol.rs index 12509503..b9ba7221 100644 --- a/dubbo/src/registry/protocol.rs +++ b/dubbo/src/registry/protocol.rs @@ -19,11 +19,10 @@ use dubbo_base::Url; use dubbo_logger::tracing; use std::{ collections::HashMap, - fmt::{Debug, Formatter}, - sync::{Arc, Mutex, RwLock}, + sync::{Arc, RwLock}, }; -use super::{memory_registry::MemoryRegistry, BoxRegistry}; +use super::n_registry::{ArcRegistry, Registry, StaticRegistry}; use crate::{ protocol::{ triple::{triple_exporter::TripleExporter, triple_protocol::TripleProtocol}, @@ -42,19 +41,6 @@ pub struct RegistryProtocol { services: HashMap>, } -impl Debug for RegistryProtocol { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.write_str( - format!( - "RegistryProtocol services{:#?},registries,{:#?}", - self.services, - self.registries.clone() - ) - .as_str(), - ) - } -} - impl RegistryProtocol { pub fn new() -> Self { RegistryProtocol { @@ -74,16 +60,17 @@ impl RegistryProtocol { self } - pub fn get_registry(&mut self, url: Url) -> BoxRegistry { - let mem = MemoryRegistry::default(); + pub fn get_registry(&mut self, url: Url) -> ArcRegistry { + let mem = StaticRegistry::default(); + let mem = ArcRegistry::new(mem); self.registries .as_ref() .unwrap() .lock() .unwrap() - .insert(url.location, Arc::new(Mutex::new(Box::new(mem.clone())))); + .insert(url.location, mem.clone()); - Box::new(mem) + mem } } @@ -105,8 +92,8 @@ impl Protocol for RegistryProtocol { if let Some(urls) = registry_url { for url in urls.clone().iter() { if !url.service_key.is_empty() { - let mut reg = self.get_registry(url.clone()); - reg.register(url.clone()).unwrap(); + let reg = self.get_registry(url.clone()); + let _ = reg.register(url.clone()).await; } } } diff --git a/dubbo/src/registry/types.rs b/dubbo/src/registry/types.rs index ae7c7ca0..5c1687da 100644 --- a/dubbo/src/registry/types.rs +++ b/dubbo/src/registry/types.rs @@ -20,30 +20,22 @@ use std::{ sync::{Arc, Mutex}, }; -use dubbo_base::Url; -use dubbo_logger::tracing::info; use itertools::Itertools; -use crate::{ - registry::{BoxRegistry, Registry}, - StdError, -}; - -use super::RegistryNotifyListener; +use super::n_registry::ArcRegistry; -pub type SafeRegistry = Arc>; -pub type Registries = Arc>>; +pub type Registries = Arc>>; pub const DEFAULT_REGISTRY_KEY: &str = "default"; pub trait RegistriesOperation { - fn get(&self, registry_key: &str) -> SafeRegistry; - fn insert(&self, registry_key: String, registry: SafeRegistry); - fn default_registry(&self) -> SafeRegistry; + fn get(&self, registry_key: &str) -> ArcRegistry; + fn insert(&self, registry_key: String, registry: ArcRegistry); + fn default_registry(&self) -> ArcRegistry; } impl RegistriesOperation for Registries { - fn get(&self, registry_key: &str) -> SafeRegistry { + fn get(&self, registry_key: &str) -> ArcRegistry { self.as_ref() .lock() .unwrap() @@ -52,11 +44,11 @@ impl RegistriesOperation for Registries { .clone() } - fn insert(&self, registry_key: String, registry: SafeRegistry) { + fn insert(&self, registry_key: String, registry: ArcRegistry) { self.as_ref().lock().unwrap().insert(registry_key, registry); } - fn default_registry(&self) -> SafeRegistry { + fn default_registry(&self) -> ArcRegistry { let guard = self.as_ref().lock().unwrap(); let (_, result) = guard .iter() @@ -66,26 +58,3 @@ impl RegistriesOperation for Registries { result.clone() } } - -impl Registry for SafeRegistry { - fn register(&mut self, url: Url) -> Result<(), StdError> { - info!("register {}.", url); - self.lock().unwrap().register(url).expect("registry err."); - Ok(()) - } - - fn unregister(&mut self, url: Url) -> Result<(), StdError> { - self.lock().unwrap().register(url).expect("registry err."); - Ok(()) - } - - fn subscribe(&self, url: Url, listener: RegistryNotifyListener) -> Result<(), StdError> { - self.lock().unwrap().register(url).expect("registry err."); - Ok(()) - } - - fn unsubscribe(&self, url: Url, listener: RegistryNotifyListener) -> Result<(), StdError> { - self.lock().unwrap().register(url).expect("registry err."); - Ok(()) - } -} diff --git a/dubbo/src/svc.rs b/dubbo/src/svc.rs index df4c071a..db59b92d 100644 --- a/dubbo/src/svc.rs +++ b/dubbo/src/svc.rs @@ -1,9 +1,5 @@ use std::{marker::PhantomData, sync::Arc}; - - - - pub trait NewService { type Service; diff --git a/dubbo/src/triple/client/builder.rs b/dubbo/src/triple/client/builder.rs index c4e6e62c..0dadbcc1 100644 --- a/dubbo/src/triple/client/builder.rs +++ b/dubbo/src/triple/client/builder.rs @@ -59,9 +59,10 @@ impl ClientBuilder { Self { timeout: None, connector: "", - registry: Some(ArcRegistry::new(RegistryComponent::StaticRegistry( - StaticRegistry::new(vec![Url::from_url(host).unwrap()]), - ))), + registry: Some(ArcRegistry::new(StaticRegistry::new(vec![Url::from_url( + host, + ) + .unwrap()]))), direct: true, host: host.to_string(), } @@ -74,18 +75,19 @@ impl ClientBuilder { } } - pub fn with_registry(self, registry: RegistryComponent) -> Self { + pub fn with_registry(self, registry: ArcRegistry) -> Self { Self { - registry: Some(ArcRegistry::new(registry)), + registry: Some(registry), ..self } } pub fn with_host(self, host: &'static str) -> Self { Self { - registry: Some(ArcRegistry::new(RegistryComponent::StaticRegistry( - StaticRegistry::new(vec![Url::from_url(host).unwrap()]), - ))), + registry: Some(ArcRegistry::new(StaticRegistry::new(vec![Url::from_url( + host, + ) + .unwrap()]))), ..self } } diff --git a/examples/echo/src/generated/grpc.examples.echo.rs b/examples/echo/src/generated/grpc.examples.echo.rs index 07c58fe9..4521c9f8 100644 --- a/examples/echo/src/generated/grpc.examples.echo.rs +++ b/examples/echo/src/generated/grpc.examples.echo.rs @@ -36,16 +36,12 @@ pub mod echo_client { &mut self, request: Request, ) -> Result, dubbo::status::Status> { - let codec = dubbo::codegen::ProstCodec::< - super::EchoRequest, - super::EchoResponse, - >::default(); + let codec = + dubbo::codegen::ProstCodec::::default(); let invocation = RpcInvocation::default() .with_service_unique_name(String::from("grpc.examples.echo.Echo")) .with_method_name(String::from("UnaryEcho")); - let path = http::uri::PathAndQuery::from_static( - "/grpc.examples.echo.Echo/UnaryEcho", - ); + let path = http::uri::PathAndQuery::from_static("/grpc.examples.echo.Echo/UnaryEcho"); self.inner.unary(request, codec, path, invocation).await } /// ServerStreamingEcho is server side streaming. @@ -53,51 +49,51 @@ pub mod echo_client { &mut self, request: Request, ) -> Result>, dubbo::status::Status> { - let codec = dubbo::codegen::ProstCodec::< - super::EchoRequest, - super::EchoResponse, - >::default(); + let codec = + dubbo::codegen::ProstCodec::::default(); let invocation = RpcInvocation::default() .with_service_unique_name(String::from("grpc.examples.echo.Echo")) .with_method_name(String::from("ServerStreamingEcho")); let path = http::uri::PathAndQuery::from_static( "/grpc.examples.echo.Echo/ServerStreamingEcho", ); - self.inner.server_streaming(request, codec, path, invocation).await + self.inner + .server_streaming(request, codec, path, invocation) + .await } /// ClientStreamingEcho is client side streaming. pub async fn client_streaming_echo( &mut self, request: impl IntoStreamingRequest, ) -> Result, dubbo::status::Status> { - let codec = dubbo::codegen::ProstCodec::< - super::EchoRequest, - super::EchoResponse, - >::default(); + let codec = + dubbo::codegen::ProstCodec::::default(); let invocation = RpcInvocation::default() .with_service_unique_name(String::from("grpc.examples.echo.Echo")) .with_method_name(String::from("ClientStreamingEcho")); let path = http::uri::PathAndQuery::from_static( "/grpc.examples.echo.Echo/ClientStreamingEcho", ); - self.inner.client_streaming(request, codec, path, invocation).await + self.inner + .client_streaming(request, codec, path, invocation) + .await } /// BidirectionalStreamingEcho is bidi streaming. pub async fn bidirectional_streaming_echo( &mut self, request: impl IntoStreamingRequest, ) -> Result>, dubbo::status::Status> { - let codec = dubbo::codegen::ProstCodec::< - super::EchoRequest, - super::EchoResponse, - >::default(); + let codec = + dubbo::codegen::ProstCodec::::default(); let invocation = RpcInvocation::default() .with_service_unique_name(String::from("grpc.examples.echo.Echo")) .with_method_name(String::from("BidirectionalStreamingEcho")); let path = http::uri::PathAndQuery::from_static( "/grpc.examples.echo.Echo/BidirectionalStreamingEcho", ); - self.inner.bidi_streaming(request, codec, path, invocation).await + self.inner + .bidi_streaming(request, codec, path, invocation) + .await } } } @@ -114,9 +110,7 @@ pub mod echo_server { request: Request, ) -> Result, dubbo::status::Status>; ///Server streaming response type for the ServerStreamingEcho method. - type ServerStreamingEchoStream: futures_util::Stream< - Item = Result, - > + type ServerStreamingEchoStream: futures_util::Stream> + Send + 'static; /// ServerStreamingEcho is server side streaming. @@ -130,19 +124,14 @@ pub mod echo_server { request: Request>, ) -> Result, dubbo::status::Status>; ///Server streaming response type for the BidirectionalStreamingEcho method. - type BidirectionalStreamingEchoStream: futures_util::Stream< - Item = Result, - > + type BidirectionalStreamingEchoStream: futures_util::Stream> + Send + 'static; /// BidirectionalStreamingEcho is bidi streaming. async fn bidirectional_streaming_echo( &self, request: Request>, - ) -> Result< - Response, - dubbo::status::Status, - >; + ) -> Result, dubbo::status::Status>; } /// Echo is the echo service. #[derive(Debug)] @@ -172,10 +161,7 @@ pub mod echo_server { type Response = http::Response; type Error = std::convert::Infallible; type Future = BoxFuture; - fn poll_ready( - &mut self, - _cx: &mut Context<'_>, - ) -> Poll> { + fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } fn call(&mut self, req: http::Request) -> Self::Future { @@ -188,26 +174,18 @@ pub mod echo_server { } impl UnarySvc for UnaryEchoServer { type Response = super::EchoResponse; - type Future = BoxFuture< - Response, - dubbo::status::Status, - >; - fn call( - &mut self, - request: Request, - ) -> Self::Future { + type Future = BoxFuture, dubbo::status::Status>; + fn call(&mut self, request: Request) -> Self::Future { let inner = self.inner.0.clone(); let fut = async move { inner.unary_echo(request).await }; Box::pin(fut) } } let fut = async move { - let mut server = TripleServer::new( - dubbo::codegen::ProstCodec::< - super::EchoResponse, - super::EchoRequest, - >::default(), - ); + let mut server = TripleServer::new(dubbo::codegen::ProstCodec::< + super::EchoResponse, + super::EchoRequest, + >::default()); let res = server.unary(UnaryEchoServer { inner }, req).await; Ok(res) }; @@ -218,32 +196,22 @@ pub mod echo_server { struct ServerStreamingEchoServer { inner: _Inner, } - impl ServerStreamingSvc - for ServerStreamingEchoServer { + impl ServerStreamingSvc for ServerStreamingEchoServer { type Response = super::EchoResponse; type ResponseStream = T::ServerStreamingEchoStream; - type Future = BoxFuture< - Response, - dubbo::status::Status, - >; - fn call( - &mut self, - request: Request, - ) -> Self::Future { + type Future = + BoxFuture, dubbo::status::Status>; + fn call(&mut self, request: Request) -> Self::Future { let inner = self.inner.0.clone(); - let fut = async move { - inner.server_streaming_echo(request).await - }; + let fut = async move { inner.server_streaming_echo(request).await }; Box::pin(fut) } } let fut = async move { - let mut server = TripleServer::new( - dubbo::codegen::ProstCodec::< - super::EchoResponse, - super::EchoRequest, - >::default(), - ); + let mut server = TripleServer::new(dubbo::codegen::ProstCodec::< + super::EchoResponse, + super::EchoRequest, + >::default()); let res = server .server_streaming(ServerStreamingEchoServer { inner }, req) .await; @@ -256,31 +224,23 @@ pub mod echo_server { struct ClientStreamingEchoServer { inner: _Inner, } - impl ClientStreamingSvc - for ClientStreamingEchoServer { + impl ClientStreamingSvc for ClientStreamingEchoServer { type Response = super::EchoResponse; - type Future = BoxFuture< - Response, - dubbo::status::Status, - >; + type Future = BoxFuture, dubbo::status::Status>; fn call( &mut self, request: Request>, ) -> Self::Future { let inner = self.inner.0.clone(); - let fut = async move { - inner.client_streaming_echo(request).await - }; + let fut = async move { inner.client_streaming_echo(request).await }; Box::pin(fut) } } let fut = async move { - let mut server = TripleServer::new( - dubbo::codegen::ProstCodec::< - super::EchoResponse, - super::EchoRequest, - >::default(), - ); + let mut server = TripleServer::new(dubbo::codegen::ProstCodec::< + super::EchoResponse, + super::EchoRequest, + >::default()); let res = server .client_streaming(ClientStreamingEchoServer { inner }, req) .await; @@ -293,56 +253,41 @@ pub mod echo_server { struct BidirectionalStreamingEchoServer { inner: _Inner, } - impl StreamingSvc - for BidirectionalStreamingEchoServer { + impl StreamingSvc for BidirectionalStreamingEchoServer { type Response = super::EchoResponse; type ResponseStream = T::BidirectionalStreamingEchoStream; - type Future = BoxFuture< - Response, - dubbo::status::Status, - >; + type Future = + BoxFuture, dubbo::status::Status>; fn call( &mut self, request: Request>, ) -> Self::Future { let inner = self.inner.0.clone(); - let fut = async move { - inner.bidirectional_streaming_echo(request).await - }; + let fut = + async move { inner.bidirectional_streaming_echo(request).await }; Box::pin(fut) } } let fut = async move { - let mut server = TripleServer::new( - dubbo::codegen::ProstCodec::< - super::EchoResponse, - super::EchoRequest, - >::default(), - ); + let mut server = TripleServer::new(dubbo::codegen::ProstCodec::< + super::EchoResponse, + super::EchoRequest, + >::default()); let res = server - .bidi_streaming( - BidirectionalStreamingEchoServer { - inner, - }, - req, - ) + .bidi_streaming(BidirectionalStreamingEchoServer { inner }, req) .await; Ok(res) }; Box::pin(fut) } - _ => { - Box::pin(async move { - Ok( - http::Response::builder() - .status(200) - .header("grpc-status", "12") - .header("content-type", "application/grpc") - .body(empty_body()) - .unwrap(), - ) - }) - } + _ => Box::pin(async move { + Ok(http::Response::builder() + .status(200) + .header("grpc-status", "12") + .header("content-type", "application/grpc") + .body(empty_body()) + .unwrap()) + }), } } } diff --git a/examples/greeter/src/greeter/client.rs b/examples/greeter/src/greeter/client.rs index afa1b205..cb92ed0b 100644 --- a/examples/greeter/src/greeter/client.rs +++ b/examples/greeter/src/greeter/client.rs @@ -21,17 +21,21 @@ pub mod protos { } use std::env; - -use dubbo::codegen::*; +use dubbo::{codegen::*, registry::n_registry::ArcRegistry}; + +use dubbo_base::Url; use futures_util::StreamExt; use protos::{greeter_client::GreeterClient, GreeterRequest}; +use registry_nacos::NacosRegistry; #[tokio::main] async fn main() { dubbo_logger::init(); - - let builder = ClientBuilder::new().with_host("http://127.0.0.1:8888"); + + let builder = ClientBuilder::new().with_registry(ArcRegistry::new(NacosRegistry::new( + Url::from_url("nacos://127.0.0.1:8848").unwrap(), + ))); let mut cli = GreeterClient::new(builder); diff --git a/examples/greeter/src/greeter/server.rs b/examples/greeter/src/greeter/server.rs index c3bc4c5f..fd436e52 100644 --- a/examples/greeter/src/greeter/server.rs +++ b/examples/greeter/src/greeter/server.rs @@ -18,11 +18,13 @@ use std::{io::ErrorKind, pin::Pin}; use async_trait::async_trait; +use dubbo_base::Url; use futures_util::{Stream, StreamExt}; +use registry_nacos::NacosRegistry; use tokio::sync::mpsc; use tokio_stream::wrappers::ReceiverStream; -use dubbo::{codegen::*, Dubbo, registry::memory_registry::MemoryRegistry}; +use dubbo::{codegen::*, registry::n_registry::ArcRegistry, Dubbo}; use dubbo_config::RootConfig; use dubbo_logger::{ tracing::{info, span}, @@ -56,10 +58,11 @@ async fn main() { Ok(config) => config, Err(_err) => panic!("err: {:?}", _err), // response was droped }; + + let nacos_registry = NacosRegistry::new(Url::from_url("nacos://127.0.0.1:8848").unwrap()); let mut f = Dubbo::new() .with_config(r) - .add_registry("memory_registry", Box::new(MemoryRegistry::new())); - + .add_registry("nacos-registry", ArcRegistry::new(nacos_registry)); f.start().await; } diff --git a/registry/nacos/Cargo.toml b/registry/nacos/Cargo.toml index 798253a6..1fa6c196 100644 --- a/registry/nacos/Cargo.toml +++ b/registry/nacos/Cargo.toml @@ -9,13 +9,15 @@ repository = "https://github.com/apache/dubbo-rust.git" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -nacos-sdk = { version = "0.3", features = ["naming", "auth-by-http"] } +nacos-sdk = { version = "0.3", features = ["naming", "auth-by-http", "async"] } dubbo.workspace = true serde_json.workspace = true serde = { workspace = true, features = ["derive"] } anyhow.workspace = true dubbo-logger.workspace = true dubbo-base.workspace = true +tokio.workspace = true +async-trait.workspace = true [dev-dependencies] tracing-subscriber = "0.3.16" diff --git a/registry/nacos/src/lib.rs b/registry/nacos/src/lib.rs index bbbaaa2f..ad34237e 100644 --- a/registry/nacos/src/lib.rs +++ b/registry/nacos/src/lib.rs @@ -16,16 +16,20 @@ */ mod utils; +use async_trait::async_trait; use dubbo_base::Url; -use std::{ - collections::{HashMap, HashSet}, - sync::{Arc, Mutex}, -}; +use std::{collections::HashMap, sync::Arc}; +use tokio::{select, sync::mpsc}; use anyhow::anyhow; -use dubbo::registry::{NotifyListener, Registry, RegistryNotifyListener, ServiceEvent}; -use dubbo_logger::tracing::{error, info, warn}; -use nacos_sdk::api::naming::{NamingService, NamingServiceBuilder, ServiceInstance}; +use dubbo::{ + registry::n_registry::{DiscoverStream, Registry, ServiceChange}, + StdError, +}; +use dubbo_logger::tracing::{debug, error, info}; +use nacos_sdk::api::naming::{ + NamingEventListener, NamingService, NamingServiceBuilder, ServiceInstance, +}; use crate::utils::{build_nacos_client_props, is_concrete_str, is_wildcard_str, match_range}; @@ -60,7 +64,6 @@ const INNERCLASS_COMPATIBLE_SYMBOL: &str = "___"; pub struct NacosRegistry { nacos_naming_service: Arc, - listeners: Mutex>>>, } impl NacosRegistry { @@ -77,49 +80,6 @@ impl NacosRegistry { Self { nacos_naming_service: Arc::new(nacos_naming_service), - listeners: Mutex::new(HashMap::new()), - } - } - - #[allow(dead_code)] - fn get_subscribe_service_names(&self, service_name: &NacosServiceName) -> HashSet { - if service_name.is_concrete() { - let mut set = HashSet::new(); - let service_subscribe_name = service_name.to_subscriber_str(); - let service_subscriber_legacy_name = service_name.to_subscriber_legacy_string(); - if service_subscribe_name.eq(&service_subscriber_legacy_name) { - set.insert(service_subscribe_name); - } else { - set.insert(service_subscribe_name); - set.insert(service_subscriber_legacy_name); - } - - set - } else { - let list_view = self.nacos_naming_service.get_service_list( - 1, - i32::MAX, - Some( - service_name - .get_group_with_default(DEFAULT_GROUP) - .to_string(), - ), - ); - if let Err(e) = list_view { - error!("list service instances occur an error: {:?}", e); - return HashSet::default(); - } - - let list_view = list_view.unwrap(); - let set: HashSet = list_view - .0 - .into_iter() - .filter(|service_name| service_name.split(SERVICE_NAME_SEPARATOR).count() == 4) - .map(|service_name| NacosServiceName::from_service_name_str(&service_name)) - .filter(|other_service_name| service_name.is_compatible(other_service_name)) - .map(|service_name| service_name.to_subscriber_str()) - .collect(); - set } } } @@ -135,20 +95,66 @@ impl NacosRegistry { ..Default::default() } } + + fn diff<'a>( + old_service: &'a Vec, + new_services: &'a Vec, + ) -> (Vec<&'a ServiceInstance>, Vec<&'a ServiceInstance>) { + let new_hosts_map: HashMap = new_services + .iter() + .map(|hosts| (hosts.ip_and_port(), hosts)) + .collect(); + + let old_hosts_map: HashMap = old_service + .iter() + .map(|hosts| (hosts.ip_and_port(), hosts)) + .collect(); + + let mut add_hosts = Vec::<&ServiceInstance>::new(); + let mut removed_hosts = Vec::<&ServiceInstance>::new(); + + for (key, new_host) in new_hosts_map.iter() { + let old_host = old_hosts_map.get(key); + match old_host { + None => { + add_hosts.push(*new_host); + } + Some(old_host) => { + if !old_host.is_same_instance(new_host) { + removed_hosts.push(*old_host); + add_hosts.push(*new_host); + } + } + } + } + + for (key, old_host) in old_hosts_map.iter() { + let new_host = new_hosts_map.get(key); + match new_host { + None => { + removed_hosts.push(*old_host); + } + Some(_) => {} + } + } + + (removed_hosts, add_hosts) + } } +#[async_trait] impl Registry for NacosRegistry { - fn register(&mut self, url: Url) -> Result<(), dubbo::StdError> { - let side = url.get_param(SIDE_KEY).unwrap_or_default(); - let register_consumer = url - .get_param(REGISTER_CONSUMER_URL_KEY) - .unwrap_or_else(|| false.to_string()) - .parse::() - .unwrap_or(false); - if side.ne(PROVIDER_SIDE) && !register_consumer { - warn!("Please set 'dubbo.registry.parameters.register-consumer-url=true' to turn on consumer url registration."); - return Ok(()); - } + async fn register(&self, url: Url) -> Result<(), dubbo::StdError> { + // let side = url.get_param(SIDE_KEY).unwrap_or_default(); + // let register_consumer = url + // .get_param(REGISTER_CONSUMER_URL_KEY) + // .unwrap_or_else(|| false.to_string()) + // .parse::() + // .unwrap_or(false); + // if side.ne(PROVIDER_SIDE) && !register_consumer { + // warn!("Please set 'dubbo.registry.parameters.register-consumer-url=true' to turn on consumer url registration."); + // return Ok(()); + // } let nacos_service_name = NacosServiceName::new(&url); @@ -162,11 +168,10 @@ impl Registry for NacosRegistry { let nacos_service_instance = Self::create_nacos_service_instance(url); info!("register service: {}", nacos_service_name); - let ret = self.nacos_naming_service.register_instance( - nacos_service_name, - group_name, - nacos_service_instance, - ); + let ret = self + .nacos_naming_service + .register_instance(nacos_service_name, group_name, nacos_service_instance) + .await; if let Err(e) = ret { error!("register to nacos occur an error: {:?}", e); return Err(anyhow!("register to nacos occur an error: {:?}", e).into()); @@ -175,7 +180,7 @@ impl Registry for NacosRegistry { Ok(()) } - fn unregister(&mut self, url: Url) -> Result<(), dubbo::StdError> { + async fn unregister(&self, url: Url) -> Result<(), dubbo::StdError> { let nacos_service_name = NacosServiceName::new(&url); let group_name = Some( @@ -189,11 +194,10 @@ impl Registry for NacosRegistry { info!("deregister service: {}", nacos_service_name); - let ret = self.nacos_naming_service.deregister_instance( - nacos_service_name, - group_name, - nacos_service_instance, - ); + let ret = self + .nacos_naming_service + .deregister_instance(nacos_service_name, group_name, nacos_service_instance) + .await; if let Err(e) = ret { error!("deregister service from nacos occur an error: {:?}", e); return Err(anyhow!("deregister service from nacos occur an error: {:?}", e).into()); @@ -201,101 +205,161 @@ impl Registry for NacosRegistry { Ok(()) } - fn subscribe(&self, url: Url, listener: RegistryNotifyListener) -> Result<(), dubbo::StdError> { + async fn subscribe(&self, url: Url) -> Result { let service_name = NacosServiceName::new(&url); - let url_str = url.to_url(); - - info!("subscribe: {}", &url_str); + let service_group = service_name + .get_group_with_default(DEFAULT_GROUP) + .to_string(); + let subscriber_url = service_name.to_subscriber_str(); + info!("subscribe: {}", subscriber_url); + + let (listener, mut change_receiver) = ServiceChangeListener::new(); + let arc_listener = Arc::new(listener); + + let (discover_tx, discover_rx) = mpsc::channel(64); + + let nacos_naming_service = self.nacos_naming_service.clone(); + + let listener_in_task = arc_listener.clone(); + let service_group_in_task = service_group.clone(); + let subscriber_url_in_task = subscriber_url.clone(); + tokio::spawn(async move { + let listener = listener_in_task; + let service_group = service_group_in_task; + let subscriber_url = subscriber_url_in_task; + + let mut current_instances = Vec::new(); + loop { + let change = select! { + _ = discover_tx.closed() => { + debug!("service {} change task quit, unsubscribe.", subscriber_url); + None + }, + change = change_receiver.recv() => change + }; + + match change { + Some(instances) => { + debug!("service {} changed", subscriber_url); + let (remove_instances, add_instances) = + NacosRegistry::diff(¤t_instances, &instances); + + for instance in remove_instances { + let service_name = instance.service_name.as_ref(); + let url = match service_name { + None => { + format!("triple://{}:{}", instance.ip(), instance.port()) + } + Some(service_name) => { + format!( + "triple://{}:{}/{}", + instance.ip(), + instance.port(), + service_name + ) + } + }; + + match discover_tx.send(Ok(ServiceChange::Remove(url))).await { + Ok(_) => {} + Err(e) => { + error!( + "send service change failed: {:?}, maybe user unsubscribe", + e + ); + break; + } + } + } + + for instance in add_instances { + let service_name = instance.service_name.as_ref(); + let url = match service_name { + None => { + format!("triple://{}:{}", instance.ip(), instance.port()) + } + Some(service_name) => { + format!( + "triple://{}:{}/{}", + instance.ip(), + instance.port(), + service_name + ) + } + }; + + match discover_tx.send(Ok(ServiceChange::Insert(url, ()))).await { + Ok(_) => {} + Err(e) => { + error!( + "send service change failed: {:?}, maybe user unsubscribe", + e + ); + break; + } + } + } + current_instances = instances; + } + None => { + error!( + "receive service change task quit, unsubscribe {}.", + subscriber_url + ); + break; + } + } + } - let nacos_listener: Arc = { - let listeners = self.listeners.lock(); - if let Err(e) = listeners { + debug!("unsubscribe service: {}", subscriber_url); + // unsubscribe + let unsubscribe = nacos_naming_service + .unsubscribe(subscriber_url, Some(service_group), Vec::new(), listener) + .await; + + match unsubscribe { + Ok(_) => {} + Err(e) => { + error!("unsubscribe service failed: {:?}", e); + } + } + }); + + let all_instance = self + .nacos_naming_service + .get_all_instances( + subscriber_url.clone(), + Some(service_group.clone()), + Vec::new(), + false, + ) + .await?; + let _ = arc_listener.changed(all_instance); + + match self + .nacos_naming_service + .subscribe( + subscriber_url.clone(), + Some(service_group.clone()), + Vec::new(), + arc_listener, + ) + .await + { + Ok(_) => {} + Err(e) => { error!("subscribe service failed: {:?}", e); return Err(anyhow!("subscribe service failed: {:?}", e).into()); } - - let mut listeners = listeners.unwrap(); - let listener_set = listeners.get_mut(url_str.as_str()); - - let wrapper = Arc::new(NotifyListenerWrapper(listener)); - if let Some(listener_set) = listener_set { - listener_set.insert(wrapper.clone()); - } else { - let mut hash_set = HashSet::new(); - hash_set.insert(wrapper.clone()); - listeners.insert(url_str, hash_set); - } - - wrapper - }; - - let ret = self.nacos_naming_service.subscribe( - service_name.to_subscriber_str(), - Some( - service_name - .get_group_with_default(DEFAULT_GROUP) - .to_string(), - ), - Vec::new(), - nacos_listener, - ); - - if let Err(e) = ret { - error!("subscribe service failed: {:?}", e); - return Err(anyhow!("subscribe service failed: {:?}", e).into()); } - Ok(()) + Ok(discover_rx) } - fn unsubscribe( - &self, - url: Url, - listener: RegistryNotifyListener, - ) -> Result<(), dubbo::StdError> { + async fn unsubscribe(&self, url: Url) -> Result<(), dubbo::StdError> { let service_name = NacosServiceName::new(&url); - let url_str = url.to_url(); - info!("unsubscribe: {}", &url_str); - - let nacos_listener: Arc = { - let listeners = self.listeners.lock(); - if let Err(e) = listeners { - error!("unsubscribe service failed: {:?}", e); - return Err(anyhow!("unsubscribe service failed: {:?}", e).into()); - } - - let mut listeners = listeners.unwrap(); - let listener_set = listeners.get_mut(url_str.as_str()); - if listener_set.is_none() { - return Ok(()); - } - - let listener_set = listener_set.unwrap(); - - let listener = Arc::new(NotifyListenerWrapper(listener)); - let listener = listener_set.take(&listener); - if listener.is_none() { - return Ok(()); - } - - listener.unwrap() - }; - - let ret = self.nacos_naming_service.unsubscribe( - service_name.to_subscriber_str(), - Some( - service_name - .get_group_with_default(DEFAULT_GROUP) - .to_string(), - ), - Vec::new(), - nacos_listener, - ); - - if let Err(e) = ret { - error!("unsubscribe service failed: {:?}", e); - return Err(anyhow!("unsubscribe service failed: {:?}", e).into()); - } + let subscriber_url = service_name.to_subscriber_str(); + info!("unsubscribe: {}", &subscriber_url); Ok(()) } @@ -484,52 +548,43 @@ impl NacosServiceName { } } -struct NotifyListenerWrapper(Arc); - -impl std::hash::Hash for NotifyListenerWrapper { - fn hash(&self, state: &mut H) { - let ptr = self.0.as_ref(); - std::ptr::hash(ptr, state); - } +struct ServiceChangeListener { + tx: mpsc::Sender>, } -impl PartialEq for NotifyListenerWrapper { - fn eq(&self, other: &Self) -> bool { - let self_ptr = self.0.as_ref() as *const dyn NotifyListener; - let other_ptr = other.0.as_ref() as *const dyn NotifyListener; +impl ServiceChangeListener { + pub fn new() -> (Self, mpsc::Receiver>) { + let (tx, rx) = mpsc::channel(64); + let this = Self { tx }; - let (self_data_ptr, _): (*const u8, *const u8) = unsafe { std::mem::transmute(self_ptr) }; + (this, rx) + } - let (other_data_ptr, _): (*const u8, *const u8) = unsafe { std::mem::transmute(other_ptr) }; - self_data_ptr == other_data_ptr + pub fn changed(&self, instances: Vec) -> Result<(), dubbo::StdError> { + match self.tx.try_send(instances) { + Ok(_) => Ok(()), + Err(e) => { + error!("send service change failed: {:?}", e); + Err(anyhow!("send service change failed: {:?}", e).into()) + } + } } } -impl Eq for NotifyListenerWrapper {} - -impl nacos_sdk::api::naming::NamingEventListener for NotifyListenerWrapper { +impl NamingEventListener for ServiceChangeListener { fn event(&self, event: Arc) { - let service_name = event.service_name.clone(); + debug!("service change {}", event.service_name.clone()); + debug!("nacos event: {:?}", event); + let instances = event.instances.as_ref(); - let urls: Vec; - if let Some(instances) = instances { - urls = instances - .iter() - .filter_map(|data| { - let url_str = - format!("triple://{}:{}/{}", data.ip(), data.port(), service_name); - Url::from_url(&url_str) - }) - .collect(); - } else { - urls = Vec::new(); + match instances { + None => { + let _ = self.changed(Vec::default()); + } + Some(instances) => { + let _ = self.changed(instances.clone()); + } } - let notify_event = ServiceEvent { - key: service_name, - action: String::from("CHANGE"), - service: urls, - }; - self.0.notify(notify_event); } } @@ -543,9 +598,9 @@ pub mod tests { use super::*; - #[test] + #[tokio::test] #[ignore] - pub fn test_register_to_nacos() { + pub async fn test_register_to_nacos() { tracing_subscriber::fmt() .with_thread_names(true) .with_file(true) @@ -556,14 +611,14 @@ pub mod tests { .init(); let nacos_registry_url = Url::from_url("nacos://127.0.0.1:8848/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-triple-api-provider&dubbo=2.0.2&interface=org.apache.dubbo.registry.RegistryService&pid=7015").unwrap(); - let mut registry = NacosRegistry::new(nacos_registry_url); + let registry = NacosRegistry::new(nacos_registry_url); let mut service_url = Url::from_url("tri://127.0.0.1:50052/org.apache.dubbo.demo.GreeterService?anyhost=true&application=dubbo-demo-triple-api-provider&background=false&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.GreeterService&methods=sayHello,sayHelloAsync&pid=7015&service-name-mapping=true&side=provider×tamp=1670060843807").unwrap(); service_url .params .insert(SIDE_KEY.to_owned(), PROVIDER_SIDE.to_owned()); - let ret = registry.register(service_url); + let ret = registry.register(service_url).await; info!("register result: {:?}", ret); @@ -571,9 +626,9 @@ pub mod tests { thread::sleep(sleep_millis); } - #[test] + #[tokio::test] #[ignore] - pub fn test_register_and_unregister() { + pub async fn test_register_and_unregister() { tracing_subscriber::fmt() .with_thread_names(true) .with_file(true) @@ -584,14 +639,14 @@ pub mod tests { .init(); let nacos_registry_url = Url::from_url("nacos://127.0.0.1:8848/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-triple-api-provider&dubbo=2.0.2&interface=org.apache.dubbo.registry.RegistryService&pid=7015").unwrap(); - let mut registry = NacosRegistry::new(nacos_registry_url); + let registry = NacosRegistry::new(nacos_registry_url); let mut service_url = Url::from_url("tri://127.0.0.1:9090/org.apache.dubbo.demo.GreeterService?anyhost=true&application=dubbo-demo-triple-api-provider&background=false&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.GreeterService&methods=sayHello,sayHelloAsync&pid=7015&service-name-mapping=true&side=provider×tamp=1670060843807").unwrap(); service_url .params .insert(SIDE_KEY.to_owned(), PROVIDER_SIDE.to_owned()); - let ret = registry.register(service_url); + let ret = registry.register(service_url).await; info!("register result: {:?}", ret); @@ -599,7 +654,7 @@ pub mod tests { thread::sleep(sleep_millis); let unregister_url = Url::from_url("tri://127.0.0.1:9090/org.apache.dubbo.demo.GreeterService?anyhost=true&application=dubbo-demo-triple-api-provider&background=false&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.GreeterService&methods=sayHello,sayHelloAsync&pid=7015&service-name-mapping=true&side=provider×tamp=1670060843807").unwrap(); - let ret = registry.unregister(unregister_url); + let ret = registry.unregister(unregister_url).await; info!("deregister result: {:?}", ret); @@ -607,20 +662,9 @@ pub mod tests { thread::sleep(sleep_millis); } - struct TestNotifyListener; - impl NotifyListener for TestNotifyListener { - fn notify(&self, event: ServiceEvent) { - info!("notified: {:?}", event.key); - } - - fn notify_all(&self, event: ServiceEvent) { - info!("notify_all: {:?}", event.key); - } - } - - #[test] + #[tokio::test] #[ignore] - fn test_subscribe() { + pub async fn test_subscribe() { tracing_subscriber::fmt() .with_thread_names(true) .with_file(true) @@ -631,33 +675,36 @@ pub mod tests { .init(); let nacos_registry_url = Url::from_url("nacos://127.0.0.1:8848/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-triple-api-provider&dubbo=2.0.2&interface=org.apache.dubbo.registry.RegistryService&pid=7015").unwrap(); - let mut registry = NacosRegistry::new(nacos_registry_url); + let registry = NacosRegistry::new(nacos_registry_url); let mut service_url = Url::from_url("tri://127.0.0.1:50052/org.apache.dubbo.demo.GreeterService?anyhost=true&application=dubbo-demo-triple-api-provider&background=false&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.GreeterService&methods=sayHello,sayHelloAsync&pid=7015&service-name-mapping=true&side=provider×tamp=1670060843807").unwrap(); service_url .params .insert(SIDE_KEY.to_owned(), PROVIDER_SIDE.to_owned()); - let ret = registry.register(service_url); + let ret = registry.register(service_url).await; info!("register result: {:?}", ret); - let subscribe_url = Url::from_url("provider://192.168.0.102:50052/org.apache.dubbo.demo.GreeterService?anyhost=true&application=dubbo-demo-triple-api-provider&background=false&bind.ip=192.168.0.102&bind.port=50052&category=configurators&check=false&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.GreeterService&ipv6=fd00:6cb1:58a2:8ddf:0:0:0:1000&methods=sayHello,sayHelloAsync&pid=44270&service-name-mapping=true&side=provider").unwrap(); - - let ret = registry.subscribe(subscribe_url, Arc::new(TestNotifyListener)); + let subscribe_url = Url::from_url("consumer://192.168.0.102:50052/org.apache.dubbo.demo.GreeterService?anyhost=true&application=dubbo-demo-triple-api-provider&background=false&bind.ip=192.168.0.102&bind.port=50052&category=configurators&check=false&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.GreeterService&ipv6=fd00:6cb1:58a2:8ddf:0:0:0:1000&methods=sayHello,sayHelloAsync&pid=44270&service-name-mapping=true&side=provider").unwrap(); + let subscribe_ret = registry.subscribe(subscribe_url).await; - if let Err(e) = ret { + if let Err(e) = subscribe_ret { error!("error message: {:?}", e); return; } + let mut rx = subscribe_ret.unwrap(); + let change = rx.recv().await; + info!("receive change: {:?}", change); + let sleep_millis = time::Duration::from_secs(300); thread::sleep(sleep_millis); } - #[test] + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[ignore] - fn test_unsubscribe() { + pub async fn test_unsubscribe() { tracing_subscriber::fmt() .with_thread_names(true) .with_file(true) @@ -668,33 +715,35 @@ pub mod tests { .init(); let nacos_registry_url = Url::from_url("nacos://127.0.0.1:8848/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-triple-api-provider&dubbo=2.0.2&interface=org.apache.dubbo.registry.RegistryService&pid=7015").unwrap(); - let mut registry = NacosRegistry::new(nacos_registry_url); + let registry = NacosRegistry::new(nacos_registry_url); let mut service_url = Url::from_url("tri://127.0.0.1:50052/org.apache.dubbo.demo.GreeterService?anyhost=true&application=dubbo-demo-triple-api-provider&background=false&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.GreeterService&methods=sayHello,sayHelloAsync&pid=7015&service-name-mapping=true&side=provider×tamp=1670060843807").unwrap(); service_url .params .insert(SIDE_KEY.to_owned(), PROVIDER_SIDE.to_owned()); - let ret = registry.register(service_url); + let ret = registry.register(service_url).await; info!("register result: {:?}", ret); let subscribe_url = Url::from_url("provider://192.168.0.102:50052/org.apache.dubbo.demo.GreeterService?anyhost=true&application=dubbo-demo-triple-api-provider&background=false&bind.ip=192.168.0.102&bind.port=50052&category=configurators&check=false&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.GreeterService&ipv6=fd00:6cb1:58a2:8ddf:0:0:0:1000&methods=sayHello,sayHelloAsync&pid=44270&service-name-mapping=true&side=provider").unwrap(); - let listener = Arc::new(TestNotifyListener); - - let ret = registry.subscribe(subscribe_url, listener.clone()); + let ret = registry.subscribe(subscribe_url).await; if let Err(e) = ret { error!("error message: {:?}", e); return; } + let mut rx = ret.unwrap(); + let change = rx.recv().await; + info!("receive change: {:?}", change); + let sleep_millis = time::Duration::from_secs(40); thread::sleep(sleep_millis); let unsubscribe_url = Url::from_url("provider://192.168.0.102:50052/org.apache.dubbo.demo.GreeterService?anyhost=true&application=dubbo-demo-triple-api-provider&background=false&bind.ip=192.168.0.102&bind.port=50052&category=configurators&check=false&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.GreeterService&ipv6=fd00:6cb1:58a2:8ddf:0:0:0:1000&methods=sayHello,sayHelloAsync&pid=44270&service-name-mapping=true&side=provider").unwrap(); - let ret = registry.unsubscribe(unsubscribe_url, listener.clone()); + let ret = registry.unsubscribe(unsubscribe_url).await; if let Err(e) = ret { error!("error message: {:?}", e); diff --git a/registry/zookeeper/Cargo.toml b/registry/zookeeper/Cargo.toml index 2df54995..ebcb2694 100644 --- a/registry/zookeeper/Cargo.toml +++ b/registry/zookeeper/Cargo.toml @@ -9,10 +9,13 @@ repository = "https://github.com/apache/dubbo-rust.git" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -zookeeper = "0.7.0" +zookeeper = "0.8.0" dubbo.workspace = true +anyhow.workspace = true serde_json.workspace = true serde = { workspace = true, features = ["derive"] } urlencoding.workspace = true dubbo-logger.workspace = true dubbo-base.workspace = true +tokio.workspace = true +async-trait.workspace = true diff --git a/registry/zookeeper/src/lib.rs b/registry/zookeeper/src/lib.rs index e8c2c5ce..f3733d50 100644 --- a/registry/zookeeper/src/lib.rs +++ b/registry/zookeeper/src/lib.rs @@ -17,27 +17,20 @@ #![allow(unused_variables, dead_code, missing_docs)] -use std::{ - collections::{HashMap, HashSet}, - env, - sync::{Arc, Mutex, RwLock}, - time::Duration, -}; +use std::{collections::HashMap, env, sync::Arc, time::Duration}; +use async_trait::async_trait; use dubbo_base::{ constants::{DUBBO_KEY, LOCALHOST_IP, PROVIDERS_KEY}, Url, }; use dubbo_logger::tracing::{debug, error, info}; use serde::{Deserialize, Serialize}; -#[allow(unused_imports)] +use tokio::{select, sync::mpsc}; use zookeeper::{Acl, CreateMode, WatchedEvent, WatchedEventType, Watcher, ZooKeeper}; use dubbo::{ - registry::{ - memory_registry::MemoryRegistry, NotifyListener, Registry, RegistryNotifyListener, - ServiceEvent, - }, + registry::n_registry::{DiscoverStream, Registry, ServiceChange}, StdError, }; @@ -54,12 +47,9 @@ impl Watcher for LoggingWatcher { } } -//#[derive(Debug)] pub struct ZookeeperRegistry { root_path: String, zk_client: Arc, - listeners: RwLock>, - memory_registry: Arc>, } #[derive(Serialize, Deserialize, Debug)] @@ -91,24 +81,6 @@ impl ZookeeperRegistry { ZookeeperRegistry { root_path: "/services".to_string(), zk_client: Arc::new(zk_client), - listeners: RwLock::new(HashMap::new()), - memory_registry: Arc::new(Mutex::new(MemoryRegistry::default())), - } - } - - fn create_listener( - &self, - path: String, - service_name: String, - listener: RegistryNotifyListener, - ) -> ServiceInstancesChangedListener { - let mut service_names = HashSet::new(); - service_names.insert(service_name.clone()); - ServiceInstancesChangedListener { - zk_client: Arc::clone(&self.zk_client), - path, - service_name: service_name.clone(), - listener, } } @@ -127,10 +99,6 @@ impl ZookeeperRegistry { s.to_string() } - pub fn get_client(&self) -> Arc { - self.zk_client.clone() - } - // If the parent node does not exist in the ZooKeeper, Err(ZkError::NoNode) will be returned. pub fn create_path( &self, @@ -154,7 +122,7 @@ impl ZookeeperRegistry { Ok(_) => Ok(()), Err(err) => { error!("zk path {} parent not exists.", path); - Err(Box::try_from(err).unwrap()) + Err(err.into()) } } } @@ -176,16 +144,12 @@ impl ZookeeperRegistry { current.push('/'); current.push_str(node_key); if !self.exists_path(current.as_str()) { - let new_create_mode = match children == node_key { - true => create_mode, - false => CreateMode::Persistent, - }; - let new_data = match children == node_key { - true => data, - false => "", + let (new_create_mode, new_data) = match children == node_key { + true => (create_mode, data), + false => (CreateMode::Persistent, ""), }; - self.create_path(current.as_str(), new_data, new_create_mode) - .unwrap(); + + self.create_path(current.as_str(), new_data, new_create_mode)?; } } Ok(()) @@ -193,7 +157,7 @@ impl ZookeeperRegistry { pub fn delete_path(&self, path: &str) { if self.exists_path(path) { - self.get_client().delete(path, None).unwrap() + self.zk_client.delete(path, None).unwrap() } } @@ -213,6 +177,65 @@ impl ZookeeperRegistry { None } } + + pub fn diff<'a>( + old_urls: &'a Vec, + new_urls: &'a Vec, + ) -> (Vec, Vec) { + let old_urls_map: HashMap = old_urls + .iter() + .map(|url| dubbo_base::Url::from_url(url.as_str())) + .filter(|item| item.is_some()) + .map(|item| item.unwrap()) + .map(|item| { + let ip_port = item.get_ip_port(); + let url = item.encoded_raw_url_string(); + (ip_port, url) + }) + .collect(); + + let new_urls_map: HashMap = new_urls + .iter() + .map(|url| dubbo_base::Url::from_url(url.as_str())) + .filter(|item| item.is_some()) + .map(|item| item.unwrap()) + .map(|item| { + let ip_port = item.get_ip_port(); + let url = item.encoded_raw_url_string(); + (ip_port, url) + }) + .collect(); + + let mut add_hosts = Vec::new(); + let mut removed_hosts = Vec::new(); + + for (key, new_host) in new_urls_map.iter() { + let old_host = old_urls_map.get(key); + match old_host { + None => { + add_hosts.push(new_host.clone()); + } + Some(old_host) => { + if !old_host.eq(new_host) { + removed_hosts.push(old_host.clone()); + add_hosts.push(new_host.clone()); + } + } + } + } + + for (key, old_host) in old_urls_map.iter() { + let new_host = old_urls_map.get(key); + match new_host { + None => { + removed_hosts.push(old_host.clone()); + } + Some(_) => {} + } + } + + (removed_hosts, add_hosts) + } } impl Default for ZookeeperRegistry { @@ -236,8 +259,9 @@ impl Default for ZookeeperRegistry { } } +#[async_trait] impl Registry for ZookeeperRegistry { - fn register(&mut self, url: Url) -> Result<(), StdError> { + async fn register(&self, url: Url) -> Result<(), StdError> { debug!("register url: {}", url); let zk_path = format!( "/{}/{}/{}/{}", @@ -250,7 +274,7 @@ impl Registry for ZookeeperRegistry { Ok(()) } - fn unregister(&mut self, url: Url) -> Result<(), StdError> { + async fn unregister(&self, url: Url) -> Result<(), StdError> { let zk_path = format!( "/{}/{}/{}/{}", DUBBO_KEY, @@ -263,194 +287,155 @@ impl Registry for ZookeeperRegistry { } // for consumer to find the changes of providers - fn subscribe(&self, url: Url, listener: RegistryNotifyListener) -> Result<(), StdError> { + async fn subscribe(&self, url: Url) -> Result { let service_name = url.get_service_name(); let zk_path = format!("/{}/{}/{}", DUBBO_KEY, &service_name, PROVIDERS_KEY); - if self - .listeners - .read() - .unwrap() - .get(service_name.as_str()) - .is_some() - { - return Ok(()); - } - self.listeners - .write() - .unwrap() - .insert(service_name.to_string(), listener.clone()); + debug!("subscribe service: {}", zk_path); - let zk_listener = - self.create_listener(zk_path.clone(), service_name.to_string(), listener.clone()); + let (listener, mut change_rx) = ZooKeeperListener::new(); + let arc_listener = Arc::new(listener); - let zk_changed_paths = self.zk_client.get_children_w(&zk_path, zk_listener); - let result = match zk_changed_paths { - Err(err) => { - error!("zk subscribe error: {}", err); - Vec::new() + let watcher = ZooKeeperWatcher::new(arc_listener.clone(), zk_path.clone()); + + let (discover_tx, discover_rx) = mpsc::channel(64); + + let zk_client_in_task = self.zk_client.clone(); + let zk_path_in_task = zk_path.clone(); + let service_name_in_task = service_name.clone(); + let arc_listener_in_task = arc_listener.clone(); + tokio::spawn(async move { + let zk_client = zk_client_in_task; + let zk_path = zk_path_in_task; + let service_name = service_name_in_task; + let listener = arc_listener_in_task; + + let mut current_urls = Vec::new(); + + loop { + let changed = select! { + _ = discover_tx.closed() => { + info!("discover task quit, discover channel closed"); + None + }, + changed = change_rx.recv() => { + changed + } + }; + + match changed { + Some(_) => { + let zookeeper_watcher = + ZooKeeperWatcher::new(listener.clone(), zk_path.clone()); + + match zk_client.get_children_w(&zk_path, zookeeper_watcher) { + Ok(children) => { + let (removed, add) = + ZookeeperRegistry::diff(¤t_urls, &children); + + for url in removed { + match discover_tx + .send(Ok(ServiceChange::Remove(url.clone()))) + .await + { + Ok(_) => {} + Err(e) => { + error!("send service change failed: {:?}, maybe user unsubscribe", e); + break; + } + } + } + + for url in add { + match discover_tx + .send(Ok(ServiceChange::Insert(url.clone(), ()))) + .await + { + Ok(_) => {} + Err(e) => { + error!("send service change failed: {:?}, maybe user unsubscribe", e); + break; + } + } + } + + current_urls = children; + } + Err(err) => { + error!("zk subscribe error: {}", err); + break; + } + } + } + None => { + error!("receive service change task quit, unsubscribe {}.", zk_path); + break; + } + } } - Ok(urls) => urls - .iter() - .map(|node_key| { - let provider_url: Url = urlencoding::decode(node_key) - .unwrap() - .to_string() - .as_str() - .into(); - provider_url - }) - .collect(), - }; - info!("notifying {}->{:?}", service_name, result); - listener.notify(ServiceEvent { - key: service_name, - action: String::from("ADD"), - service: result, + + debug!("unsubscribe service: {}", zk_path); }); - Ok(()) - } - fn unsubscribe(&self, url: Url, listener: RegistryNotifyListener) -> Result<(), StdError> { - todo!() + arc_listener.changed(zk_path); + + Ok(discover_rx) } -} -pub struct ServiceInstancesChangedListener { - zk_client: Arc, - path: String, - service_name: String, - listener: RegistryNotifyListener, -} + async fn unsubscribe(&self, url: Url) -> Result<(), StdError> { + let service_name = url.get_service_name(); + let zk_path = format!("/{}/{}/{}", DUBBO_KEY, &service_name, PROVIDERS_KEY); -impl Watcher for ServiceInstancesChangedListener { - fn handle(&self, event: WatchedEvent) { - if let (WatchedEventType::NodeChildrenChanged, Some(path)) = (event.event_type, event.path) - { - let event_path = path.clone(); - let dirs = self - .zk_client - .get_children(&event_path, false) - .expect("msg"); - let result: Vec = dirs - .iter() - .map(|node_key| { - let provider_url: Url = node_key.as_str().into(); - provider_url - }) - .collect(); - let res = self.zk_client.get_children_w( - &path, - ServiceInstancesChangedListener { - zk_client: Arc::clone(&self.zk_client), - path: path.clone(), - service_name: self.service_name.clone(), - listener: Arc::clone(&self.listener), - }, - ); - - info!("notify {}->{:?}", self.service_name, result); - self.listener.notify(ServiceEvent { - key: self.service_name.clone(), - action: String::from("ADD"), - service: result, - }); - } + info!("unsubscribe service: {}", zk_path); + Ok(()) } } -impl NotifyListener for ServiceInstancesChangedListener { - fn notify(&self, event: ServiceEvent) { - self.listener.notify(event); +pub struct ZooKeeperListener { + tx: mpsc::Sender, +} + +impl ZooKeeperListener { + pub fn new() -> (ZooKeeperListener, mpsc::Receiver) { + let (tx, rx) = mpsc::channel(64); + let this = ZooKeeperListener { tx }; + (this, rx) } - fn notify_all(&self, event: ServiceEvent) { - self.listener.notify(event); + pub fn changed(&self, path: String) { + match self.tx.try_send(path) { + Ok(_) => {} + Err(err) => { + error!("send change list to listener occur an error: {}", err); + return; + } + } } } -#[cfg(test)] -mod tests { - use std::sync::Arc; - - use zookeeper::{Acl, CreateMode, WatchedEvent, Watcher}; - - use crate::ZookeeperRegistry; - - struct TestZkWatcher { - pub watcher: Arc>, - } +pub struct ZooKeeperWatcher { + listener: Arc, + path: String, +} - impl Watcher for TestZkWatcher { - fn handle(&self, event: WatchedEvent) { - println!("event: {:?}", event); - } +impl ZooKeeperWatcher { + pub fn new(listener: Arc, path: String) -> ZooKeeperWatcher { + ZooKeeperWatcher { listener, path } } +} - #[test] - fn zk_read_write_watcher() { - // https://github.com/bonifaido/rust-zookeeper/blob/master/examples/zookeeper_example.rs - // using ENV to set zookeeper server urls - let zkr = ZookeeperRegistry::default(); - let zk_client = zkr.get_client(); - let watcher = TestZkWatcher { - watcher: Arc::new(None), - }; - if zk_client.exists("/test", true).is_err() { - zk_client - .create( - "/test", - vec![1, 3], - Acl::open_unsafe().clone(), - CreateMode::Ephemeral, - ) - .unwrap(); - } - let zk_res = zk_client.create( - "/test", - "hello".into(), - Acl::open_unsafe().clone(), - CreateMode::Ephemeral, - ); - let result = zk_client.get_children_w("/test", watcher); - assert!(result.is_ok()); - if zk_client.exists("/test/a", true).is_err() { - zk_client.delete("/test/a", None).unwrap(); - } - if zk_client.exists("/test/a", true).is_err() { - zk_client.delete("/test/b", None).unwrap(); +impl Watcher for ZooKeeperWatcher { + fn handle(&self, event: WatchedEvent) { + info!("receive zookeeper event: {:?}", event); + let event_type: WatchedEventType = event.event_type; + match event_type { + WatchedEventType::None => { + info!("event type is none, ignore it."); + return; + } + _ => {} } - let zk_res = zk_client.create( - "/test/a", - "hello".into(), - Acl::open_unsafe().clone(), - CreateMode::Ephemeral, - ); - let zk_res = zk_client.create( - "/test/b", - "world".into(), - Acl::open_unsafe().clone(), - CreateMode::Ephemeral, - ); - let test_a_result = zk_client.get_data("/test", true); - assert!(test_a_result.is_ok()); - let vec1 = test_a_result.unwrap().0; - // data in /test should equals to "hello" - assert_eq!(String::from_utf8(vec1).unwrap(), "hello"); - zk_client.close().unwrap() - } - #[test] - fn create_path_with_parent_check() { - let zkr = ZookeeperRegistry::default(); - let path = "/du1bbo/test11111"; - let data = "hello"; - // creating a child on a not exists parent, throw a NoNode error. - // let result = zkr.create_path(path, data, CreateMode::Ephemeral); - // assert!(result.is_err()); - let create_with_parent_check_result = - zkr.create_path_with_parent_check(path, data, CreateMode::Ephemeral); - assert!(create_with_parent_check_result.is_ok()); - assert_eq!(data, zkr.get_data(path, false).unwrap()); + self.listener.changed(self.path.clone()); } } From 7ee9e739263eec9a837cbeb0a3e30b2f7d7e7567 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=AF=9B=E6=96=87=E8=B6=85?= Date: Fri, 15 Mar 2024 15:37:08 +0800 Subject: [PATCH 42/48] Ftr: add extension module (#181) * Ftr: add extension module - adapt static registry by extension - adapt nacos registry by extension link #180 * cargo fmt all * fix ci error * fix nacos image version error * Rft: re-design extension register * Fix: cargo fix * Fix: add some license for every files - extract UrlParam to single file - fix github ci error * Fix: fmt all * Fix: Add license for extension_param.rs and registry_param.rs * Fix: rename query_param_by_kv method name --- .github/workflows/github-actions.yml | 7 + Cargo.toml | 1 + common/base/Cargo.toml | 5 +- common/base/src/extension_param.rs | 85 ++ common/base/src/lib.rs | 4 + common/base/src/registry_param.rs | 355 +++++++++ common/base/src/url.rs | 285 +++---- config/src/router.rs | 17 + config/src/service.rs | 12 - dubbo/src/cluster/failover.rs | 20 +- .../router/condition/condition_router.rs | 17 + dubbo/src/cluster/router/condition/matcher.rs | 17 + dubbo/src/cluster/router/condition/mod.rs | 16 + .../cluster/router/condition/single_router.rs | 16 + .../router/manager/condition_manager.rs | 17 + dubbo/src/cluster/router/manager/mod.rs | 16 + .../cluster/router/manager/router_manager.rs | 17 + .../src/cluster/router/manager/tag_manager.rs | 17 + dubbo/src/cluster/router/mod.rs | 16 + .../cluster/router/nacos_config_center/mod.rs | 16 + .../nacos_config_center/nacos_client.rs | 17 + dubbo/src/cluster/router/router_chain.rs | 17 + dubbo/src/cluster/router/tag/mod.rs | 16 + dubbo/src/cluster/router/tag/tag_router.rs | 17 + dubbo/src/cluster/router/utils.rs | 17 + dubbo/src/codegen.rs | 3 +- dubbo/src/directory/mod.rs | 33 +- dubbo/src/extension/mod.rs | 332 ++++++++ dubbo/src/extension/registry_extension.rs | 384 +++++++++ dubbo/src/framework.rs | 66 +- dubbo/src/invoker/clone_body.rs | 19 +- dubbo/src/invoker/clone_invoker.rs | 19 +- dubbo/src/invoker/mod.rs | 20 +- dubbo/src/lib.rs | 2 +- dubbo/src/loadbalancer/mod.rs | 18 +- dubbo/src/param.rs | 17 + dubbo/src/protocol/triple/triple_invoker.rs | 4 +- dubbo/src/protocol/triple/triple_protocol.rs | 15 +- dubbo/src/registry/mod.rs | 80 +- dubbo/src/registry/n_registry.rs | 203 ----- dubbo/src/registry/protocol.rs | 40 +- dubbo/src/registry/registry.rs | 222 ++++++ dubbo/src/registry/types.rs | 60 -- dubbo/src/route/mod.rs | 21 +- dubbo/src/svc.rs | 47 +- dubbo/src/triple/client/builder.rs | 44 +- dubbo/src/triple/server/builder.rs | 12 +- dubbo/src/triple/transport/connection.rs | 5 +- examples/echo/src/echo/client.rs | 7 +- examples/echo/src/echo/server.rs | 3 +- examples/greeter/src/greeter/client.rs | 12 +- examples/greeter/src/greeter/server.rs | 9 +- protocol/base/src/invoker.rs | 6 +- registry/nacos/src/lib.rs | 748 +++++++----------- registry/nacos/src/utils/mod.rs | 102 --- registry/zookeeper/src/lib.rs | 61 +- remoting/base/src/exchange/client.rs | 2 +- 57 files changed, 2398 insertions(+), 1258 deletions(-) create mode 100644 common/base/src/extension_param.rs create mode 100644 common/base/src/registry_param.rs create mode 100644 dubbo/src/extension/mod.rs create mode 100644 dubbo/src/extension/registry_extension.rs delete mode 100644 dubbo/src/registry/n_registry.rs create mode 100644 dubbo/src/registry/registry.rs delete mode 100644 dubbo/src/registry/types.rs delete mode 100644 registry/nacos/src/utils/mod.rs diff --git a/.github/workflows/github-actions.yml b/.github/workflows/github-actions.yml index 878a5e13..8f5e9aa2 100644 --- a/.github/workflows/github-actions.yml +++ b/.github/workflows/github-actions.yml @@ -60,6 +60,13 @@ jobs: - 2181:2181 env: ZOO_MY_ID: 1 + nacos: + image: nacos/nacos-server:v2.3.1 + ports: + - 8848:8848 + - 9848:9848 + env: + MODE: standalone steps: - uses: actions/checkout@main - uses: actions-rs/toolchain@v1 diff --git a/Cargo.toml b/Cargo.toml index 0abff00d..50d46f0b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,6 +58,7 @@ serde_yaml = "0.9.4" # yaml file parser once_cell = "1.16.0" itertools = "0.10.1" bytes = "1.0" +url = "2.5.0" diff --git a/common/base/Cargo.toml b/common/base/Cargo.toml index 40579e0d..678af777 100644 --- a/common/base/Cargo.toml +++ b/common/base/Cargo.toml @@ -6,6 +6,5 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -urlencoding.workspace = true -http = "0.2" -dubbo-logger.workspace = true \ No newline at end of file +dubbo-logger.workspace = true +url.workspace = true \ No newline at end of file diff --git a/common/base/src/extension_param.rs b/common/base/src/extension_param.rs new file mode 100644 index 00000000..93e0a16c --- /dev/null +++ b/common/base/src/extension_param.rs @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +use crate::{url::UrlParam, StdError}; +use std::{borrow::Cow, convert::Infallible, str::FromStr}; + +pub struct ExtensionName(String); + +impl ExtensionName { + pub fn new(name: String) -> Self { + ExtensionName(name) + } +} + +impl UrlParam for ExtensionName { + type TargetType = String; + + fn name() -> &'static str { + "extension-name" + } + + fn value(&self) -> Self::TargetType { + self.0.clone() + } + + fn as_str(&self) -> Cow { + self.0.as_str().into() + } +} + +impl FromStr for ExtensionName { + type Err = StdError; + + fn from_str(s: &str) -> Result { + Ok(ExtensionName::new(s.to_string())) + } +} + +pub enum ExtensionType { + Registry, +} + +impl UrlParam for ExtensionType { + type TargetType = String; + + fn name() -> &'static str { + "extension-type" + } + + fn value(&self) -> Self::TargetType { + match self { + ExtensionType::Registry => "registry".to_owned(), + } + } + + fn as_str(&self) -> Cow { + match self { + ExtensionType::Registry => Cow::Borrowed("registry"), + } + } +} + +impl FromStr for ExtensionType { + type Err = Infallible; + + fn from_str(s: &str) -> Result { + match s { + "registry" => Ok(ExtensionType::Registry), + _ => panic!("the extension type enum is not in range"), + } + } +} diff --git a/common/base/src/lib.rs b/common/base/src/lib.rs index b97b342f..dcc92564 100644 --- a/common/base/src/lib.rs +++ b/common/base/src/lib.rs @@ -19,8 +19,12 @@ allow(dead_code, unused_imports, unused_variables, unused_mut) )] pub mod constants; +pub mod extension_param; pub mod node; +pub mod registry_param; pub mod url; pub use node::Node; pub use url::Url; + +pub type StdError = Box; diff --git a/common/base/src/registry_param.rs b/common/base/src/registry_param.rs new file mode 100644 index 00000000..8aa7c07f --- /dev/null +++ b/common/base/src/registry_param.rs @@ -0,0 +1,355 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +use crate::{url::UrlParam, StdError, Url}; +use std::{borrow::Cow, convert::Infallible, str::FromStr}; + +pub struct RegistryUrl(Url); + +impl RegistryUrl { + pub fn new(url: Url) -> Self { + Self(url) + } +} + +impl UrlParam for RegistryUrl { + type TargetType = Url; + + fn name() -> &'static str { + "registry" + } + + fn value(&self) -> Self::TargetType { + self.0.clone() + } + + fn as_str(&self) -> Cow { + self.0.as_str().into() + } +} + +impl FromStr for RegistryUrl { + type Err = StdError; + + fn from_str(s: &str) -> Result { + Ok(Self(s.parse()?)) + } +} + +pub struct ServiceNamespace(String); + +impl ServiceNamespace { + pub fn new(namespace: String) -> Self { + Self(namespace) + } +} + +impl UrlParam for ServiceNamespace { + type TargetType = String; + + fn name() -> &'static str { + "namespace" + } + + fn value(&self) -> Self::TargetType { + self.0.clone() + } + + fn as_str(&self) -> Cow { + self.0.as_str().into() + } +} + +impl FromStr for ServiceNamespace { + type Err = StdError; + + fn from_str(s: &str) -> Result { + Ok(Self(s.to_string())) + } +} + +impl Default for ServiceNamespace { + fn default() -> Self { + Self("public".to_string()) + } +} + +pub struct AppName(String); + +impl AppName { + pub fn new(app_name: String) -> Self { + Self(app_name) + } +} + +impl UrlParam for AppName { + type TargetType = String; + + fn name() -> &'static str { + "app_name" + } + + fn value(&self) -> Self::TargetType { + self.0.clone() + } + + fn as_str(&self) -> Cow { + self.0.as_str().into() + } +} + +impl FromStr for AppName { + type Err = StdError; + + fn from_str(s: &str) -> Result { + Ok(Self(s.to_string())) + } +} + +impl Default for AppName { + fn default() -> Self { + Self("UnknownApp".to_string()) + } +} + +pub struct InterfaceName(String); + +impl InterfaceName { + pub fn new(interface_name: String) -> Self { + Self(interface_name) + } +} + +impl UrlParam for InterfaceName { + type TargetType = String; + + fn name() -> &'static str { + "interface" + } + + fn value(&self) -> Self::TargetType { + self.0.clone() + } + + fn as_str(&self) -> Cow { + self.0.as_str().into() + } +} + +impl FromStr for InterfaceName { + type Err = StdError; + + fn from_str(s: &str) -> Result { + Ok(Self(s.to_string())) + } +} + +impl Default for InterfaceName { + fn default() -> Self { + Self("".to_string()) + } +} + +pub struct Category(String); + +impl Category { + pub fn new(category: String) -> Self { + Self(category) + } +} + +impl UrlParam for Category { + type TargetType = String; + + fn name() -> &'static str { + "category" + } + + fn value(&self) -> Self::TargetType { + self.0.clone() + } + + fn as_str(&self) -> Cow { + self.0.as_str().into() + } +} + +impl FromStr for Category { + type Err = StdError; + + fn from_str(s: &str) -> Result { + Ok(Self(s.to_string())) + } +} + +impl Default for Category { + fn default() -> Self { + Self("".to_string()) + } +} + +pub struct Version(String); + +impl Version { + pub fn new(version: String) -> Self { + Self(version) + } +} + +impl UrlParam for Version { + type TargetType = String; + + fn name() -> &'static str { + "version" + } + + fn value(&self) -> Self::TargetType { + self.0.clone() + } + + fn as_str(&self) -> Cow { + self.0.as_str().into() + } +} + +impl FromStr for Version { + type Err = StdError; + + fn from_str(s: &str) -> Result { + Ok(Self(s.to_string())) + } +} + +impl Default for Version { + fn default() -> Self { + Self("".to_string()) + } +} + +pub struct Group(String); + +impl Group { + pub fn new(group: String) -> Self { + Self(group) + } +} + +impl UrlParam for Group { + type TargetType = String; + + fn name() -> &'static str { + "group" + } + + fn value(&self) -> Self::TargetType { + self.0.clone() + } + + fn as_str(&self) -> Cow { + self.0.as_str().into() + } +} + +impl FromStr for Group { + type Err = StdError; + + fn from_str(s: &str) -> Result { + Ok(Self(s.to_string())) + } +} + +impl Default for Group { + fn default() -> Self { + Self("".to_string()) + } +} + +pub enum Side { + Provider, + Consumer, +} + +impl UrlParam for Side { + type TargetType = String; + + fn name() -> &'static str { + "side" + } + + fn value(&self) -> Self::TargetType { + match self { + Side::Consumer => "consumer".to_owned(), + Side::Provider => "provider".to_owned(), + } + } + + fn as_str(&self) -> Cow { + match self { + Side::Consumer => Cow::Borrowed("consumer"), + Side::Provider => Cow::Borrowed("provider"), + } + } +} + +impl FromStr for Side { + type Err = Infallible; + + fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "consumer" => Ok(Side::Consumer), + "provider" => Ok(Side::Provider), + _ => Ok(Side::Consumer), + } + } +} + +impl Default for Side { + fn default() -> Self { + Side::Consumer + } +} + +pub struct StaticInvokerUrls(String); + +impl UrlParam for StaticInvokerUrls { + type TargetType = Vec; + + fn name() -> &'static str { + "static-invoker-urls" + } + + fn value(&self) -> Self::TargetType { + self.0.split(",").map(|url| url.parse().unwrap()).collect() + } + + fn as_str(&self) -> Cow { + Cow::Borrowed(&self.0) + } +} + +impl FromStr for StaticInvokerUrls { + type Err = Infallible; + + fn from_str(s: &str) -> Result { + Ok(Self(s.to_string())) + } +} + +impl Default for StaticInvokerUrls { + fn default() -> Self { + Self(String::default()) + } +} diff --git a/common/base/src/url.rs b/common/base/src/url.rs index 82b026fd..ac97f261 100644 --- a/common/base/src/url.rs +++ b/common/base/src/url.rs @@ -16,237 +16,162 @@ */ use std::{ + borrow::Cow, collections::HashMap, - fmt::{Display, Formatter}, + fmt::{Debug, Display, Formatter}, + str::FromStr, }; -use crate::constants::{GROUP_KEY, INTERFACE_KEY, VERSION_KEY}; -use http::Uri; - -#[derive(Debug, Clone, Default, PartialEq)] +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Url { - pub raw_url_string: String, - // value of scheme is different to base name, eg. triple -> tri:// - pub scheme: String, - pub location: String, - pub ip: String, - pub port: String, - // serviceKey format in dubbo java and go '{group}/{interfaceName}:{version}' - pub service_key: String, - // same to interfaceName - pub service_name: String, - pub params: HashMap, + inner: url::Url, } impl Url { - pub fn new() -> Self { - Default::default() + pub fn empty() -> Self { + "empty://localhost".parse().unwrap() + } + + pub fn protocol(&self) -> &str { + self.inner.scheme() + } + + pub fn host(&self) -> Option<&str> { + self.inner.host_str() + } + + pub fn authority(&self) -> &str { + self.inner.authority() + } + + pub fn username(&self) -> &str { + self.inner.username() + } + + pub fn password(&self) -> Option<&str> { + self.inner.password() } - pub fn from_url(url: &str) -> Option { - // url: triple://127.0.0.1:8888/helloworld.Greeter - let uri = url - .parse::() - .map_err(|err| { - dubbo_logger::tracing::error!("fail to parse url({}), err: {:?}", url, err); - }) - .unwrap(); - let query = uri.path_and_query().unwrap().query(); - let mut url_inst = Self { - raw_url_string: url.to_string(), - scheme: uri.scheme_str()?.to_string(), - ip: uri.authority()?.host().to_string(), - port: uri.authority()?.port()?.to_string(), - location: uri.authority()?.to_string(), - service_key: uri.path().trim_start_matches('/').to_string(), - service_name: uri.path().trim_start_matches('/').to_string(), - params: if let Some(..) = query { - Url::decode(query.unwrap()) - } else { - HashMap::new() - }, - }; - url_inst.renew_raw_url_string(); - Some(url_inst) + pub fn port(&self) -> Option { + self.inner.port_or_known_default() } - pub fn get_service_key(&self) -> String { - self.service_key.clone() + pub fn path(&self) -> &str { + self.inner.path() } - pub fn get_service_name(&self) -> String { - self.service_name.clone() + pub fn query(&self) -> Option { + self.inner + .query_pairs() + .find(|(k, _)| k == T::name()) + .map(|(_, v)| T::from_str(&v).ok()) + .flatten() } - pub fn get_param(&self, key: &str) -> Option { - self.params.get(key).cloned() + pub fn query_param_by_key(&self, key: &str) -> Option { + self.inner + .query_pairs() + .find(|(k, _)| k == key) + .map(|(_, v)| v.into_owned()) } - fn encode_param(&self) -> String { - let mut params_vec: Vec = Vec::new(); - for (k, v) in self.params.iter() { - // let tmp = format!("{}={}", k, v); - params_vec.push(format!("{}={}", k, v)); - } - if params_vec.is_empty() { - "".to_string() - } else { - format!("?{}", params_vec.join("&")) - } + pub fn all_query_params(&self) -> HashMap { + self.inner + .query_pairs() + .map(|(k, v)| (k.into_owned(), v.into_owned())) + .collect() } - pub fn params_count(&self) -> usize { - self.params.len() + pub fn set_protocol(&mut self, protocol: &str) { + let _ = self.inner.set_scheme(protocol); } - fn decode(raw_query_string: &str) -> HashMap { - let mut params = HashMap::new(); - let p: Vec = raw_query_string - .split('&') - .map(|v| v.trim().to_string()) - .collect(); - for v in p.iter() { - let values: Vec = v.split('=').map(|v| v.trim().to_string()).collect(); - if values.len() != 2 { - continue; - } - params.insert(values[0].clone(), values[1].clone()); - } - params + pub fn set_host(&mut self, host: &str) { + let _ = self.inner.set_host(Some(host)); } - pub fn set_param(&mut self, key: &str, value: &str) { - self.params.insert(key.to_string(), value.to_string()); - self.renew_raw_url_string(); + pub fn set_port(&mut self, port: u16) { + let _ = self.inner.set_port(Some(port)); } - pub fn raw_url_string(&self) -> String { - self.raw_url_string.clone() + pub fn set_username(&mut self, username: &str) { + let _ = self.inner.set_username(username); } - pub fn encoded_raw_url_string(&self) -> String { - urlencoding::encode(self.raw_url_string.as_str()).to_string() + pub fn set_password(&mut self, password: &str) { + let _ = self.inner.set_password(Some(password)); } - fn build_service_key(&self) -> String { - format!( - "{group}/{interfaceName}:{version}", - group = self.get_param(GROUP_KEY).unwrap_or("default".to_string()), - interfaceName = self.get_param(INTERFACE_KEY).unwrap_or("error".to_string()), - version = self.get_param(VERSION_KEY).unwrap_or("1.0.0".to_string()) - ) + pub fn set_path(&mut self, path: &str) { + let _ = self.inner.set_path(path); } - pub fn to_url(&self) -> String { - self.raw_url_string() + pub fn extend_pairs(&mut self, pairs: impl Iterator) { + let mut query_pairs = self.inner.query_pairs_mut(); + query_pairs.extend_pairs(pairs); } - fn renew_raw_url_string(&mut self) { - self.raw_url_string = format!( - "{}://{}:{}/{}{}", - self.scheme, - self.ip, - self.port, - self.service_name, - self.encode_param() - ); - self.service_key = self.build_service_key() + pub fn add_query_param(&mut self, param: T) { + let mut pairs = self.inner.query_pairs_mut(); + pairs.append_pair(T::name(), ¶m.as_str()); } - // short_url is used for tcp listening - pub fn short_url(&self) -> String { - format!( - "{}://{}:{}/{}", - self.scheme, self.ip, self.port, self.service_name - ) + pub fn remove_query_param(&mut self) { + let query = self.inner.query_pairs().filter(|(k, v)| k.ne(T::name())); + let mut inner_url = self.inner.clone(); + inner_url.query_pairs_mut().clear().extend_pairs(query); + self.inner = inner_url; } - pub fn protocol(&self) -> String { - self.scheme.clone() + pub fn remove_all_param(&mut self) { + self.inner.query_pairs_mut().clear(); } - pub fn get_ip_port(&self) -> String { - format!("{}:{}", self.ip, self.port) + pub fn as_str(&self) -> &str { + self.inner.as_str() + } + + pub fn short_url_without_query(&self) -> String { + let mut url = self.inner.clone(); + url.set_query(Some("")); + url.into() + } +} + +impl FromStr for Url { + type Err = url::ParseError; + + fn from_str(s: &str) -> Result { + Ok(Url { + inner: url::Url::parse(s)?, + }) } } impl Display for Url { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.write_str(self.raw_url_string().as_str()) + std::fmt::Display::fmt(&self.inner, f) } } -impl Into for Url { - fn into(self) -> Uri { - self.raw_url_string.parse::().unwrap() +impl Debug for Url { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + std::fmt::Debug::fmt(&self.inner, f) } } -impl From<&str> for Url { - fn from(url: &str) -> Self { - Url::from_url(url).unwrap() +impl From for String { + fn from(url: Url) -> Self { + url.inner.into() } } -#[cfg(test)] -mod tests { - use crate::{ - constants::{ANYHOST_KEY, VERSION_KEY}, - url::Url, - }; - - #[test] - fn test_from_url() { - let mut u1 = Url::from_url("tri://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&\ - application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&\ - environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&\ - module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&\ - side=provider&timeout=3000×tamp=1556509797245&version=1.0.0&application=test"); - assert_eq!( - u1.as_ref().unwrap().service_key, - "default/com.ikurento.user.UserProvider:1.0.0" - ); - assert_eq!( - u1.as_ref() - .unwrap() - .get_param(ANYHOST_KEY) - .unwrap() - .as_str(), - "true" - ); - assert_eq!( - u1.as_ref() - .unwrap() - .get_param("default.timeout") - .unwrap() - .as_str(), - "10000" - ); - assert_eq!(u1.as_ref().unwrap().scheme, "tri"); - assert_eq!(u1.as_ref().unwrap().ip, "127.0.0.1"); - assert_eq!(u1.as_ref().unwrap().port, "20000"); - assert_eq!(u1.as_ref().unwrap().params_count(), 18); - u1.as_mut().unwrap().set_param("key1", "value1"); - assert_eq!( - u1.as_ref().unwrap().get_param("key1").unwrap().as_str(), - "value1" - ); - assert_eq!( - u1.as_ref() - .unwrap() - .get_param(VERSION_KEY) - .unwrap() - .as_str(), - "1.0.0" - ); - } - - #[test] - fn test2() { - let url: Url = "tri://0.0.0.0:8888/org.apache.dubbo.sample.tri.Greeter".into(); - assert_eq!( - url.raw_url_string(), - "tri://0.0.0.0:8888/org.apache.dubbo.sample.tri.Greeter" - ) - } +pub trait UrlParam: FromStr { + type TargetType; + + fn name() -> &'static str; + + fn value(&self) -> Self::TargetType; + + fn as_str(&self) -> Cow; } diff --git a/config/src/router.rs b/config/src/router.rs index b45bd478..7976f6ea 100644 --- a/config/src/router.rs +++ b/config/src/router.rs @@ -1,3 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Default)] diff --git a/config/src/service.rs b/config/src/service.rs index 1f85a926..8a1f1910 100644 --- a/config/src/service.rs +++ b/config/src/service.rs @@ -41,16 +41,4 @@ impl ServiceConfig { pub fn protocol(self, protocol: String) -> Self { Self { protocol, ..self } } - - // pub fn get_url(&self) -> Vec { - // let mut urls = Vec::new(); - // for (_, conf) in self.protocol_configs.iter() { - // urls.push(Url { - // url: conf.to_owned().to_url(), - // service_key: "".to_string(), - // }); - // } - - // urls - // } } diff --git a/dubbo/src/cluster/failover.rs b/dubbo/src/cluster/failover.rs index 8a00c9fb..a223ddf6 100644 --- a/dubbo/src/cluster/failover.rs +++ b/dubbo/src/cluster/failover.rs @@ -1,12 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + use std::task::Poll; +use dubbo_base::StdError; use futures_util::future; use http::Request; use tower::{retry::Retry, util::Oneshot, ServiceExt}; use tower_service::Service; -use crate::StdError; - pub struct Failover { inner: N, // loadbalancer service } diff --git a/dubbo/src/cluster/router/condition/condition_router.rs b/dubbo/src/cluster/router/condition/condition_router.rs index 73aca005..21b525ac 100644 --- a/dubbo/src/cluster/router/condition/condition_router.rs +++ b/dubbo/src/cluster/router/condition/condition_router.rs @@ -1,3 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + use crate::{ cluster::router::{condition::single_router::ConditionSingleRouter, Router}, codegen::RpcInvocation, diff --git a/dubbo/src/cluster/router/condition/matcher.rs b/dubbo/src/cluster/router/condition/matcher.rs index 92bbe2da..2ee33d6e 100644 --- a/dubbo/src/cluster/router/condition/matcher.rs +++ b/dubbo/src/cluster/router/condition/matcher.rs @@ -1,3 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + use regex::Regex; use std::{collections::HashSet, error::Error, option::Option}; diff --git a/dubbo/src/cluster/router/condition/mod.rs b/dubbo/src/cluster/router/condition/mod.rs index 7285b88f..d4a83b90 100644 --- a/dubbo/src/cluster/router/condition/mod.rs +++ b/dubbo/src/cluster/router/condition/mod.rs @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ pub mod condition_router; pub mod matcher; pub mod single_router; diff --git a/dubbo/src/cluster/router/condition/single_router.rs b/dubbo/src/cluster/router/condition/single_router.rs index 5f06aa8f..54c61cb1 100644 --- a/dubbo/src/cluster/router/condition/single_router.rs +++ b/dubbo/src/cluster/router/condition/single_router.rs @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ use dubbo_base::Url; use dubbo_logger::tracing::info; use regex::Regex; diff --git a/dubbo/src/cluster/router/manager/condition_manager.rs b/dubbo/src/cluster/router/manager/condition_manager.rs index 7ad5e1b6..77729503 100644 --- a/dubbo/src/cluster/router/manager/condition_manager.rs +++ b/dubbo/src/cluster/router/manager/condition_manager.rs @@ -1,3 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + use crate::cluster::router::condition::{ condition_router::{ConditionRouter, ConditionSingleRouters}, single_router::ConditionSingleRouter, diff --git a/dubbo/src/cluster/router/manager/mod.rs b/dubbo/src/cluster/router/manager/mod.rs index 025f6c16..593fa22d 100644 --- a/dubbo/src/cluster/router/manager/mod.rs +++ b/dubbo/src/cluster/router/manager/mod.rs @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ mod condition_manager; pub mod router_manager; mod tag_manager; diff --git a/dubbo/src/cluster/router/manager/router_manager.rs b/dubbo/src/cluster/router/manager/router_manager.rs index e963181e..e6c8b6c3 100644 --- a/dubbo/src/cluster/router/manager/router_manager.rs +++ b/dubbo/src/cluster/router/manager/router_manager.rs @@ -1,3 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + use crate::cluster::router::{ manager::{condition_manager::ConditionRouterManager, tag_manager::TagRouterManager}, nacos_config_center::nacos_client::NacosClient, diff --git a/dubbo/src/cluster/router/manager/tag_manager.rs b/dubbo/src/cluster/router/manager/tag_manager.rs index 8dc24999..f028af21 100644 --- a/dubbo/src/cluster/router/manager/tag_manager.rs +++ b/dubbo/src/cluster/router/manager/tag_manager.rs @@ -1,3 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + use crate::cluster::router::tag::tag_router::{TagRouter, TagRouterInner}; use dubbo_config::router::TagRouterConfig; use std::sync::{Arc, RwLock}; diff --git a/dubbo/src/cluster/router/mod.rs b/dubbo/src/cluster/router/mod.rs index 17c9aec2..edc081b8 100644 --- a/dubbo/src/cluster/router/mod.rs +++ b/dubbo/src/cluster/router/mod.rs @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ pub mod condition; pub mod manager; pub mod nacos_config_center; diff --git a/dubbo/src/cluster/router/nacos_config_center/mod.rs b/dubbo/src/cluster/router/nacos_config_center/mod.rs index 7878fa9f..71722315 100644 --- a/dubbo/src/cluster/router/nacos_config_center/mod.rs +++ b/dubbo/src/cluster/router/nacos_config_center/mod.rs @@ -1 +1,17 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ pub mod nacos_client; diff --git a/dubbo/src/cluster/router/nacos_config_center/nacos_client.rs b/dubbo/src/cluster/router/nacos_config_center/nacos_client.rs index ce72641a..68b5f096 100644 --- a/dubbo/src/cluster/router/nacos_config_center/nacos_client.rs +++ b/dubbo/src/cluster/router/nacos_config_center/nacos_client.rs @@ -1,3 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + use crate::cluster::router::manager::router_manager::{ get_global_router_manager, RouterConfigChangeEvent, }; diff --git a/dubbo/src/cluster/router/router_chain.rs b/dubbo/src/cluster/router/router_chain.rs index 42d5826f..601bc5e1 100644 --- a/dubbo/src/cluster/router/router_chain.rs +++ b/dubbo/src/cluster/router/router_chain.rs @@ -1,3 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + use crate::{cluster::router::BoxRouter, invocation::RpcInvocation}; use dubbo_base::Url; use std::{collections::HashMap, sync::Arc}; diff --git a/dubbo/src/cluster/router/tag/mod.rs b/dubbo/src/cluster/router/tag/mod.rs index 6ac5b218..673a7201 100644 --- a/dubbo/src/cluster/router/tag/mod.rs +++ b/dubbo/src/cluster/router/tag/mod.rs @@ -1 +1,17 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ pub mod tag_router; diff --git a/dubbo/src/cluster/router/tag/tag_router.rs b/dubbo/src/cluster/router/tag/tag_router.rs index 7a83ea57..3d28f936 100644 --- a/dubbo/src/cluster/router/tag/tag_router.rs +++ b/dubbo/src/cluster/router/tag/tag_router.rs @@ -1,3 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + use crate::{ cluster::router::{utils::to_original_map, Router}, codegen::RpcInvocation, diff --git a/dubbo/src/cluster/router/utils.rs b/dubbo/src/cluster/router/utils.rs index 2ca50fcc..eca98f6e 100644 --- a/dubbo/src/cluster/router/utils.rs +++ b/dubbo/src/cluster/router/utils.rs @@ -1,3 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + use dubbo_base::Url; use std::{collections::HashMap, string::String}; diff --git a/dubbo/src/codegen.rs b/dubbo/src/codegen.rs index 452f560d..eb1d3850 100644 --- a/dubbo/src/codegen.rs +++ b/dubbo/src/codegen.rs @@ -22,6 +22,7 @@ pub use std::{ pub use async_trait::async_trait; pub use bytes::Bytes; +pub use dubbo_base::StdError; pub use http_body::Body; pub use hyper::Body as hyperBody; pub use tower_service::Service; @@ -39,7 +40,7 @@ pub use super::{ TripleServer, }, }, - BoxBody, BoxFuture, StdError, + BoxBody, BoxFuture, }; pub use crate::{ filter::{service::FilterService, Filter}, diff --git a/dubbo/src/directory/mod.rs b/dubbo/src/directory/mod.rs index f0d9ebe2..ace67e68 100644 --- a/dubbo/src/directory/mod.rs +++ b/dubbo/src/directory/mod.rs @@ -27,21 +27,21 @@ use crate::{ invocation::Invocation, invoker::{clone_invoker::CloneInvoker, NewInvoker}, param::Param, - registry::n_registry::Registry, svc::NewService, - StdError, }; -use dubbo_base::Url; -use dubbo_logger::tracing::debug; -use futures_core::ready; +use dubbo_base::{StdError, Url}; +use dubbo_logger::tracing::{debug, error}; use futures_util::future; use tokio::sync::mpsc::channel; use tokio_stream::wrappers::ReceiverStream; use tower::{ buffer::Buffer, discover::{Change, Discover}, + ServiceExt, }; +use crate::extension::registry_extension::{proxy::RegistryProxy, Registry}; +use dubbo_base::registry_param::InterfaceName; use tower_service::Service; type BufferedDirectory = @@ -49,7 +49,8 @@ type BufferedDirectory = pub struct NewCachedDirectory where - N: Registry + Clone + Send + Sync + 'static, + N: Service<(), Response = RegistryProxy> + Send + Clone + 'static, + >::Future: Send + 'static, { inner: CachedDirectory, RpcInvocation>, } @@ -76,7 +77,8 @@ pub struct Directory { impl NewCachedDirectory where - N: Registry + Clone + Send + Sync + 'static, + N: Service<(), Response = RegistryProxy> + Send + Clone + 'static, + >::Future: Send + 'static, { pub fn layer() -> impl tower_layer::Layer { tower_layer::layer_fn(|inner: N| { @@ -92,7 +94,8 @@ impl NewService for NewCachedDirectory where T: Param, // service registry - N: Registry + Clone + Send + Sync + 'static, + N: Service<(), Response = RegistryProxy> + Send + Clone + 'static, + >::Future: Send + 'static, { type Service = BufferedDirectory; @@ -151,14 +154,15 @@ impl NewService for NewDirectory where T: Param, // service registry - N: Registry + Clone + Send + Sync + 'static, + N: Service<(), Response = RegistryProxy> + Send + Clone + 'static, + >::Future: Send + 'static, { type Service = BufferedDirectory; fn new_service(&self, target: T) -> Self::Service { let service_name = target.param().get_target_service_unique_name(); - let registry = self.inner.clone(); + let fut = self.inner.clone().oneshot(()); let (tx, rx) = channel(Self::MAX_DIRECTORY_BUFFER_SIZE); @@ -166,7 +170,14 @@ where // todo use dubbo url model generate subscribe url // category:serviceInterface:version:group let consumer_url = format!("consumer://{}/{}", "127.0.0.1:8888", service_name); - let subscribe_url = Url::from_url(&consumer_url).unwrap(); + let mut subscribe_url: Url = consumer_url.parse().unwrap(); + subscribe_url.add_query_param(InterfaceName::new(service_name)); + + let Ok(registry) = fut.await else { + error!("registry extension load failed."); + return; + }; + let receiver = registry.subscribe(subscribe_url).await; debug!("discover start!"); match receiver { diff --git a/dubbo/src/extension/mod.rs b/dubbo/src/extension/mod.rs new file mode 100644 index 00000000..5641bea8 --- /dev/null +++ b/dubbo/src/extension/mod.rs @@ -0,0 +1,332 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +pub mod registry_extension; + +use crate::{ + extension::registry_extension::proxy::RegistryProxy, registry::registry::StaticRegistry, +}; +use dubbo_base::{extension_param::ExtensionType, url::UrlParam, StdError, Url}; +use dubbo_logger::tracing::{error, info}; +use thiserror::Error; +use tokio::sync::oneshot; + +pub static EXTENSIONS: once_cell::sync::Lazy = + once_cell::sync::Lazy::new(|| ExtensionDirectory::init()); + +#[derive(Default)] +struct ExtensionDirectory { + registry_extension_loader: registry_extension::RegistryExtensionLoader, +} + +impl ExtensionDirectory { + fn init() -> ExtensionDirectoryCommander { + let (tx, mut rx) = tokio::sync::mpsc::channel::(64); + + tokio::spawn(async move { + let mut extension_directory = ExtensionDirectory::default(); + + // register static registry extension + let _ = extension_directory + .register( + StaticRegistry::name(), + StaticRegistry::convert_to_extension_factories(), + ExtensionType::Registry, + ) + .await; + + while let Some(extension_opt) = rx.recv().await { + match extension_opt { + ExtensionOpt::Register( + extension_name, + extension_factories, + extension_type, + tx, + ) => { + let result = extension_directory + .register(extension_name, extension_factories, extension_type) + .await; + let _ = tx.send(result); + } + ExtensionOpt::Remove(extension_name, extension_type, tx) => { + let result = extension_directory + .remove(extension_name, extension_type) + .await; + let _ = tx.send(result); + } + ExtensionOpt::Load(url, extension_type, tx) => { + let result = extension_directory.load(url, extension_type).await; + let _ = tx.send(result); + } + } + } + }); + + ExtensionDirectoryCommander { sender: tx } + } + + async fn register( + &mut self, + extension_name: String, + extension_factories: ExtensionFactories, + extension_type: ExtensionType, + ) -> Result<(), StdError> { + match extension_type { + ExtensionType::Registry => match extension_factories { + ExtensionFactories::RegistryExtensionFactory(registry_extension_factory) => { + self.registry_extension_loader + .register(extension_name, registry_extension_factory) + .await; + Ok(()) + } + }, + } + } + + async fn remove( + &mut self, + extension_name: String, + extension_type: ExtensionType, + ) -> Result<(), StdError> { + match extension_type { + ExtensionType::Registry => { + self.registry_extension_loader.remove(extension_name).await; + Ok(()) + } + } + } + + async fn load( + &mut self, + url: Url, + extension_type: ExtensionType, + ) -> Result { + match extension_type { + ExtensionType::Registry => { + let extension = self.registry_extension_loader.load(&url).await; + match extension { + Ok(extension) => Ok(Extensions::Registry(extension)), + Err(err) => { + error!("load extension failed: {}", err); + Err(err) + } + } + } + } + } +} + +pub struct ExtensionDirectoryCommander { + sender: tokio::sync::mpsc::Sender, +} + +impl ExtensionDirectoryCommander { + #[allow(private_bounds)] + pub async fn register(&self) -> Result<(), StdError> + where + T: Extension, + T: ExtensionMetaInfo, + T: ConvertToExtensionFactories, + { + let extension_name = T::name(); + let extension_factories = T::convert_to_extension_factories(); + let extension_type = T::extension_type(); + + info!( + "register extension: {}, type: {}", + extension_name, + extension_type.as_str() + ); + + let (tx, rx) = oneshot::channel(); + + let send = self + .sender + .send(ExtensionOpt::Register( + extension_name.clone(), + extension_factories, + extension_type, + tx, + )) + .await; + + let Ok(_) = send else { + let err_msg = format!("register extension {} failed", extension_name); + return Err(RegisterExtensionError::new(err_msg).into()); + }; + + let ret = rx.await; + + match ret { + Ok(_) => Ok(()), + Err(_) => { + let err_msg = format!("register extension {} failed", extension_name); + Err(RegisterExtensionError::new(err_msg).into()) + } + } + } + + #[allow(private_bounds)] + pub async fn remove(&self) -> Result<(), StdError> + where + T: Extension, + T: ExtensionMetaInfo, + { + let extension_name = T::name(); + let extension_type = T::extension_type(); + + info!( + "remove extension: {}, type: {}", + extension_name, + extension_type.as_str() + ); + + let (tx, rx) = oneshot::channel(); + + let send = self + .sender + .send(ExtensionOpt::Remove( + extension_name.clone(), + extension_type, + tx, + )) + .await; + + let Ok(_) = send else { + let err_msg = format!("remove extension {} failed", extension_name); + return Err(RemoveExtensionError::new(err_msg).into()); + }; + + let ret = rx.await; + + match ret { + Ok(_) => Ok(()), + Err(_) => { + let err_msg = format!("remove extension {} failed", extension_name); + Err(RemoveExtensionError::new(err_msg).into()) + } + } + } + + pub async fn load_registry(&self, url: Url) -> Result { + let url_str = url.to_string(); + info!("load registry extension: {}", url_str); + + let (tx, rx) = oneshot::channel(); + + let send = self + .sender + .send(ExtensionOpt::Load(url, ExtensionType::Registry, tx)) + .await; + + let Ok(_) = send else { + let err_msg = format!("load registry extension failed: {}", url_str); + return Err(LoadExtensionError::new(err_msg).into()); + }; + + let extensions = rx.await; + + let Ok(extension) = extensions else { + let err_msg = format!("load registry extension failed: {}", url_str); + return Err(LoadExtensionError::new(err_msg).into()); + }; + + let Ok(extensions) = extension else { + let err_msg = format!("load registry extension failed: {}", url_str); + return Err(LoadExtensionError::new(err_msg).into()); + }; + + match extensions { + Extensions::Registry(proxy) => Ok(proxy), + } + } +} + +enum ExtensionOpt { + Register( + String, + ExtensionFactories, + ExtensionType, + oneshot::Sender>, + ), + Remove(String, ExtensionType, oneshot::Sender>), + Load( + Url, + ExtensionType, + oneshot::Sender>, + ), +} + +pub(crate) trait Sealed {} + +#[allow(private_bounds)] +#[async_trait::async_trait] +pub trait Extension: Sealed { + type Target; + + fn name() -> String; + + async fn create(url: &Url) -> Result; +} + +#[allow(private_bounds)] +pub(crate) trait ExtensionMetaInfo { + fn extension_type() -> ExtensionType; +} + +pub(crate) enum Extensions { + Registry(RegistryProxy), +} + +pub(crate) enum ExtensionFactories { + RegistryExtensionFactory(registry_extension::RegistryExtensionFactory), +} + +#[allow(private_bounds)] +pub(crate) trait ConvertToExtensionFactories { + fn convert_to_extension_factories() -> ExtensionFactories; +} + +#[derive(Error, Debug)] +#[error("{0}")] +pub(crate) struct RegisterExtensionError(String); + +impl RegisterExtensionError { + pub fn new(msg: String) -> Self { + RegisterExtensionError(msg) + } +} + +#[derive(Error, Debug)] +#[error("{0}")] +pub struct RemoveExtensionError(String); + +impl RemoveExtensionError { + pub fn new(msg: String) -> Self { + RemoveExtensionError(msg) + } +} + +#[derive(Error, Debug)] +#[error("{0}")] +pub struct LoadExtensionError(String); + +impl LoadExtensionError { + pub fn new(msg: String) -> Self { + LoadExtensionError(msg) + } +} diff --git a/dubbo/src/extension/registry_extension.rs b/dubbo/src/extension/registry_extension.rs new file mode 100644 index 00000000..e27d6a58 --- /dev/null +++ b/dubbo/src/extension/registry_extension.rs @@ -0,0 +1,384 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use std::{collections::HashMap, future::Future, pin::Pin}; + +use async_trait::async_trait; +use thiserror::Error; +use tokio::sync::mpsc::Receiver; +use tower::discover::Change; + +use dubbo_base::{ + extension_param::ExtensionName, registry_param::RegistryUrl, url::UrlParam, StdError, Url, +}; +use proxy::RegistryProxy; + +use crate::extension::{ + ConvertToExtensionFactories, Extension, ExtensionFactories, ExtensionMetaInfo, ExtensionType, +}; + +// extension://0.0.0.0/?extension-type=registry&extension-name=nacos®istry-url=nacos://127.0.0.1:8848 +pub fn to_extension_url(registry_url: Url) -> Url { + let mut registry_extension_loader_url: Url = "extension://0.0.0.0".parse().unwrap(); + + let protocol = registry_url.protocol(); + + registry_extension_loader_url.add_query_param(ExtensionType::Registry); + registry_extension_loader_url.add_query_param(ExtensionName::new(protocol.to_string())); + registry_extension_loader_url.add_query_param(RegistryUrl::new(registry_url)); + + registry_extension_loader_url +} + +pub type ServiceChange = Change; +pub type DiscoverStream = Receiver>; + +#[async_trait] +pub trait Registry { + async fn register(&self, url: Url) -> Result<(), StdError>; + + async fn unregister(&self, url: Url) -> Result<(), StdError>; + + async fn subscribe(&self, url: Url) -> Result; + + async fn unsubscribe(&self, url: Url) -> Result<(), StdError>; + + fn url(&self) -> &Url; +} + +impl crate::extension::Sealed for T where T: Registry + Send + 'static {} + +impl ExtensionMetaInfo for T +where + T: Registry + Send + 'static, + T: Extension>, +{ + fn extension_type() -> ExtensionType { + ExtensionType::Registry + } +} + +impl ConvertToExtensionFactories for T +where + T: Registry + Send + 'static, + T: Extension>, +{ + fn convert_to_extension_factories() -> ExtensionFactories { + fn constrain(f: F) -> F + where + F: for<'a> Fn( + &'a Url, + ) -> Pin< + Box< + dyn Future, StdError>> + + Send + + 'a, + >, + >, + { + f + } + + let constructor = constrain(|url: &Url| { + let f = ::create(url); + Box::pin(f) + }); + + ExtensionFactories::RegistryExtensionFactory(RegistryExtensionFactory::new(constructor)) + } +} + +#[derive(Default)] +pub(super) struct RegistryExtensionLoader { + factories: HashMap, +} + +impl RegistryExtensionLoader { + pub(crate) async fn register( + &mut self, + extension_name: String, + factory: RegistryExtensionFactory, + ) { + self.factories.insert(extension_name, factory); + } + + pub(crate) async fn remove(&mut self, extension_name: String) { + self.factories.remove(&extension_name); + } + + pub(crate) async fn load(&mut self, url: &Url) -> Result { + let extension_name = url.query::().unwrap(); + let extension_name = extension_name.value(); + let factory = self.factories.get_mut(&extension_name).ok_or_else(|| { + RegistryExtensionLoaderError::new(format!( + "registry extension loader error: extension name {} not found", + extension_name + )) + })?; + factory.create(url).await + } +} + +type RegistryConstructor = for<'a> fn( + &'a Url, +) -> Pin< + Box, StdError>> + Send + 'a>, +>; + +pub(crate) struct RegistryExtensionFactory { + constructor: RegistryConstructor, + instances: HashMap, +} + +impl RegistryExtensionFactory { + pub(super) fn new(constructor: RegistryConstructor) -> Self { + Self { + constructor, + instances: HashMap::new(), + } + } +} + +impl RegistryExtensionFactory { + pub(super) async fn create(&mut self, url: &Url) -> Result { + let registry_url = url.query::().unwrap(); + let registry_url = registry_url.value(); + let url_str = registry_url.as_str().to_string(); + match self.instances.get(&url_str) { + Some(proxy) => { + let proxy = proxy.clone(); + Ok(proxy) + } + None => { + let registry = (self.constructor)(url).await?; + let proxy = >>::from(registry); + self.instances.insert(url_str, proxy.clone()); + Ok(proxy) + } + } + } +} + +#[derive(Error, Debug)] +#[error("{0}")] +pub(crate) struct RegistryExtensionLoaderError(String); + +impl RegistryExtensionLoaderError { + pub(crate) fn new(msg: String) -> Self { + RegistryExtensionLoaderError(msg) + } +} + +pub mod proxy { + use async_trait::async_trait; + use thiserror::Error; + use tokio::sync::oneshot; + + use dubbo_base::{StdError, Url}; + use dubbo_logger::tracing::error; + + use crate::extension::registry_extension::{DiscoverStream, Registry}; + + pub(super) enum RegistryOpt { + Register(Url, oneshot::Sender>), + Unregister(Url, oneshot::Sender>), + Subscribe(Url, oneshot::Sender>), + UnSubscribe(Url, oneshot::Sender>), + } + + #[derive(Clone)] + pub struct RegistryProxy { + sender: tokio::sync::mpsc::Sender, + url: Url, + } + + #[async_trait] + impl Registry for RegistryProxy { + async fn register(&self, url: Url) -> Result<(), StdError> { + let (tx, rx) = oneshot::channel(); + + match self + .sender + .send(RegistryOpt::Register(url.clone(), tx)) + .await + { + Ok(_) => match rx.await { + Ok(result) => result, + Err(_) => { + error!( + "registry proxy error: receive register response failed, url: {}", + url + ); + return Err( + RegistryProxyError::new("receive register response failed").into() + ); + } + }, + Err(_) => { + error!( + "registry proxy error: send register request failed, url: {}", + url + ); + return Err(RegistryProxyError::new("send register opt failed").into()); + } + } + } + + async fn unregister(&self, url: Url) -> Result<(), StdError> { + let (tx, rx) = oneshot::channel(); + match self + .sender + .send(RegistryOpt::Unregister(url.clone(), tx)) + .await + { + Ok(_) => match rx.await { + Ok(result) => result, + Err(_) => { + error!( + "registry proxy error: receive unregister response failed, url: {}", + url + ); + return Err( + RegistryProxyError::new("receive unregister response failed").into(), + ); + } + }, + Err(_) => { + error!( + "registry proxy error: send unregister request failed, url: {}", + url + ); + return Err(RegistryProxyError::new("send unregister opt failed").into()); + } + } + } + + async fn subscribe(&self, url: Url) -> Result { + let (tx, rx) = oneshot::channel(); + + match self + .sender + .send(RegistryOpt::Subscribe(url.clone(), tx)) + .await + { + Ok(_) => match rx.await { + Ok(result) => result, + Err(_) => { + error!( + "registry proxy error: receive subscribe response failed, url: {}", + url + ); + return Err( + RegistryProxyError::new("receive subscribe response failed").into() + ); + } + }, + Err(_) => { + error!( + "registry proxy error: send subscribe request failed, url: {}", + url + ); + return Err(RegistryProxyError::new("send subscribe opt failed").into()); + } + } + } + + async fn unsubscribe(&self, url: Url) -> Result<(), StdError> { + let (tx, rx) = oneshot::channel(); + match self + .sender + .send(RegistryOpt::UnSubscribe(url.clone(), tx)) + .await + { + Ok(_) => { + match rx.await { + Ok(result) => result, + Err(_) => { + error!("registry proxy error: receive unsubscribe response failed, url: {}", url); + return Err(RegistryProxyError::new( + "receive unsubscribe response failed", + ) + .into()); + } + } + } + Err(_) => { + error!( + "registry proxy error: send unsubscribe request failed, url: {}", + url + ); + return Err(RegistryProxyError::new("send unsubscribe opt failed").into()); + } + } + } + + fn url(&self) -> &Url { + &self.url + } + } + + impl From> for RegistryProxy { + fn from(registry: Box) -> Self { + let url = registry.url().clone(); + + let (sender, mut receiver) = tokio::sync::mpsc::channel(1024); + + tokio::spawn(async move { + while let Some(opt) = receiver.recv().await { + match opt { + RegistryOpt::Register(url, tx) => { + let register = registry.register(url).await; + if let Err(_) = tx.send(register) { + error!("registry proxy error: send register response failed"); + } + } + RegistryOpt::Unregister(url, tx) => { + let unregister = registry.unregister(url).await; + if let Err(_) = tx.send(unregister) { + error!("registry proxy error: send unregister response failed"); + } + } + RegistryOpt::Subscribe(url, tx) => { + let subscribe = registry.subscribe(url).await; + if let Err(_) = tx.send(subscribe) { + error!("registry proxy error: send subscribe response failed"); + } + } + RegistryOpt::UnSubscribe(url, tx) => { + let unsubscribe = registry.unsubscribe(url).await; + if let Err(_) = tx.send(unsubscribe) { + error!("registry proxy error: send unsubscribe response failed"); + } + } + } + } + }); + + RegistryProxy { sender, url } + } + } + + #[derive(Error, Debug)] + #[error("registry proxy error: {0}")] + pub(crate) struct RegistryProxyError(String); + + impl RegistryProxyError { + pub(crate) fn new(msg: &str) -> Self { + RegistryProxyError(msg.to_string()) + } + } +} diff --git a/dubbo/src/framework.rs b/dubbo/src/framework.rs index 2666d259..f3c6dc1a 100644 --- a/dubbo/src/framework.rs +++ b/dubbo/src/framework.rs @@ -15,20 +15,13 @@ * limitations under the License. */ -use std::{ - collections::HashMap, - error::Error, - pin::Pin, - sync::{Arc, Mutex}, -}; +use std::{collections::HashMap, error::Error, pin::Pin}; use crate::{ + extension, + extension::registry_extension::Registry, protocol::{BoxExporter, Protocol}, - registry::{ - n_registry::{ArcRegistry, Registry}, - protocol::RegistryProtocol, - types::{Registries, RegistriesOperation}, - }, + registry::protocol::RegistryProtocol, }; use dubbo_base::Url; use dubbo_config::{get_global_config, protocol::ProtocolRetrieve, RootConfig}; @@ -40,7 +33,7 @@ use futures::{future, Future}; #[derive(Default)] pub struct Dubbo { protocols: HashMap>, - registries: Option, + registries: Vec, service_registry: HashMap>, // registry: Urls config: Option<&'static RootConfig>, } @@ -49,7 +42,7 @@ impl Dubbo { pub fn new() -> Dubbo { Self { protocols: HashMap::new(), - registries: None, + registries: Vec::default(), service_registry: HashMap::new(), config: None, } @@ -60,14 +53,10 @@ impl Dubbo { self } - pub fn add_registry(mut self, registry_key: &str, registry: ArcRegistry) -> Self { - if self.registries.is_none() { - self.registries = Some(Arc::new(Mutex::new(HashMap::new()))); - } - self.registries - .as_ref() - .unwrap() - .insert(registry_key.to_string(), registry); + pub fn add_registry(mut self, registry: &str) -> Self { + let url: Url = registry.parse().unwrap(); + let url = extension::registry_extension::to_extension_url(url); + self.registries.push(url); self } @@ -88,10 +77,15 @@ impl Dubbo { let protocol = root_config .protocols .get_protocol_or_default(service_config.protocol.as_str()); - let protocol_url = - format!("{}/{}", protocol.to_url(), service_config.interface.clone(),); + let interface_name = service_config.interface.clone(); + let protocol_url = format!( + "{}/{}?interface={}", + protocol.to_url(), + interface_name, + interface_name + ); tracing::info!("protocol_url: {:?}", protocol_url); - Url::from_url(&protocol_url) + protocol_url.parse().ok() } else { return Err(format!("base {:?} not exists", service_config.protocol).into()); }; @@ -117,9 +111,20 @@ impl Dubbo { self.init().unwrap(); tracing::info!("starting..."); // TODO: server registry + + let mut registry_extensions = Vec::new(); + + for registry_url in &self.registries { + let registry_url = registry_url.clone(); + let registry_extension = extension::EXTENSIONS.load_registry(registry_url).await; + if let Ok(registry_extension) = registry_extension { + registry_extensions.push(registry_extension); + } + } + let mem_reg = Box::new( RegistryProtocol::new() - .with_registries(self.registries.as_ref().unwrap().clone()) + .with_registries(registry_extensions.clone()) .with_services(self.service_registry.clone()), ); let mut async_vec: Vec + Send>>> = Vec::new(); @@ -128,15 +133,10 @@ impl Dubbo { tracing::info!("base: {:?}, service url: {:?}", name, url); let exporter = mem_reg.clone().export(url.to_owned()); async_vec.push(exporter); + //TODO multiple registry - if self.registries.is_some() { - let _ = self - .registries - .as_ref() - .unwrap() - .default_registry() - .register(url.clone()) - .await; + for registry_extension in ®istry_extensions { + let _ = registry_extension.register(url.clone()).await; } } } diff --git a/dubbo/src/invoker/clone_body.rs b/dubbo/src/invoker/clone_body.rs index 4de8f899..913910a5 100644 --- a/dubbo/src/invoker/clone_body.rs +++ b/dubbo/src/invoker/clone_body.rs @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ use std::{ collections::VecDeque, pin::Pin, @@ -8,13 +24,12 @@ use std::{ use bytes::{Buf, BufMut, Bytes, BytesMut}; use futures_core::ready; +use dubbo_base::StdError; use http::HeaderMap; use http_body::Body; use pin_project::pin_project; use thiserror::Error; -use crate::StdError; - #[derive(Error, Debug)] #[error("buffered body reach max capacity.")] pub struct ReachMaxCapacityError; diff --git a/dubbo/src/invoker/clone_invoker.rs b/dubbo/src/invoker/clone_invoker.rs index c1fa00d8..557d76e0 100644 --- a/dubbo/src/invoker/clone_invoker.rs +++ b/dubbo/src/invoker/clone_invoker.rs @@ -1,5 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ use std::{mem, pin::Pin, task::Poll}; +use dubbo_base::StdError; use dubbo_logger::tracing::debug; use futures_core::{future::BoxFuture, ready, Future, TryFuture}; use futures_util::FutureExt; @@ -16,8 +33,6 @@ use tokio_util::sync::ReusableBoxFuture; use tower::{buffer::Buffer, ServiceExt}; use tower_service::Service; -use crate::StdError; - use super::clone_body::CloneBody; enum Inner { diff --git a/dubbo/src/invoker/mod.rs b/dubbo/src/invoker/mod.rs index 92b8b462..1c87c0ec 100644 --- a/dubbo/src/invoker/mod.rs +++ b/dubbo/src/invoker/mod.rs @@ -1,5 +1,19 @@ -use dubbo_base::Url; - +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ use crate::{codegen::TripleInvoker, invoker::clone_invoker::CloneInvoker, svc::NewService}; pub mod clone_body; @@ -13,7 +27,7 @@ impl NewService for NewInvoker { fn new_service(&self, url: String) -> Self::Service { // todo create another invoker by url protocol - let url = Url::from_url(&url).unwrap(); + let url = url.parse().unwrap(); CloneInvoker::new(TripleInvoker::new(url)) } } diff --git a/dubbo/src/lib.rs b/dubbo/src/lib.rs index d397b42b..1a521a2e 100644 --- a/dubbo/src/lib.rs +++ b/dubbo/src/lib.rs @@ -19,6 +19,7 @@ pub mod cluster; pub mod codegen; pub mod context; pub mod directory; +pub mod extension; pub mod filter; mod framework; pub mod invocation; @@ -38,7 +39,6 @@ use std::{future::Future, pin::Pin}; pub use framework::Dubbo; -pub type StdError = Box; pub type BoxFuture = self::Pin> + Send + 'static>>; pub(crate) type Error = Box; pub type BoxBody = http_body::combinators::UnsyncBoxBody; diff --git a/dubbo/src/loadbalancer/mod.rs b/dubbo/src/loadbalancer/mod.rs index 4e26781d..74f22174 100644 --- a/dubbo/src/loadbalancer/mod.rs +++ b/dubbo/src/loadbalancer/mod.rs @@ -1,3 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +use dubbo_base::StdError; use futures_core::future::BoxFuture; use tower::{discover::ServiceList, ServiceExt}; use tower_service::Service; @@ -7,7 +24,6 @@ use crate::{ invoker::{clone_body::CloneBody, clone_invoker::CloneInvoker}, param::Param, svc::NewService, - StdError, }; use crate::protocol::triple::triple_invoker::TripleInvoker; diff --git a/dubbo/src/param.rs b/dubbo/src/param.rs index bef50419..298c3b31 100644 --- a/dubbo/src/param.rs +++ b/dubbo/src/param.rs @@ -1,3 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + pub trait Param { fn param(&self) -> T; } diff --git a/dubbo/src/protocol/triple/triple_invoker.rs b/dubbo/src/protocol/triple/triple_invoker.rs index c8451e3c..cb6b08cc 100644 --- a/dubbo/src/protocol/triple/triple_invoker.rs +++ b/dubbo/src/protocol/triple/triple_invoker.rs @@ -35,7 +35,7 @@ pub struct TripleInvoker { impl TripleInvoker { pub fn new(url: Url) -> TripleInvoker { - let uri = http::Uri::from_str(&url.to_url()).unwrap(); + let uri = http::Uri::from_str(url.as_str()).unwrap(); Self { url, conn: Connection::new().with_host(uri).build(), @@ -55,7 +55,7 @@ impl TripleInvoker { let path_and_query = parts.headers.get("path").unwrap().to_str().unwrap(); - let authority = self.url.clone().get_ip_port(); + let authority = self.url.authority(); let uri = Uri::builder() .scheme("http") diff --git a/dubbo/src/protocol/triple/triple_protocol.rs b/dubbo/src/protocol/triple/triple_protocol.rs index 71f6edc4..27174ba0 100644 --- a/dubbo/src/protocol/triple/triple_protocol.rs +++ b/dubbo/src/protocol/triple/triple_protocol.rs @@ -15,10 +15,10 @@ * limitations under the License. */ -use std::{boxed::Box, collections::HashMap}; +use std::collections::HashMap; use async_trait::async_trait; -use dubbo_base::Url; +use dubbo_base::{registry_param::InterfaceName, url::UrlParam, Url}; use super::{ triple_exporter::TripleExporter, triple_invoker::TripleInvoker, triple_server::TripleServer, @@ -44,8 +44,9 @@ impl TripleProtocol { } pub fn get_server(&self, url: Url) -> Option { + let interface_name = url.query::().unwrap(); self.servers - .get(&url.service_key) + .get(interface_name.value().as_str()) .map(|data| data.to_owned()) } } @@ -61,8 +62,12 @@ impl Protocol for TripleProtocol { async fn export(mut self, url: Url) -> BoxExporter { // service_key is same to key of TRIPLE_SERVICES let server = TripleServer::new(); - self.servers.insert(url.service_key.clone(), server.clone()); - server.serve(url.short_url().as_str().into()).await; + + let interface_name = url.query::().unwrap(); + let interface_name = interface_name.value(); + + self.servers.insert(interface_name, server.clone()); + server.serve(url).await; Box::new(TripleExporter::new()) } diff --git a/dubbo/src/registry/mod.rs b/dubbo/src/registry/mod.rs index b82fda8d..08ae175b 100644 --- a/dubbo/src/registry/mod.rs +++ b/dubbo/src/registry/mod.rs @@ -16,47 +16,43 @@ */ #![allow(unused_variables, dead_code, missing_docs)] + +use crate::{extension, extension::registry_extension::proxy::RegistryProxy}; +use dubbo_base::{StdError, Url}; +use std::{ + future::Future, + pin::Pin, + task::{Context, Poll}, +}; +use tower_service::Service; + pub mod integration; -pub mod n_registry; pub mod protocol; -pub mod types; - -// use std::{ -// fmt::{Debug, Formatter}, -// sync::Arc, -// }; - -// use dubbo_base::Url; - -// pub type RegistryNotifyListener = Arc; -// pub trait Registry { -// fn register(&mut self, url: Url) -> Result<(), crate::StdError>; -// fn unregister(&mut self, url: Url) -> Result<(), crate::StdError>; - -// fn subscribe(&self, url: Url, listener: RegistryNotifyListener) -> Result<(), crate::StdError>; -// fn unsubscribe( -// &self, -// url: Url, -// listener: RegistryNotifyListener, -// ) -> Result<(), crate::StdError>; -// } - -// pub trait NotifyListener { -// fn notify(&self, event: ServiceEvent); -// fn notify_all(&self, event: ServiceEvent); -// } - -// #[derive(Debug)] -// pub struct ServiceEvent { -// pub key: String, -// pub action: String, -// pub service: Vec, -// } - -// pub type BoxRegistry = Box; - -// impl Debug for BoxRegistry { -// fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { -// f.write_str("BoxRegistry") -// } -// } +pub mod registry; + +#[derive(Clone)] +pub struct MkRegistryService { + registry_url: Url, +} + +impl MkRegistryService { + pub fn new(registry_url: Url) -> Self { + Self { registry_url } + } +} + +impl Service<()> for MkRegistryService { + type Response = RegistryProxy; + type Error = StdError; + type Future = + Pin> + Send + 'static>>; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, req: ()) -> Self::Future { + let fut = extension::EXTENSIONS.load_registry(self.registry_url.clone()); + Box::pin(fut) + } +} diff --git a/dubbo/src/registry/n_registry.rs b/dubbo/src/registry/n_registry.rs deleted file mode 100644 index abcd56b8..00000000 --- a/dubbo/src/registry/n_registry.rs +++ /dev/null @@ -1,203 +0,0 @@ -use std::{ - collections::{HashMap, HashSet}, - sync::Arc, -}; - -use async_trait::async_trait; -use dubbo_base::Url; -use thiserror::Error; -use tokio::sync::{ - mpsc::{self, Receiver}, - Mutex, -}; -use tower::discover::Change; - -use crate::StdError; - -pub type ServiceChange = Change; -pub type DiscoverStream = Receiver>; -pub type BoxRegistry = Box; - -#[async_trait] -pub trait Registry { - async fn register(&self, url: Url) -> Result<(), StdError>; - - async fn unregister(&self, url: Url) -> Result<(), StdError>; - - async fn subscribe(&self, url: Url) -> Result; - - async fn unsubscribe(&self, url: Url) -> Result<(), StdError>; -} - -#[derive(Clone)] -pub struct ArcRegistry { - inner: Arc, -} - -pub enum RegistryComponent { - NacosRegistry(ArcRegistry), - ZookeeperRegistry, - StaticRegistry(StaticRegistry), -} - -pub struct StaticServiceValues { - listeners: Vec>>, - urls: HashSet, -} - -#[derive(Default)] -pub struct StaticRegistry { - urls: Mutex>, -} - -impl ArcRegistry { - pub fn new(registry: impl Registry + Send + Sync + 'static) -> Self { - Self { - inner: Arc::new(registry), - } - } -} - -#[async_trait] -impl Registry for ArcRegistry { - async fn register(&self, url: Url) -> Result<(), StdError> { - self.inner.register(url).await - } - - async fn unregister(&self, url: Url) -> Result<(), StdError> { - self.inner.unregister(url).await - } - - async fn subscribe(&self, url: Url) -> Result { - self.inner.subscribe(url).await - } - - async fn unsubscribe(&self, url: Url) -> Result<(), StdError> { - self.inner.unsubscribe(url).await - } -} - -#[async_trait] -impl Registry for RegistryComponent { - async fn register(&self, url: Url) -> Result<(), StdError> { - todo!() - } - - async fn unregister(&self, url: Url) -> Result<(), StdError> { - todo!() - } - - async fn subscribe(&self, url: Url) -> Result { - match self { - RegistryComponent::NacosRegistry(registry) => registry.subscribe(url).await, - RegistryComponent::ZookeeperRegistry => todo!(), - RegistryComponent::StaticRegistry(registry) => registry.subscribe(url).await, - } - } - - async fn unsubscribe(&self, url: Url) -> Result<(), StdError> { - todo!() - } -} - -impl StaticRegistry { - pub fn new(urls: Vec) -> Self { - let mut map = HashMap::with_capacity(urls.len()); - - for url in urls { - let service_name = url.get_service_name(); - let static_values = map - .entry(service_name) - .or_insert_with(|| StaticServiceValues { - listeners: Vec::new(), - urls: HashSet::new(), - }); - let url = url.to_string(); - static_values.urls.insert(url.clone()); - } - - Self { - urls: Mutex::new(map), - } - } -} - -#[async_trait] -impl Registry for StaticRegistry { - async fn register(&self, url: Url) -> Result<(), StdError> { - let service_name = url.get_service_name(); - let mut lock = self.urls.lock().await; - - let static_values = lock - .entry(service_name) - .or_insert_with(|| StaticServiceValues { - listeners: Vec::new(), - urls: HashSet::new(), - }); - let url = url.to_string(); - static_values.urls.insert(url.clone()); - - static_values.listeners.retain(|listener| { - let ret = listener.try_send(Ok(ServiceChange::Insert(url.clone(), ()))); - ret.is_ok() - }); - - Ok(()) - } - - async fn unregister(&self, url: Url) -> Result<(), StdError> { - let service_name = url.get_service_name(); - let mut lock = self.urls.lock().await; - - match lock.get_mut(&service_name) { - None => Ok(()), - Some(static_values) => { - let url = url.to_string(); - static_values.urls.remove(&url); - static_values.listeners.retain(|listener| { - let ret = listener.try_send(Ok(ServiceChange::Remove(url.clone()))); - ret.is_ok() - }); - if static_values.urls.is_empty() { - lock.remove(&service_name); - } - Ok(()) - } - } - } - - async fn subscribe(&self, url: Url) -> Result { - let service_name = url.get_service_name(); - - let change_rx = { - let mut lock = self.urls.lock().await; - let static_values = lock - .entry(service_name) - .or_insert_with(|| StaticServiceValues { - listeners: Vec::new(), - urls: HashSet::new(), - }); - - let (tx, change_rx) = mpsc::channel(64); - static_values.listeners.push(tx); - - for url in static_values.urls.iter() { - static_values.listeners.retain(|listener| { - let ret = listener.try_send(Ok(ServiceChange::Insert(url.clone(), ()))); - ret.is_ok() - }); - } - change_rx - }; - - Ok(change_rx) - } - - async fn unsubscribe(&self, url: Url) -> Result<(), StdError> { - Ok(()) - } -} - -#[derive(Error, Debug)] -#[error("static registry error: {0}")] -struct StaticRegistryError(String); diff --git a/dubbo/src/registry/protocol.rs b/dubbo/src/registry/protocol.rs index b9ba7221..76350a42 100644 --- a/dubbo/src/registry/protocol.rs +++ b/dubbo/src/registry/protocol.rs @@ -15,26 +15,25 @@ * limitations under the License. */ -use dubbo_base::Url; +use dubbo_base::{registry_param::InterfaceName, url::UrlParam, Url}; use dubbo_logger::tracing; use std::{ collections::HashMap, sync::{Arc, RwLock}, }; -use super::n_registry::{ArcRegistry, Registry, StaticRegistry}; use crate::{ + extension::registry_extension::{proxy::RegistryProxy, Registry}, protocol::{ triple::{triple_exporter::TripleExporter, triple_protocol::TripleProtocol}, BoxExporter, BoxInvoker, Protocol, }, - registry::types::Registries, }; #[derive(Clone, Default)] pub struct RegistryProtocol { // registerAddr: Registry - registries: Option, + registries: Vec, // providerUrl: Exporter exporters: Arc>>, // serviceName: registryUrls @@ -44,14 +43,14 @@ pub struct RegistryProtocol { impl RegistryProtocol { pub fn new() -> Self { RegistryProtocol { - registries: None, + registries: Vec::default(), exporters: Arc::new(RwLock::new(HashMap::new())), services: HashMap::new(), } } - pub fn with_registries(mut self, registries: Registries) -> Self { - self.registries = Some(registries); + pub fn with_registries(mut self, registries: Vec) -> Self { + self.registries.extend(registries); self } @@ -59,19 +58,6 @@ impl RegistryProtocol { self.services.extend(services); self } - - pub fn get_registry(&mut self, url: Url) -> ArcRegistry { - let mem = StaticRegistry::default(); - let mem = ArcRegistry::new(mem); - self.registries - .as_ref() - .unwrap() - .lock() - .unwrap() - .insert(url.location, mem.clone()); - - mem - } } #[async_trait::async_trait] @@ -88,23 +74,23 @@ impl Protocol for RegistryProtocol { // init Exporter based on provider_url // server registry based on register_url // start server health check - let registry_url = self.services.get(url.get_service_name().as_str()); + let service_name = url.query::().unwrap(); + let registry_url = self.services.get(service_name.as_str().as_ref()); if let Some(urls) = registry_url { - for url in urls.clone().iter() { - if !url.service_key.is_empty() { - let reg = self.get_registry(url.clone()); - let _ = reg.register(url.clone()).await; + for url in urls.iter() { + for registry_proxy in &self.registries { + let _ = registry_proxy.register(url.clone()).await; } } } - match url.clone().scheme.as_str() { + match url.clone().protocol() { "tri" => { let pro = Box::new(TripleProtocol::new()); return pro.export(url).await; } _ => { - tracing::error!("base {:?} not implemented", url.scheme); + tracing::error!("base {:?} not implemented", url.protocol()); Box::new(TripleExporter::new()) } } diff --git a/dubbo/src/registry/registry.rs b/dubbo/src/registry/registry.rs new file mode 100644 index 00000000..85a81683 --- /dev/null +++ b/dubbo/src/registry/registry.rs @@ -0,0 +1,222 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +use std::collections::{HashMap, HashSet}; + +use async_trait::async_trait; +use itertools::Itertools; +use thiserror::Error; +use tokio::sync::{ + mpsc::{self}, + Mutex, +}; + +use dubbo_base::{ + extension_param::{ExtensionName, ExtensionType}, + registry_param::{InterfaceName, RegistryUrl, StaticInvokerUrls}, + url::UrlParam, + StdError, Url, +}; + +use crate::extension::{ + registry_extension::{DiscoverStream, Registry, ServiceChange}, + Extension, +}; + +pub struct StaticServiceValues { + listeners: Vec>>, + urls: HashSet, +} + +pub struct StaticRegistry { + urls: Mutex>, + self_url: Url, +} + +impl StaticRegistry { + pub fn to_extension_url(static_invoker_urls: Vec) -> Url { + let static_invoker_urls: StaticInvokerUrls = + static_invoker_urls.iter().join(",").parse().unwrap(); + let mut static_registry_extension_loader_url: Url = "extension://0.0.0.0".parse().unwrap(); + + static_registry_extension_loader_url.add_query_param(ExtensionType::Registry); + static_registry_extension_loader_url.add_query_param(ExtensionName::new(Self::name())); + static_registry_extension_loader_url + .add_query_param(RegistryUrl::new("static://127.0.0.1".parse().unwrap())); + static_registry_extension_loader_url.add_query_param(static_invoker_urls); + + static_registry_extension_loader_url + } +} + +impl StaticRegistry { + pub fn new(url: Url) -> Self { + let static_urls = url.query::(); + let static_urls = match static_urls { + None => Vec::default(), + Some(static_urls) => static_urls.value(), + }; + + let mut map = HashMap::with_capacity(static_urls.len()); + + for url in static_urls { + let interface_name = url.query::().unwrap(); + let interface_name = interface_name.value(); + + let static_values = map + .entry(interface_name) + .or_insert_with(|| StaticServiceValues { + listeners: Vec::new(), + urls: HashSet::new(), + }); + let url = url.to_string(); + static_values.urls.insert(url.clone()); + } + + let self_url = "static://0.0.0.0".parse().unwrap(); + + Self { + urls: Mutex::new(map), + self_url, + } + } +} + +impl Default for StaticRegistry { + fn default() -> Self { + let self_url = "static://0.0.0.0".parse().unwrap(); + + Self { + self_url, + urls: Mutex::new(HashMap::new()), + } + } +} +#[async_trait] +impl Registry for StaticRegistry { + async fn register(&self, url: Url) -> Result<(), StdError> { + let interface_name = url.query::().unwrap(); + let interface_name = interface_name.value(); + + let mut lock = self.urls.lock().await; + + let static_values = lock + .entry(interface_name) + .or_insert_with(|| StaticServiceValues { + listeners: Vec::new(), + urls: HashSet::new(), + }); + let url = url.to_string(); + static_values.urls.insert(url.clone()); + + static_values.listeners.retain(|listener| { + let ret = listener.try_send(Ok(ServiceChange::Insert(url.clone(), ()))); + ret.is_ok() + }); + + Ok(()) + } + + async fn unregister(&self, url: Url) -> Result<(), StdError> { + let interface_name = url.query::().unwrap(); + let interface_name = interface_name.value(); + + let mut lock = self.urls.lock().await; + + match lock.get_mut(&interface_name) { + None => Ok(()), + Some(static_values) => { + let url = url.to_string(); + static_values.urls.remove(&url); + static_values.listeners.retain(|listener| { + let ret = listener.try_send(Ok(ServiceChange::Remove(url.clone()))); + ret.is_ok() + }); + if static_values.urls.is_empty() { + lock.remove(&interface_name); + } + Ok(()) + } + } + } + + async fn subscribe(&self, url: Url) -> Result { + let interface_name = url.query::().unwrap(); + let interface_name = interface_name.value(); + + let change_rx = { + let mut lock = self.urls.lock().await; + let static_values = lock + .entry(interface_name) + .or_insert_with(|| StaticServiceValues { + listeners: Vec::new(), + urls: HashSet::new(), + }); + + let (tx, change_rx) = mpsc::channel(64); + static_values.listeners.push(tx); + + for listener in &static_values.listeners { + for url in &static_values.urls { + let _ = listener + .send(Ok(ServiceChange::Insert(url.clone(), ()))) + .await; + } + } + + change_rx + }; + + Ok(change_rx) + } + + async fn unsubscribe(&self, url: Url) -> Result<(), StdError> { + Ok(()) + } + + fn url(&self) -> &Url { + &self.self_url + } +} + +#[async_trait::async_trait] +impl Extension for StaticRegistry { + type Target = Box; + + fn name() -> String { + "static".to_string() + } + + async fn create(url: &Url) -> Result { + // url example: + // extension://0.0.0.0?extension-type=registry&extension-name=static®istry=static://127.0.0.1 + let static_invoker_urls = url.query::(); + + let registry_url = url.query::().unwrap(); + let mut registry_url = registry_url.value(); + + if let Some(static_invoker_urls) = static_invoker_urls { + registry_url.add_query_param(static_invoker_urls); + } + + let static_registry = StaticRegistry::new(registry_url); + + Ok(Box::new(static_registry)) + } +} +#[derive(Error, Debug)] +#[error("static registry error: {0}")] +struct StaticRegistryError(String); diff --git a/dubbo/src/registry/types.rs b/dubbo/src/registry/types.rs deleted file mode 100644 index 5c1687da..00000000 --- a/dubbo/src/registry/types.rs +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -use std::{ - collections::HashMap, - sync::{Arc, Mutex}, -}; - -use itertools::Itertools; - -use super::n_registry::ArcRegistry; - -pub type Registries = Arc>>; - -pub const DEFAULT_REGISTRY_KEY: &str = "default"; - -pub trait RegistriesOperation { - fn get(&self, registry_key: &str) -> ArcRegistry; - fn insert(&self, registry_key: String, registry: ArcRegistry); - fn default_registry(&self) -> ArcRegistry; -} - -impl RegistriesOperation for Registries { - fn get(&self, registry_key: &str) -> ArcRegistry { - self.as_ref() - .lock() - .unwrap() - .get(registry_key) - .unwrap() - .clone() - } - - fn insert(&self, registry_key: String, registry: ArcRegistry) { - self.as_ref().lock().unwrap().insert(registry_key, registry); - } - - fn default_registry(&self) -> ArcRegistry { - let guard = self.as_ref().lock().unwrap(); - let (_, result) = guard - .iter() - .find_or_first(|e| e.0 == DEFAULT_REGISTRY_KEY) - .unwrap() - .to_owned(); - result.clone() - } -} diff --git a/dubbo/src/route/mod.rs b/dubbo/src/route/mod.rs index c2448642..28dfda7c 100644 --- a/dubbo/src/route/mod.rs +++ b/dubbo/src/route/mod.rs @@ -1,5 +1,23 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + use std::pin::Pin; +use dubbo_base::StdError; use dubbo_logger::tracing::debug; use futures_core::{ready, Future}; use futures_util::{future::Ready, FutureExt, TryFutureExt}; @@ -11,7 +29,6 @@ use crate::{ invoker::clone_invoker::CloneInvoker, param::Param, svc::NewService, - StdError, }; pub struct NewRoutes { @@ -20,6 +37,7 @@ pub struct NewRoutes { pub struct NewRoutesFuture { inner: RoutesFutureInnerState, + #[allow(dead_code)] target: T, } @@ -39,6 +57,7 @@ pub enum RoutesFutureInnerState { #[derive(Clone)] pub struct Routes { + #[allow(dead_code)] target: T, invokers: Vec>, } diff --git a/dubbo/src/svc.rs b/dubbo/src/svc.rs index db59b92d..f8466636 100644 --- a/dubbo/src/svc.rs +++ b/dubbo/src/svc.rs @@ -1,4 +1,20 @@ -use std::{marker::PhantomData, sync::Arc}; +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +use std::sync::Arc; pub trait NewService { type Service; @@ -45,32 +61,3 @@ impl NewService for ArcNewService { self.inner.new_service(t) } } - -// inner: Box> + Send>>> + Send>, -pub struct BoxedService { - inner: N, - _mark: PhantomData, -} - -impl BoxedService { - pub fn layer() -> impl tower_layer::Layer { - tower_layer::layer_fn(|inner: N| Self { - inner, - _mark: PhantomData, - }) - } -} - -// impl NewService for BoxedService -// where -// N: NewService, -// N::Service: Service + Send, -// >::Future: Send + 'static, -// { -// type Service = Box>::Response, Error = >::Error, Future = Pin>::Response, >::Error>> + Send>>> + Send>; - -// fn new_service(&self, target: T) -> Self::Service { -// let service = self.inner.new_service(target); -// Box::new(service.map_future(|f|Box::pin(f) as _)) -// } -// } diff --git a/dubbo/src/triple/client/builder.rs b/dubbo/src/triple/client/builder.rs index 0dadbcc1..94c855b4 100644 --- a/dubbo/src/triple/client/builder.rs +++ b/dubbo/src/triple/client/builder.rs @@ -18,14 +18,11 @@ use std::sync::Arc; use crate::{ - cluster::NewCluster, - directory::NewCachedDirectory, - loadbalancer::NewLoadBalancer, - registry::n_registry::{ArcRegistry, RegistryComponent, StaticRegistry}, - route::NewRoutes, - utils::boxed_clone::BoxCloneService, + cluster::NewCluster, directory::NewCachedDirectory, extension, loadbalancer::NewLoadBalancer, + route::NewRoutes, utils::boxed_clone::BoxCloneService, }; +use crate::registry::{registry::StaticRegistry, MkRegistryService}; use aws_smithy_http::body::SdkBody; use dubbo_base::Url; use tower::ServiceBuilder; @@ -33,15 +30,15 @@ use tower::ServiceBuilder; pub type ClientBoxService = BoxCloneService, http::Response, crate::Error>; -pub type ServiceMK = Arc>>>>; +pub type ServiceMK = + Arc>>>>; #[derive(Default)] pub struct ClientBuilder { pub timeout: Option, pub connector: &'static str, - registry: Option, + registry_extension_url: Option, pub direct: bool, - host: String, } impl ClientBuilder { @@ -49,22 +46,18 @@ impl ClientBuilder { ClientBuilder { timeout: None, connector: "", - registry: None, + registry_extension_url: None, direct: false, - host: "".to_string(), } } pub fn from_static(host: &str) -> ClientBuilder { + let registry_extension_url = StaticRegistry::to_extension_url(vec![host.parse().unwrap()]); Self { timeout: None, connector: "", - registry: Some(ArcRegistry::new(StaticRegistry::new(vec![Url::from_url( - host, - ) - .unwrap()]))), + registry_extension_url: Some(registry_extension_url), direct: true, - host: host.to_string(), } } @@ -75,19 +68,19 @@ impl ClientBuilder { } } - pub fn with_registry(self, registry: ArcRegistry) -> Self { + pub fn with_registry(self, registry: Url) -> Self { + let registry_extension_url = extension::registry_extension::to_extension_url(registry); Self { - registry: Some(registry), + registry_extension_url: Some(registry_extension_url), ..self } } pub fn with_host(self, host: &'static str) -> Self { + let registry_extension_url = StaticRegistry::to_extension_url(vec![host.parse().unwrap()]); + Self { - registry: Some(ArcRegistry::new(StaticRegistry::new(vec![Url::from_url( - host, - ) - .unwrap()]))), + registry_extension_url: Some(registry_extension_url), ..self } } @@ -101,14 +94,17 @@ impl ClientBuilder { } pub fn build(mut self) -> ServiceMK { - let registry = self.registry.take().expect("registry must not be empty"); + let registry = self + .registry_extension_url + .take() + .expect("registry must not be empty"); let mk_service = ServiceBuilder::new() .layer(NewCluster::layer()) .layer(NewLoadBalancer::layer()) .layer(NewRoutes::layer()) .layer(NewCachedDirectory::layer()) - .service(registry); + .service(MkRegistryService::new(registry)); Arc::new(mk_service) } diff --git a/dubbo/src/triple/server/builder.rs b/dubbo/src/triple/server/builder.rs index 9ef71529..b473dd89 100644 --- a/dubbo/src/triple/server/builder.rs +++ b/dubbo/src/triple/server/builder.rs @@ -21,7 +21,7 @@ use std::{ str::FromStr, }; -use dubbo_base::Url; +use dubbo_base::{registry_param::InterfaceName, url::UrlParam, Url}; use dubbo_logger::tracing; use http::{Request, Response, Uri}; use hyper::body::Body; @@ -132,7 +132,7 @@ impl ServerBuilder { impl From for ServerBuilder { fn from(u: Url) -> Self { - let uri = match http::Uri::from_str(&u.raw_url_string()) { + let uri = match http::Uri::from_str(&u.as_str()) { Ok(v) => v, Err(err) => { tracing::error!("http uri parse error: {}, url: {:?}", err, &u); @@ -142,10 +142,14 @@ impl From for ServerBuilder { let authority = uri.authority().unwrap(); + let service_name = u.query::().unwrap().value(); + Self { - listener: u.get_param("listener").unwrap_or("tcp".to_string()), + listener: u + .query_param_by_key("listener") + .unwrap_or("tcp".to_string()), addr: authority.to_string().to_socket_addrs().unwrap().next(), - service_names: vec![u.service_name], + service_names: vec![service_name], server: DubboServer::default(), certs: Vec::new(), keys: Vec::new(), diff --git a/dubbo/src/triple/transport/connection.rs b/dubbo/src/triple/transport/connection.rs index cb0b9d71..212c8807 100644 --- a/dubbo/src/triple/transport/connection.rs +++ b/dubbo/src/triple/transport/connection.rs @@ -15,12 +15,11 @@ * limitations under the License. */ +use dubbo_base::StdError; use hyper::client::{conn::Builder, service::Connect}; use tower_service::Service; -use crate::{ - boxed, invoker::clone_body::CloneBody, triple::transport::connector::get_connector, StdError, -}; +use crate::{boxed, invoker::clone_body::CloneBody, triple::transport::connector::get_connector}; type HyperConnect = Connect< crate::utils::boxed_clone::BoxCloneService, diff --git a/examples/echo/src/echo/client.rs b/examples/echo/src/echo/client.rs index 0a2f150b..18939a29 100644 --- a/examples/echo/src/echo/client.rs +++ b/examples/echo/src/echo/client.rs @@ -34,9 +34,10 @@ async fn main() { // let builder = ClientBuilder::new() // .with_connector("unix") // .with_host("unix://127.0.0.1:8888"); - let builder = ClientBuilder::from_static(&"http://127.0.0.1:8888") - .with_timeout(1000000) - .with_direct(true); + let builder = + ClientBuilder::from_static(&"http://127.0.0.1:8888?interface=grpc.examples.echo.Echo") + .with_timeout(1000000) + .with_direct(true); let mut cli = EchoClient::new(builder); // let mut unary_cli = cli.clone().with_filter(FakeFilter {}); // let mut cli = EchoClient::build(ClientBuilder::from_static("http://127.0.0.1:8888")); diff --git a/examples/echo/src/echo/server.rs b/examples/echo/src/echo/server.rs index 90efc1a6..010821e2 100644 --- a/examples/echo/src/echo/server.rs +++ b/examples/echo/src/echo/server.rs @@ -70,7 +70,8 @@ async fn main() { // Dubbo::new() // .with_config({ // let mut r = RootConfig::new(); - // r.test_config(); + // r.test_config + // (); // r // }) // .start() diff --git a/examples/greeter/src/greeter/client.rs b/examples/greeter/src/greeter/client.rs index cb92ed0b..14743ae3 100644 --- a/examples/greeter/src/greeter/client.rs +++ b/examples/greeter/src/greeter/client.rs @@ -20,11 +20,9 @@ pub mod protos { include!(concat!(env!("OUT_DIR"), "/org.apache.dubbo.sample.tri.rs")); } -use std::env; +use dubbo::codegen::*; -use dubbo::{codegen::*, registry::n_registry::ArcRegistry}; - -use dubbo_base::Url; +use dubbo::extension; use futures_util::StreamExt; use protos::{greeter_client::GreeterClient, GreeterRequest}; use registry_nacos::NacosRegistry; @@ -33,9 +31,9 @@ use registry_nacos::NacosRegistry; async fn main() { dubbo_logger::init(); - let builder = ClientBuilder::new().with_registry(ArcRegistry::new(NacosRegistry::new( - Url::from_url("nacos://127.0.0.1:8848").unwrap(), - ))); + let _ = extension::EXTENSIONS.register::().await; + + let builder = ClientBuilder::new().with_registry("nacos://127.0.0.1:8848".parse().unwrap()); let mut cli = GreeterClient::new(builder); diff --git a/examples/greeter/src/greeter/server.rs b/examples/greeter/src/greeter/server.rs index fd436e52..a652ed84 100644 --- a/examples/greeter/src/greeter/server.rs +++ b/examples/greeter/src/greeter/server.rs @@ -18,13 +18,12 @@ use std::{io::ErrorKind, pin::Pin}; use async_trait::async_trait; -use dubbo_base::Url; use futures_util::{Stream, StreamExt}; use registry_nacos::NacosRegistry; use tokio::sync::mpsc; use tokio_stream::wrappers::ReceiverStream; -use dubbo::{codegen::*, registry::n_registry::ArcRegistry, Dubbo}; +use dubbo::{codegen::*, extension, Dubbo}; use dubbo_config::RootConfig; use dubbo_logger::{ tracing::{info, span}, @@ -34,8 +33,6 @@ use protos::{ greeter_server::{register_server, Greeter}, GreeterReply, GreeterRequest, }; -use registry_zookeeper::ZookeeperRegistry; - pub mod protos { #![allow(non_camel_case_types)] include!(concat!(env!("OUT_DIR"), "/org.apache.dubbo.sample.tri.rs")); @@ -59,10 +56,10 @@ async fn main() { Err(_err) => panic!("err: {:?}", _err), // response was droped }; - let nacos_registry = NacosRegistry::new(Url::from_url("nacos://127.0.0.1:8848").unwrap()); + let _ = extension::EXTENSIONS.register::().await; let mut f = Dubbo::new() .with_config(r) - .add_registry("nacos-registry", ArcRegistry::new(nacos_registry)); + .add_registry("nacos://127.0.0.1:8848/"); f.start().await; } diff --git a/protocol/base/src/invoker.rs b/protocol/base/src/invoker.rs index 14de6ce8..d676fa49 100644 --- a/protocol/base/src/invoker.rs +++ b/protocol/base/src/invoker.rs @@ -66,9 +66,9 @@ impl Node for BaseInvoker { impl Display for BaseInvoker { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.debug_struct("Invoker") - .field("protocol", &self.url.scheme) - .field("host", &self.url.ip) - .field("path", &self.url.location) + .field("protocol", &self.url.protocol()) + .field("host", &self.url.host()) + .field("path", &self.url.path()) .finish() } } diff --git a/registry/nacos/src/lib.rs b/registry/nacos/src/lib.rs index ad34237e..204846b2 100644 --- a/registry/nacos/src/lib.rs +++ b/registry/nacos/src/lib.rs @@ -14,84 +14,47 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -mod utils; use async_trait::async_trait; -use dubbo_base::Url; +use dubbo_base::{StdError, Url}; use std::{collections::HashMap, sync::Arc}; -use tokio::{select, sync::mpsc}; +use tokio::sync::mpsc; -use anyhow::anyhow; -use dubbo::{ - registry::n_registry::{DiscoverStream, Registry, ServiceChange}, - StdError, +use dubbo::extension::{ + registry_extension::{DiscoverStream, Registry, ServiceChange}, + Extension, }; -use dubbo_logger::tracing::{debug, error, info}; -use nacos_sdk::api::naming::{ - NamingEventListener, NamingService, NamingServiceBuilder, ServiceInstance, +use dubbo_base::{ + registry_param::{ + AppName, Category, Group, InterfaceName, RegistryUrl, ServiceNamespace, Version, + }, + url::UrlParam, }; - -use crate::utils::{build_nacos_client_props, is_concrete_str, is_wildcard_str, match_range}; - -const VERSION_KEY: &str = "version"; - -const GROUP_KEY: &str = "group"; - -const DEFAULT_GROUP: &str = "DEFAULT_GROUP"; - -const PROVIDER_SIDE: &str = "provider"; - -const DEFAULT_CATEGORY: &str = PROVIDERS_CATEGORY; - -const SIDE_KEY: &str = "side"; - -const REGISTER_CONSUMER_URL_KEY: &str = "register-consumer-url"; - -const SERVICE_NAME_SEPARATOR: &str = ":"; - -const CATEGORY_KEY: &str = "category"; - -const PROVIDERS_CATEGORY: &str = "providers"; - -#[allow(dead_code)] -const ADMIN_PROTOCOL: &str = "admin"; - -#[allow(dead_code)] -const INNERCLASS_SYMBOL: &str = "$"; - -#[allow(dead_code)] -const INNERCLASS_COMPATIBLE_SYMBOL: &str = "___"; +use dubbo_logger::tracing::info; +use nacos_sdk::api::{ + naming::{NamingEventListener, NamingService, NamingServiceBuilder, ServiceInstance}, + props::ClientProps, +}; +use tokio::sync::{watch, Notify}; pub struct NacosRegistry { - nacos_naming_service: Arc, + url: Url, + nacos_service: Arc, } impl NacosRegistry { - pub fn new(url: Url) -> Self { - let (nacos_client_props, enable_auth) = build_nacos_client_props(&url); - - let mut nacos_naming_builder = NamingServiceBuilder::new(nacos_client_props); - - if enable_auth { - nacos_naming_builder = nacos_naming_builder.enable_auth_plugin_http(); - } - - let nacos_naming_service = nacos_naming_builder.build().unwrap(); - - Self { - nacos_naming_service: Arc::new(nacos_naming_service), - } + pub fn new(url: Url, nacos_service: Arc) -> Self { + Self { url, nacos_service } } -} -impl NacosRegistry { - fn create_nacos_service_instance(url: Url) -> ServiceInstance { - let ip = url.ip; - let port = url.port; - nacos_sdk::api::naming::ServiceInstance { - ip, - port: port.parse().unwrap(), - metadata: url.params, + fn create_nacos_service_instance(url: &Url) -> ServiceInstance { + let ip = url.host().unwrap(); + let port = url.port().unwrap(); + + ServiceInstance { + ip: ip.to_string(), + port: port.into(), + metadata: url.all_query_params(), ..Default::default() } } @@ -144,447 +107,312 @@ impl NacosRegistry { #[async_trait] impl Registry for NacosRegistry { - async fn register(&self, url: Url) -> Result<(), dubbo::StdError> { - // let side = url.get_param(SIDE_KEY).unwrap_or_default(); - // let register_consumer = url - // .get_param(REGISTER_CONSUMER_URL_KEY) - // .unwrap_or_else(|| false.to_string()) - // .parse::() - // .unwrap_or(false); - // if side.ne(PROVIDER_SIDE) && !register_consumer { - // warn!("Please set 'dubbo.registry.parameters.register-consumer-url=true' to turn on consumer url registration."); - // return Ok(()); - // } - - let nacos_service_name = NacosServiceName::new(&url); - - let group_name = Some( - nacos_service_name - .get_group_with_default(DEFAULT_GROUP) - .to_string(), - ); - let nacos_service_name = nacos_service_name.to_register_str(); - - let nacos_service_instance = Self::create_nacos_service_instance(url); - - info!("register service: {}", nacos_service_name); - let ret = self - .nacos_naming_service - .register_instance(nacos_service_name, group_name, nacos_service_instance) - .await; - if let Err(e) = ret { - error!("register to nacos occur an error: {:?}", e); - return Err(anyhow!("register to nacos occur an error: {:?}", e).into()); - } + async fn register(&self, url: Url) -> Result<(), StdError> { + let service_name = NacosServiceName::new(&url); + + let group_name = service_name.group(); + + let registry_service_name_str = service_name.value(); + + let service_instance = Self::create_nacos_service_instance(&url); + + self.nacos_service + .register_instance( + registry_service_name_str.to_owned(), + Some(group_name.to_owned()), + service_instance, + ) + .await?; Ok(()) } - async fn unregister(&self, url: Url) -> Result<(), dubbo::StdError> { - let nacos_service_name = NacosServiceName::new(&url); + async fn unregister(&self, url: Url) -> Result<(), StdError> { + let service_name = NacosServiceName::new(&url); - let group_name = Some( - nacos_service_name - .get_group_with_default(DEFAULT_GROUP) - .to_string(), - ); - let nacos_service_name = nacos_service_name.to_register_str(); + let group_name = service_name.group(); - let nacos_service_instance = Self::create_nacos_service_instance(url); + let registry_service_name_str = service_name.value(); - info!("deregister service: {}", nacos_service_name); + let service_instance = Self::create_nacos_service_instance(&url); + + self.nacos_service + .deregister_instance( + registry_service_name_str.to_owned(), + Some(group_name.to_owned()), + service_instance, + ) + .await?; - let ret = self - .nacos_naming_service - .deregister_instance(nacos_service_name, group_name, nacos_service_instance) - .await; - if let Err(e) = ret { - error!("deregister service from nacos occur an error: {:?}", e); - return Err(anyhow!("deregister service from nacos occur an error: {:?}", e).into()); - } Ok(()) } async fn subscribe(&self, url: Url) -> Result { let service_name = NacosServiceName::new(&url); - let service_group = service_name - .get_group_with_default(DEFAULT_GROUP) - .to_string(); - let subscriber_url = service_name.to_subscriber_str(); - info!("subscribe: {}", subscriber_url); - let (listener, mut change_receiver) = ServiceChangeListener::new(); - let arc_listener = Arc::new(listener); + let group_name = service_name.group().to_owned(); + + let registry_service_name_str = service_name.value().to_owned(); + + let all_instance = self + .nacos_service + .get_all_instances( + registry_service_name_str.to_owned(), + Some(group_name.to_owned()), + Vec::default(), + false, + ) + .await?; - let (discover_tx, discover_rx) = mpsc::channel(64); + let (tx, rx) = mpsc::channel(1024); - let nacos_naming_service = self.nacos_naming_service.clone(); + let (event_listener, mut listener_change_rx, closed) = NacosNamingEventListener::new(); + let event_listener = Arc::new(event_listener); - let listener_in_task = arc_listener.clone(); - let service_group_in_task = service_group.clone(); - let subscriber_url_in_task = subscriber_url.clone(); + let nacos_service_cloned = self.nacos_service.clone(); + let event_listener_cloned = event_listener.clone(); + let registry_service_name_str_clone = registry_service_name_str.clone(); + let group_name_clone = group_name.clone(); tokio::spawn(async move { - let listener = listener_in_task; - let service_group = service_group_in_task; - let subscriber_url = subscriber_url_in_task; + let mut current_instances = all_instance; + for instance in ¤t_instances { + let url = instance_to_url(instance).as_str().to_owned(); + let _ = tx.send(Ok(ServiceChange::Insert(url, ()))).await; + } - let mut current_instances = Vec::new(); loop { - let change = select! { - _ = discover_tx.closed() => { - debug!("service {} change task quit, unsubscribe.", subscriber_url); - None + let change = tokio::select! { + _ = closed.notified() => { + break; }, - change = change_receiver.recv() => change + change = listener_change_rx.changed() => change }; - match change { - Some(instances) => { - debug!("service {} changed", subscriber_url); - let (remove_instances, add_instances) = - NacosRegistry::diff(¤t_instances, &instances); - - for instance in remove_instances { - let service_name = instance.service_name.as_ref(); - let url = match service_name { - None => { - format!("triple://{}:{}", instance.ip(), instance.port()) - } - Some(service_name) => { - format!( - "triple://{}:{}/{}", - instance.ip(), - instance.port(), - service_name - ) - } - }; - - match discover_tx.send(Ok(ServiceChange::Remove(url))).await { - Ok(_) => {} - Err(e) => { - error!( - "send service change failed: {:?}, maybe user unsubscribe", - e - ); - break; - } - } - } - - for instance in add_instances { - let service_name = instance.service_name.as_ref(); - let url = match service_name { - None => { - format!("triple://{}:{}", instance.ip(), instance.port()) - } - Some(service_name) => { - format!( - "triple://{}:{}/{}", - instance.ip(), - instance.port(), - service_name - ) - } - }; - - match discover_tx.send(Ok(ServiceChange::Insert(url, ()))).await { - Ok(_) => {} - Err(e) => { - error!( - "send service change failed: {:?}, maybe user unsubscribe", - e - ); - break; - } - } - } - current_instances = instances; - } - None => { - error!( - "receive service change task quit, unsubscribe {}.", - subscriber_url - ); - break; - } + if change.is_err() { + break; } - } - debug!("unsubscribe service: {}", subscriber_url); - // unsubscribe - let unsubscribe = nacos_naming_service - .unsubscribe(subscriber_url, Some(service_group), Vec::new(), listener) - .await; + let change = listener_change_rx.borrow_and_update().clone(); + if change.is_empty() { + continue; + } + let (remove_instances, add_instances) = Self::diff(¤t_instances, &change); + + for remove_instance in remove_instances { + let url = instance_to_url(remove_instance).as_str().to_owned(); + let Ok(_) = tx.send(Ok(ServiceChange::Remove(url))).await else { + break; + }; + } - match unsubscribe { - Ok(_) => {} - Err(e) => { - error!("unsubscribe service failed: {:?}", e); + for add_instance in add_instances { + let url = instance_to_url(add_instance).as_str().to_owned(); + let Ok(_) = tx.send(Ok(ServiceChange::Insert(url, ()))).await else { + break; + }; } + + current_instances = change; } - }); - let all_instance = self - .nacos_naming_service - .get_all_instances( - subscriber_url.clone(), - Some(service_group.clone()), - Vec::new(), - false, - ) - .await?; - let _ = arc_listener.changed(all_instance); + info!("unsubscribe"); + let _ = nacos_service_cloned + .unsubscribe( + registry_service_name_str_clone, + Some(group_name_clone), + Vec::default(), + event_listener_cloned, + ) + .await; + }); - match self - .nacos_naming_service + let _ = self + .nacos_service .subscribe( - subscriber_url.clone(), - Some(service_group.clone()), - Vec::new(), - arc_listener, + registry_service_name_str, + Some(group_name), + Vec::default(), + event_listener, ) - .await - { - Ok(_) => {} - Err(e) => { - error!("subscribe service failed: {:?}", e); - return Err(anyhow!("subscribe service failed: {:?}", e).into()); - } - } + .await?; - Ok(discover_rx) + Ok(rx) } - async fn unsubscribe(&self, url: Url) -> Result<(), dubbo::StdError> { - let service_name = NacosServiceName::new(&url); - let subscriber_url = service_name.to_subscriber_str(); - info!("unsubscribe: {}", &subscriber_url); - + async fn unsubscribe(&self, _: Url) -> Result<(), StdError> { Ok(()) } + + fn url(&self) -> &Url { + &self.url + } } -struct NacosServiceName { - category: String, +#[async_trait] +impl Extension for NacosRegistry { + type Target = Box; - service_interface: String, + fn name() -> String { + "nacos".to_string() + } - version: String, + async fn create(url: &Url) -> Result { + // url example: + // extension://0.0.0.0?extension-type=registry&extension-name=nacos®istry=nacos://127.0.0.1:8848 + let registry_url = url.query::().unwrap(); + let registry_url = registry_url.value(); - group: String, -} + let host = registry_url.host().unwrap(); + let port = registry_url.port().unwrap_or(8848); -impl NacosServiceName { - fn new(url: &Url) -> NacosServiceName { - let service_interface = url.get_service_name(); + let nacos_server_addr = format!("{}:{}", host, port); - let category = url.get_param(CATEGORY_KEY).unwrap_or_default(); + let namespace = registry_url.query::().unwrap_or_default(); + let namespace = namespace.value(); - let version = url.get_param(VERSION_KEY).unwrap_or_default(); + let app_name = registry_url.query::().unwrap_or_default(); + let app_name = app_name.value(); - let group = url.get_param(GROUP_KEY).unwrap_or_default(); + let user_name = registry_url.username(); + let password = registry_url.password().unwrap_or_default(); - Self { - category, - service_interface: service_interface.clone(), - version, - group, + let nacos_client_props = ClientProps::new() + .server_addr(nacos_server_addr) + .namespace(namespace) + .app_name(app_name) + .auth_username(user_name) + .auth_password(password); + + let mut nacos_naming_builder = NamingServiceBuilder::new(nacos_client_props); + + if !user_name.is_empty() { + nacos_naming_builder = nacos_naming_builder.enable_auth_plugin_http(); } - } - #[allow(dead_code)] - fn from_service_name_str(service_name_str: &str) -> Self { - let mut splitter = service_name_str.split(SERVICE_NAME_SEPARATOR); + let nacos_naming_service = nacos_naming_builder.build().unwrap(); - let category = splitter.next().unwrap_or_default().to_string(); - let service_interface = splitter.next().unwrap_or_default().to_string(); - let version = splitter.next().unwrap_or_default().to_string(); - let group = splitter.next().unwrap_or_default().to_string(); + let nacos_registry = NacosRegistry::new(registry_url, Arc::new(nacos_naming_service)); - Self { - category, - service_interface, - version, - group, - } + Ok(Box::new(nacos_registry)) } +} - #[allow(dead_code)] - fn version(&self) -> &str { - &self.version - } +fn instance_to_url(instance: &ServiceInstance) -> Url { + let mut url = Url::empty(); + url.set_protocol("provider"); + url.set_host(instance.ip()); + url.set_port(instance.port().try_into().unwrap_or_default()); + url.extend_pairs( + instance + .metadata() + .iter() + .map(|(k, v)| (k.clone(), v.clone())), + ); - #[allow(dead_code)] - fn get_version_with_default<'a>(&'a self, default: &'a str) -> &str { - if self.version.is_empty() { - default - } else { - &self.version - } - } + url +} - #[allow(dead_code)] - fn group(&self) -> &str { - &self.group +struct NacosNamingEventListener { + tx: watch::Sender>, + closed: Arc, +} + +impl NacosNamingEventListener { + fn new() -> (Self, watch::Receiver>, Arc) { + let (tx, rx) = watch::channel(Vec::new()); + + let closed = Arc::new(Notify::new()); + let this = Self { + tx, + closed: closed.clone(), + }; + (this, rx, closed) } +} - fn get_group_with_default<'a>(&'a self, default: &'a str) -> &str { - if self.group.is_empty() { - default - } else { - &self.group +impl NamingEventListener for NacosNamingEventListener { + fn event(&self, event: Arc) { + match event.instances { + Some(ref instances) => { + let instances = instances.clone(); + let send = self.tx.send(instances); + match send { + Ok(_) => {} + Err(_) => { + self.closed.notify_waiters(); + } + } + } + None => {} } } +} +struct NacosServiceName { #[allow(dead_code)] - fn category(&self) -> &str { - &self.category - } + category: String, #[allow(dead_code)] - fn get_category_with_default<'a>(&'a self, default: &'a str) -> &str { - if self.category.is_empty() { - default - } else { - &self.category - } - } + interface: String, #[allow(dead_code)] - fn service_interface(&self) -> &str { - &self.service_interface - } + version: String, #[allow(dead_code)] - fn get_service_interface_with_default<'a>(&'a self, default: &'a str) -> &str { - if self.service_interface.is_empty() { - default - } else { - &self.service_interface - } - } + group: String, - fn to_register_str(&self) -> String { - let category = if self.category.is_empty() { - DEFAULT_CATEGORY - } else { - &self.category - }; - format!( - "{}:{}:{}:{}", - category, self.service_interface, self.version, self.group - ) - } + #[allow(dead_code)] + value: String, +} - fn to_subscriber_str(&self) -> String { - let category = if is_concrete_str(&self.service_interface) { - DEFAULT_CATEGORY - } else { - &self.category - }; +impl NacosServiceName { + fn new(url: &Url) -> Self { + let interface = url.query::().unwrap(); + let interface = interface.value(); - format!( - "{}:{}:{}:{}", - category, self.service_interface, self.version, self.group - ) - } + let category = url.query::().unwrap_or_default(); + let category = category.value(); - #[allow(dead_code)] - fn to_subscriber_legacy_string(&self) -> String { - let mut legacy_string = DEFAULT_CATEGORY.to_owned(); - if !self.service_interface.is_empty() { - legacy_string.push_str(SERVICE_NAME_SEPARATOR); - legacy_string.push_str(&self.service_interface); - } + let version = url.query::().unwrap_or_default(); + let version = version.value(); - if !self.version.is_empty() { - legacy_string.push_str(SERVICE_NAME_SEPARATOR); - legacy_string.push_str(&self.version); - } + let group = url.query::().unwrap_or_default(); + let group = group.value(); - if !self.group.is_empty() { - legacy_string.push_str(SERVICE_NAME_SEPARATOR); - legacy_string.push_str(&self.group); - } + let value = format!("{}:{}:{}:{}", category, interface, version, group); - legacy_string + Self { + category, + interface, + version, + group, + value, + } } #[allow(dead_code)] - fn is_concrete(&self) -> bool { - is_concrete_str(&self.service_interface) - && is_concrete_str(&self.version) - && is_concrete_str(&self.group) + fn category(&self) -> &str { + &self.category } #[allow(dead_code)] - fn is_compatible(&self, other: &NacosServiceName) -> bool { - if !other.is_concrete() { - return false; - } - - if !self.category.eq(&other.category) && !match_range(&self.category, &other.category) { - return false; - } - - if is_wildcard_str(&self.version) { - return true; - } - - if is_wildcard_str(&self.group) { - return true; - } - - if !&self.version.eq(&other.version) && !match_range(&self.version, &other.version) { - return false; - } - - if !self.group.eq(&other.group) && !match_range(&self.group, &other.group) { - return false; - } - - true + fn interface(&self) -> &str { + &self.interface } -} - -struct ServiceChangeListener { - tx: mpsc::Sender>, -} - -impl ServiceChangeListener { - pub fn new() -> (Self, mpsc::Receiver>) { - let (tx, rx) = mpsc::channel(64); - let this = Self { tx }; - (this, rx) + #[allow(dead_code)] + fn version(&self) -> &str { + &self.version } - pub fn changed(&self, instances: Vec) -> Result<(), dubbo::StdError> { - match self.tx.try_send(instances) { - Ok(_) => Ok(()), - Err(e) => { - error!("send service change failed: {:?}", e); - Err(anyhow!("send service change failed: {:?}", e).into()) - } - } + #[allow(dead_code)] + fn group(&self) -> &str { + &self.group } -} - -impl NamingEventListener for ServiceChangeListener { - fn event(&self, event: Arc) { - debug!("service change {}", event.service_name.clone()); - debug!("nacos event: {:?}", event); - let instances = event.instances.as_ref(); - match instances { - None => { - let _ = self.changed(Vec::default()); - } - Some(instances) => { - let _ = self.changed(instances.clone()); - } - } + #[allow(dead_code)] + fn value(&self) -> &str { + &self.value } } @@ -593,7 +421,9 @@ pub mod tests { use core::time; use std::thread; + use tracing::error; + use dubbo_base::{extension_param::ExtensionName, registry_param::Side}; use tracing::metadata::LevelFilter; use super::*; @@ -610,13 +440,17 @@ pub mod tests { .with_max_level(LevelFilter::DEBUG) .init(); - let nacos_registry_url = Url::from_url("nacos://127.0.0.1:8848/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-triple-api-provider&dubbo=2.0.2&interface=org.apache.dubbo.registry.RegistryService&pid=7015").unwrap(); - let registry = NacosRegistry::new(nacos_registry_url); + let mut extension_url: Url = "extension://0.0.0.0?extension-type=registry" + .parse() + .unwrap(); + extension_url.add_query_param(ExtensionName::new("nacos".to_string())); + extension_url.add_query_param(RegistryUrl::new("nacos://127.0.0.1:8848/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-triple-api-provider&dubbo=2.0.2&interface=org.apache.dubbo.registry.RegistryService&pid=7015".parse().unwrap())); + + let registry = NacosRegistry::create(&extension_url).await.unwrap(); - let mut service_url = Url::from_url("tri://127.0.0.1:50052/org.apache.dubbo.demo.GreeterService?anyhost=true&application=dubbo-demo-triple-api-provider&background=false&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.GreeterService&methods=sayHello,sayHelloAsync&pid=7015&service-name-mapping=true&side=provider×tamp=1670060843807").unwrap(); - service_url - .params - .insert(SIDE_KEY.to_owned(), PROVIDER_SIDE.to_owned()); + let mut service_url: Url = "tri://127.0.0.1:50052/org.apache.dubbo.demo.GreeterService?anyhost=true&application=dubbo-demo-triple-api-provider&background=false&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.GreeterService&methods=sayHello,sayHelloAsync&pid=7015&service-name-mapping=true&side=provider×tamp=1670060843807".parse().unwrap(); + + service_url.add_query_param(Side::Provider); let ret = registry.register(service_url).await; @@ -638,13 +472,17 @@ pub mod tests { .with_max_level(LevelFilter::DEBUG) .init(); - let nacos_registry_url = Url::from_url("nacos://127.0.0.1:8848/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-triple-api-provider&dubbo=2.0.2&interface=org.apache.dubbo.registry.RegistryService&pid=7015").unwrap(); - let registry = NacosRegistry::new(nacos_registry_url); + let mut extension_url: Url = "extension://0.0.0.0?extension-type=registry" + .parse() + .unwrap(); + extension_url.add_query_param(ExtensionName::new("nacos".to_string())); + extension_url.add_query_param(RegistryUrl::new("nacos://127.0.0.1:8848/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-triple-api-provider&dubbo=2.0.2&interface=org.apache.dubbo.registry.RegistryService&pid=7015".parse().unwrap())); + + let registry = NacosRegistry::create(&extension_url).await.unwrap(); + + let mut service_url: Url = "tri://127.0.0.1:50052/org.apache.dubbo.demo.GreeterService?anyhost=true&application=dubbo-demo-triple-api-provider&background=false&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.GreeterService&methods=sayHello,sayHelloAsync&pid=7015&service-name-mapping=true&side=provider×tamp=1670060843807".parse().unwrap(); - let mut service_url = Url::from_url("tri://127.0.0.1:9090/org.apache.dubbo.demo.GreeterService?anyhost=true&application=dubbo-demo-triple-api-provider&background=false&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.GreeterService&methods=sayHello,sayHelloAsync&pid=7015&service-name-mapping=true&side=provider×tamp=1670060843807").unwrap(); - service_url - .params - .insert(SIDE_KEY.to_owned(), PROVIDER_SIDE.to_owned()); + service_url.add_query_param(Side::Provider); let ret = registry.register(service_url).await; @@ -653,7 +491,7 @@ pub mod tests { let sleep_millis = time::Duration::from_secs(10); thread::sleep(sleep_millis); - let unregister_url = Url::from_url("tri://127.0.0.1:9090/org.apache.dubbo.demo.GreeterService?anyhost=true&application=dubbo-demo-triple-api-provider&background=false&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.GreeterService&methods=sayHello,sayHelloAsync&pid=7015&service-name-mapping=true&side=provider×tamp=1670060843807").unwrap(); + let unregister_url = "tri://127.0.0.1:9090/org.apache.dubbo.demo.GreeterService?anyhost=true&application=dubbo-demo-triple-api-provider&background=false&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.GreeterService&methods=sayHello,sayHelloAsync&pid=7015&service-name-mapping=true&side=provider×tamp=1670060843807".parse().unwrap(); let ret = registry.unregister(unregister_url).await; info!("deregister result: {:?}", ret); @@ -674,19 +512,23 @@ pub mod tests { .with_max_level(LevelFilter::DEBUG) .init(); - let nacos_registry_url = Url::from_url("nacos://127.0.0.1:8848/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-triple-api-provider&dubbo=2.0.2&interface=org.apache.dubbo.registry.RegistryService&pid=7015").unwrap(); - let registry = NacosRegistry::new(nacos_registry_url); + let mut extension_url: Url = "extension://0.0.0.0?extension-type=registry" + .parse() + .unwrap(); + extension_url.add_query_param(ExtensionName::new("nacos".to_string())); + extension_url.add_query_param(RegistryUrl::new("nacos://127.0.0.1:8848/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-triple-api-provider&dubbo=2.0.2&interface=org.apache.dubbo.registry.RegistryService&pid=7015".parse().unwrap())); - let mut service_url = Url::from_url("tri://127.0.0.1:50052/org.apache.dubbo.demo.GreeterService?anyhost=true&application=dubbo-demo-triple-api-provider&background=false&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.GreeterService&methods=sayHello,sayHelloAsync&pid=7015&service-name-mapping=true&side=provider×tamp=1670060843807").unwrap(); - service_url - .params - .insert(SIDE_KEY.to_owned(), PROVIDER_SIDE.to_owned()); + let registry = NacosRegistry::create(&extension_url).await.unwrap(); + + let mut service_url: Url = "tri://127.0.0.1:50052/org.apache.dubbo.demo.GreeterService?anyhost=true&application=dubbo-demo-triple-api-provider&background=false&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.GreeterService&methods=sayHello,sayHelloAsync&pid=7015&service-name-mapping=true&side=provider×tamp=1670060843807".parse().unwrap(); + + service_url.add_query_param(Side::Provider); let ret = registry.register(service_url).await; info!("register result: {:?}", ret); - let subscribe_url = Url::from_url("consumer://192.168.0.102:50052/org.apache.dubbo.demo.GreeterService?anyhost=true&application=dubbo-demo-triple-api-provider&background=false&bind.ip=192.168.0.102&bind.port=50052&category=configurators&check=false&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.GreeterService&ipv6=fd00:6cb1:58a2:8ddf:0:0:0:1000&methods=sayHello,sayHelloAsync&pid=44270&service-name-mapping=true&side=provider").unwrap(); + let subscribe_url = "consumer://192.168.0.102:50052/org.apache.dubbo.demo.GreeterService?anyhost=true&application=dubbo-demo-triple-api-provider&background=false&bind.ip=192.168.0.102&bind.port=50052&category=configurators&check=false&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.GreeterService&ipv6=fd00:6cb1:58a2:8ddf:0:0:0:1000&methods=sayHello,sayHelloAsync&pid=44270&service-name-mapping=true&side=provider".parse().unwrap(); let subscribe_ret = registry.subscribe(subscribe_url).await; if let Err(e) = subscribe_ret { @@ -714,19 +556,23 @@ pub mod tests { .with_max_level(LevelFilter::DEBUG) .init(); - let nacos_registry_url = Url::from_url("nacos://127.0.0.1:8848/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-triple-api-provider&dubbo=2.0.2&interface=org.apache.dubbo.registry.RegistryService&pid=7015").unwrap(); - let registry = NacosRegistry::new(nacos_registry_url); + let mut extension_url: Url = "extension://0.0.0.0?extension-type=registry" + .parse() + .unwrap(); + extension_url.add_query_param(ExtensionName::new("nacos".to_string())); + extension_url.add_query_param(RegistryUrl::new("nacos://127.0.0.1:8848/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-triple-api-provider&dubbo=2.0.2&interface=org.apache.dubbo.registry.RegistryService&pid=7015".parse().unwrap())); + + let registry = NacosRegistry::create(&extension_url).await.unwrap(); - let mut service_url = Url::from_url("tri://127.0.0.1:50052/org.apache.dubbo.demo.GreeterService?anyhost=true&application=dubbo-demo-triple-api-provider&background=false&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.GreeterService&methods=sayHello,sayHelloAsync&pid=7015&service-name-mapping=true&side=provider×tamp=1670060843807").unwrap(); - service_url - .params - .insert(SIDE_KEY.to_owned(), PROVIDER_SIDE.to_owned()); + let mut service_url: Url = "tri://127.0.0.1:50052/org.apache.dubbo.demo.GreeterService?anyhost=true&application=dubbo-demo-triple-api-provider&background=false&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.GreeterService&methods=sayHello,sayHelloAsync&pid=7015&service-name-mapping=true&side=provider×tamp=1670060843807".parse().unwrap(); + + service_url.add_query_param(Side::Provider); let ret = registry.register(service_url).await; info!("register result: {:?}", ret); - let subscribe_url = Url::from_url("provider://192.168.0.102:50052/org.apache.dubbo.demo.GreeterService?anyhost=true&application=dubbo-demo-triple-api-provider&background=false&bind.ip=192.168.0.102&bind.port=50052&category=configurators&check=false&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.GreeterService&ipv6=fd00:6cb1:58a2:8ddf:0:0:0:1000&methods=sayHello,sayHelloAsync&pid=44270&service-name-mapping=true&side=provider").unwrap(); + let subscribe_url = "provider://192.168.0.102:50052/org.apache.dubbo.demo.GreeterService?anyhost=true&application=dubbo-demo-triple-api-provider&background=false&bind.ip=192.168.0.102&bind.port=50052&category=configurators&check=false&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.GreeterService&ipv6=fd00:6cb1:58a2:8ddf:0:0:0:1000&methods=sayHello,sayHelloAsync&pid=44270&service-name-mapping=true&side=provider".parse().unwrap(); let ret = registry.subscribe(subscribe_url).await; @@ -742,13 +588,7 @@ pub mod tests { let sleep_millis = time::Duration::from_secs(40); thread::sleep(sleep_millis); - let unsubscribe_url = Url::from_url("provider://192.168.0.102:50052/org.apache.dubbo.demo.GreeterService?anyhost=true&application=dubbo-demo-triple-api-provider&background=false&bind.ip=192.168.0.102&bind.port=50052&category=configurators&check=false&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.GreeterService&ipv6=fd00:6cb1:58a2:8ddf:0:0:0:1000&methods=sayHello,sayHelloAsync&pid=44270&service-name-mapping=true&side=provider").unwrap(); - let ret = registry.unsubscribe(unsubscribe_url).await; - - if let Err(e) = ret { - error!("error message: {:?}", e); - return; - } + drop(rx); let sleep_millis = time::Duration::from_secs(40); thread::sleep(sleep_millis); diff --git a/registry/nacos/src/utils/mod.rs b/registry/nacos/src/utils/mod.rs deleted file mode 100644 index b247f60d..00000000 --- a/registry/nacos/src/utils/mod.rs +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -use dubbo_base::Url; -use nacos_sdk::api::props::ClientProps; - -const APP_NAME_KEY: &str = "AppName"; - -const UNKNOWN_APP: &str = "UnknownApp"; - -const NAMESPACE_KEY: &str = "namespace"; - -const DEFAULT_NAMESPACE: &str = "public"; - -const USERNAME_KEY: &str = "username"; - -const PASSWORD_KEY: &str = "password"; - -const BACKUP_KEY: &str = "backup"; - -const WILDCARD: &str = "*"; - -const RANGE_STR_SEPARATOR: &str = ","; - -pub(crate) fn build_nacos_client_props(url: &Url) -> (nacos_sdk::api::props::ClientProps, bool) { - let host = &url.ip; - let port = &url.port; - let backup = url - .get_param(BACKUP_KEY) - .map(|mut data| { - data.insert(0, ','); - data - }) - .unwrap_or_default(); - let server_addr = format!("{}:{}{}", host, port, backup); - - let namespace = url - .get_param(NAMESPACE_KEY) - .unwrap_or_else(|| DEFAULT_NAMESPACE.to_string()); - let app_name = url - .get_param(APP_NAME_KEY) - .unwrap_or_else(|| UNKNOWN_APP.to_string()); - let username = url.get_param(USERNAME_KEY).unwrap_or_default(); - let password = url.get_param(PASSWORD_KEY).unwrap_or_default(); - - let enable_auth = !password.is_empty() && !username.is_empty(); - - // todo ext parameters - - let mut client_props = ClientProps::new(); - - client_props = client_props - .server_addr(server_addr) - .namespace(namespace) - .app_name(app_name) - .auth_username(username) - .auth_password(password); - - (client_props, enable_auth) -} - -pub(crate) fn is_wildcard_str(str: &str) -> bool { - str.eq(WILDCARD) -} - -pub(crate) fn is_range_str(str: &str) -> bool { - let ret = str.split(RANGE_STR_SEPARATOR); - let count = ret.count(); - count > 1 -} - -pub(crate) fn is_concrete_str(str: &str) -> bool { - !is_wildcard_str(str) && !is_range_str(str) -} - -pub(crate) fn match_range(range: &str, value: &str) -> bool { - if range.is_empty() { - return true; - } - - if !is_range_str(range) { - return false; - } - - range - .split(RANGE_STR_SEPARATOR) - .any(|data| (*data).eq(value)) -} diff --git a/registry/zookeeper/src/lib.rs b/registry/zookeeper/src/lib.rs index f3733d50..dc44899d 100644 --- a/registry/zookeeper/src/lib.rs +++ b/registry/zookeeper/src/lib.rs @@ -22,17 +22,15 @@ use std::{collections::HashMap, env, sync::Arc, time::Duration}; use async_trait::async_trait; use dubbo_base::{ constants::{DUBBO_KEY, LOCALHOST_IP, PROVIDERS_KEY}, - Url, + StdError, Url, }; use dubbo_logger::tracing::{debug, error, info}; use serde::{Deserialize, Serialize}; use tokio::{select, sync::mpsc}; use zookeeper::{Acl, CreateMode, WatchedEvent, WatchedEventType, Watcher, ZooKeeper}; -use dubbo::{ - registry::n_registry::{DiscoverStream, Registry, ServiceChange}, - StdError, -}; +use dubbo::extension::registry_extension::{DiscoverStream, Registry, ServiceChange}; +use dubbo_base::{registry_param::InterfaceName, url::UrlParam}; // 从url中获取服务注册的元数据 // rawURL = fmt.Sprintf("%s://%s%s?%s", c.Protocol, host, c.Path, s) @@ -184,24 +182,24 @@ impl ZookeeperRegistry { ) -> (Vec, Vec) { let old_urls_map: HashMap = old_urls .iter() - .map(|url| dubbo_base::Url::from_url(url.as_str())) - .filter(|item| item.is_some()) + .map(|url| url.parse()) + .filter(|item| item.is_ok()) .map(|item| item.unwrap()) - .map(|item| { - let ip_port = item.get_ip_port(); - let url = item.encoded_raw_url_string(); + .map(|item: Url| { + let ip_port = item.authority().to_owned(); + let url = item.as_str().to_owned(); (ip_port, url) }) .collect(); let new_urls_map: HashMap = new_urls .iter() - .map(|url| dubbo_base::Url::from_url(url.as_str())) - .filter(|item| item.is_some()) + .map(|url| url.parse()) + .filter(|item| item.is_ok()) .map(|item| item.unwrap()) - .map(|item| { - let ip_port = item.get_ip_port(); - let url = item.encoded_raw_url_string(); + .map(|item: Url| { + let ip_port = item.authority().to_owned(); + let url = item.as_str().to_owned(); (ip_port, url) }) .collect(); @@ -263,24 +261,23 @@ impl Default for ZookeeperRegistry { impl Registry for ZookeeperRegistry { async fn register(&self, url: Url) -> Result<(), StdError> { debug!("register url: {}", url); + let interface_name = url.query::().unwrap().value(); + let url_str = url.as_str(); let zk_path = format!( "/{}/{}/{}/{}", - DUBBO_KEY, - url.service_name, - PROVIDERS_KEY, - url.encoded_raw_url_string() + DUBBO_KEY, interface_name, PROVIDERS_KEY, url_str ); self.create_path_with_parent_check(zk_path.as_str(), LOCALHOST_IP, CreateMode::Ephemeral)?; Ok(()) } async fn unregister(&self, url: Url) -> Result<(), StdError> { + let interface_name = url.query::().unwrap().value(); + let url_str = url.as_str(); + let zk_path = format!( "/{}/{}/{}/{}", - DUBBO_KEY, - url.service_name, - PROVIDERS_KEY, - url.encoded_raw_url_string() + DUBBO_KEY, interface_name, PROVIDERS_KEY, url_str ); self.delete_path(zk_path.as_str()); Ok(()) @@ -288,8 +285,9 @@ impl Registry for ZookeeperRegistry { // for consumer to find the changes of providers async fn subscribe(&self, url: Url) -> Result { - let service_name = url.get_service_name(); - let zk_path = format!("/{}/{}/{}", DUBBO_KEY, &service_name, PROVIDERS_KEY); + let interface_name = url.query::().unwrap().value(); + + let zk_path = format!("/{}/{}/{}", DUBBO_KEY, interface_name, PROVIDERS_KEY); debug!("subscribe service: {}", zk_path); @@ -302,12 +300,12 @@ impl Registry for ZookeeperRegistry { let zk_client_in_task = self.zk_client.clone(); let zk_path_in_task = zk_path.clone(); - let service_name_in_task = service_name.clone(); + let interface_name_in_task = interface_name.clone(); let arc_listener_in_task = arc_listener.clone(); tokio::spawn(async move { let zk_client = zk_client_in_task; let zk_path = zk_path_in_task; - let service_name = service_name_in_task; + let interface_name = interface_name_in_task; let listener = arc_listener_in_task; let mut current_urls = Vec::new(); @@ -383,12 +381,17 @@ impl Registry for ZookeeperRegistry { } async fn unsubscribe(&self, url: Url) -> Result<(), StdError> { - let service_name = url.get_service_name(); - let zk_path = format!("/{}/{}/{}", DUBBO_KEY, &service_name, PROVIDERS_KEY); + let interface_name = url.query::().unwrap().value(); + + let zk_path = format!("/{}/{}/{}", DUBBO_KEY, &interface_name, PROVIDERS_KEY); info!("unsubscribe service: {}", zk_path); Ok(()) } + + fn url(&self) -> &Url { + todo!() + } } pub struct ZooKeeperListener { diff --git a/remoting/base/src/exchange/client.rs b/remoting/base/src/exchange/client.rs index edbb4a63..71c42d4d 100644 --- a/remoting/base/src/exchange/client.rs +++ b/remoting/base/src/exchange/client.rs @@ -51,7 +51,7 @@ impl ExchangeClient { pub fn new(url: Url, client: BoxedClient, connection_timeout: Duration) -> Self { ExchangeClient { connection_timeout, - address: url.get_ip_port(), + address: url.authority().to_owned(), client: None, init: AtomicBool::new(false), active: AtomicI32::new(0), From c8cf3a19e8b3b598943370ad36eb10e25f5ac014 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=AF=9B=E6=96=87=E8=B6=85?= Date: Wed, 20 Mar 2024 19:45:49 +0800 Subject: [PATCH 43/48] Fix: get stuck when load extension in the concurrency environment (#184) * Fix: get stuck when load extension in the concurrency environment - Add a new struct called LoadExtensionPromise - Remove async modifier in ExtensionDirectory Close #183 * Ftr: use RwLock instead of unsafe * Rft: simplify the code of extension promise resolve --- dubbo/src/extension/mod.rs | 154 ++++++++++++++++++---- dubbo/src/extension/registry_extension.rs | 79 +++++------ dubbo/src/registry/registry.rs | 2 +- registry/nacos/src/lib.rs | 10 +- 4 files changed, 174 insertions(+), 71 deletions(-) diff --git a/dubbo/src/extension/mod.rs b/dubbo/src/extension/mod.rs index 5641bea8..c1d03955 100644 --- a/dubbo/src/extension/mod.rs +++ b/dubbo/src/extension/mod.rs @@ -22,8 +22,9 @@ use crate::{ }; use dubbo_base::{extension_param::ExtensionType, url::UrlParam, StdError, Url}; use dubbo_logger::tracing::{error, info}; +use std::{future::Future, pin::Pin, sync::Arc}; use thiserror::Error; -use tokio::sync::oneshot; +use tokio::sync::{oneshot, RwLock}; pub static EXTENSIONS: once_cell::sync::Lazy = once_cell::sync::Lazy::new(|| ExtensionDirectory::init()); @@ -41,13 +42,11 @@ impl ExtensionDirectory { let mut extension_directory = ExtensionDirectory::default(); // register static registry extension - let _ = extension_directory - .register( - StaticRegistry::name(), - StaticRegistry::convert_to_extension_factories(), - ExtensionType::Registry, - ) - .await; + let _ = extension_directory.register( + StaticRegistry::name(), + StaticRegistry::convert_to_extension_factories(), + ExtensionType::Registry, + ); while let Some(extension_opt) = rx.recv().await { match extension_opt { @@ -57,20 +56,19 @@ impl ExtensionDirectory { extension_type, tx, ) => { - let result = extension_directory - .register(extension_name, extension_factories, extension_type) - .await; + let result = extension_directory.register( + extension_name, + extension_factories, + extension_type, + ); let _ = tx.send(result); } ExtensionOpt::Remove(extension_name, extension_type, tx) => { - let result = extension_directory - .remove(extension_name, extension_type) - .await; + let result = extension_directory.remove(extension_name, extension_type); let _ = tx.send(result); } ExtensionOpt::Load(url, extension_type, tx) => { - let result = extension_directory.load(url, extension_type).await; - let _ = tx.send(result); + let _ = extension_directory.load(url, extension_type, tx); } } } @@ -79,7 +77,7 @@ impl ExtensionDirectory { ExtensionDirectoryCommander { sender: tx } } - async fn register( + fn register( &mut self, extension_name: String, extension_factories: ExtensionFactories, @@ -89,40 +87,53 @@ impl ExtensionDirectory { ExtensionType::Registry => match extension_factories { ExtensionFactories::RegistryExtensionFactory(registry_extension_factory) => { self.registry_extension_loader - .register(extension_name, registry_extension_factory) - .await; + .register(extension_name, registry_extension_factory); Ok(()) } }, } } - async fn remove( + fn remove( &mut self, extension_name: String, extension_type: ExtensionType, ) -> Result<(), StdError> { match extension_type { ExtensionType::Registry => { - self.registry_extension_loader.remove(extension_name).await; + self.registry_extension_loader.remove(extension_name); Ok(()) } } } - async fn load( + fn load( &mut self, url: Url, extension_type: ExtensionType, - ) -> Result { + callback: oneshot::Sender>, + ) { match extension_type { ExtensionType::Registry => { - let extension = self.registry_extension_loader.load(&url).await; + let extension = self.registry_extension_loader.load(url); match extension { - Ok(extension) => Ok(Extensions::Registry(extension)), + Ok(mut extension) => { + tokio::spawn(async move { + let extension = extension.resolve().await; + match extension { + Ok(extension) => { + let _ = callback.send(Ok(Extensions::Registry(extension))); + } + Err(err) => { + error!("load extension failed: {}", err); + let _ = callback.send(Err(err)); + } + } + }); + } Err(err) => { error!("load extension failed: {}", err); - Err(err) + let _ = callback.send(Err(err)); } } } @@ -130,6 +141,95 @@ impl ExtensionDirectory { } } +type ExtensionCreator = Box< + dyn Fn(Url) -> Pin> + Send + 'static>> + + Send + + Sync + + 'static, +>; +pub(crate) struct ExtensionPromiseResolver { + resolved_data: Option, + creator: ExtensionCreator, + url: Url, +} + +impl ExtensionPromiseResolver +where + T: Send + Clone + 'static, +{ + fn new(creator: ExtensionCreator, url: Url) -> Self { + ExtensionPromiseResolver { + resolved_data: None, + creator, + url, + } + } + + fn resolved_data(&self) -> Option { + self.resolved_data.clone() + } + + async fn resolve(&mut self) -> Result { + match (self.creator)(self.url.clone()).await { + Ok(data) => { + self.resolved_data = Some(data.clone()); + Ok(data) + } + Err(err) => { + error!("create extension failed: {}", err); + Err(LoadExtensionError::new( + "load extension failed, create extension occur an error".to_string(), + ) + .into()) + } + } + } +} + +pub(crate) struct LoadExtensionPromise { + resolver: Arc>>, +} + +impl LoadExtensionPromise +where + T: Send + Clone + 'static, +{ + pub(crate) fn new(creator: ExtensionCreator, url: Url) -> Self { + let resolver = ExtensionPromiseResolver::new(creator, url); + LoadExtensionPromise { + resolver: Arc::new(RwLock::new(resolver)), + } + } + + pub(crate) async fn resolve(&mut self) -> Result { + // get read lock + let resolver_read_lock = self.resolver.read().await; + // if extension is not None, return it + if let Some(extension) = resolver_read_lock.resolved_data() { + return Ok(extension); + } + drop(resolver_read_lock); + + let mut write_lock = self.resolver.write().await; + + match write_lock.resolved_data() { + Some(extension) => Ok(extension), + None => { + let extension = write_lock.resolve().await; + extension + } + } + } +} + +impl Clone for LoadExtensionPromise { + fn clone(&self) -> Self { + LoadExtensionPromise { + resolver: self.resolver.clone(), + } + } +} + pub struct ExtensionDirectoryCommander { sender: tokio::sync::mpsc::Sender, } @@ -280,7 +380,7 @@ pub trait Extension: Sealed { fn name() -> String; - async fn create(url: &Url) -> Result; + async fn create(url: Url) -> Result; } #[allow(private_bounds)] diff --git a/dubbo/src/extension/registry_extension.rs b/dubbo/src/extension/registry_extension.rs index e27d6a58..ce998bce 100644 --- a/dubbo/src/extension/registry_extension.rs +++ b/dubbo/src/extension/registry_extension.rs @@ -29,6 +29,7 @@ use proxy::RegistryProxy; use crate::extension::{ ConvertToExtensionFactories, Extension, ExtensionFactories, ExtensionMetaInfo, ExtensionType, + LoadExtensionPromise, }; // extension://0.0.0.0/?extension-type=registry&extension-name=nacos®istry-url=nacos://127.0.0.1:8848 @@ -78,27 +79,9 @@ where T: Extension>, { fn convert_to_extension_factories() -> ExtensionFactories { - fn constrain(f: F) -> F - where - F: for<'a> Fn( - &'a Url, - ) -> Pin< - Box< - dyn Future, StdError>> - + Send - + 'a, - >, - >, - { - f - } - - let constructor = constrain(|url: &Url| { - let f = ::create(url); - Box::pin(f) - }); - - ExtensionFactories::RegistryExtensionFactory(RegistryExtensionFactory::new(constructor)) + ExtensionFactories::RegistryExtensionFactory(RegistryExtensionFactory::new( + ::create, + )) } } @@ -108,19 +91,18 @@ pub(super) struct RegistryExtensionLoader { } impl RegistryExtensionLoader { - pub(crate) async fn register( - &mut self, - extension_name: String, - factory: RegistryExtensionFactory, - ) { + pub(crate) fn register(&mut self, extension_name: String, factory: RegistryExtensionFactory) { self.factories.insert(extension_name, factory); } - pub(crate) async fn remove(&mut self, extension_name: String) { + pub(crate) fn remove(&mut self, extension_name: String) { self.factories.remove(&extension_name); } - pub(crate) async fn load(&mut self, url: &Url) -> Result { + pub(crate) fn load( + &mut self, + url: Url, + ) -> Result, StdError> { let extension_name = url.query::().unwrap(); let extension_name = extension_name.value(); let factory = self.factories.get_mut(&extension_name).ok_or_else(|| { @@ -129,19 +111,19 @@ impl RegistryExtensionLoader { extension_name )) })?; - factory.create(url).await + factory.create(url) } } -type RegistryConstructor = for<'a> fn( - &'a Url, +type RegistryConstructor = fn( + Url, ) -> Pin< - Box, StdError>> + Send + 'a>, + Box, StdError>> + Send>, >; pub(crate) struct RegistryExtensionFactory { constructor: RegistryConstructor, - instances: HashMap, + instances: HashMap>, } impl RegistryExtensionFactory { @@ -154,7 +136,10 @@ impl RegistryExtensionFactory { } impl RegistryExtensionFactory { - pub(super) async fn create(&mut self, url: &Url) -> Result { + pub(super) fn create( + &mut self, + url: Url, + ) -> Result, StdError> { let registry_url = url.query::().unwrap(); let registry_url = registry_url.value(); let url_str = registry_url.as_str().to_string(); @@ -164,10 +149,28 @@ impl RegistryExtensionFactory { Ok(proxy) } None => { - let registry = (self.constructor)(url).await?; - let proxy = >>::from(registry); - self.instances.insert(url_str, proxy.clone()); - Ok(proxy) + let constructor = self.constructor; + + let creator = move |url: Url| { + let registry = constructor(url); + Box::pin(async move { + let registry = registry.await?; + let proxy = + >>::from(registry); + Ok(proxy) + }) + as Pin< + Box< + dyn Future> + + Send + + 'static, + >, + > + }; + + let promise = LoadExtensionPromise::new(Box::new(creator), url); + self.instances.insert(url_str, promise.clone()); + Ok(promise) } } } diff --git a/dubbo/src/registry/registry.rs b/dubbo/src/registry/registry.rs index 85a81683..f998778b 100644 --- a/dubbo/src/registry/registry.rs +++ b/dubbo/src/registry/registry.rs @@ -200,7 +200,7 @@ impl Extension for StaticRegistry { "static".to_string() } - async fn create(url: &Url) -> Result { + async fn create(url: Url) -> Result { // url example: // extension://0.0.0.0?extension-type=registry&extension-name=static®istry=static://127.0.0.1 let static_invoker_urls = url.query::(); diff --git a/registry/nacos/src/lib.rs b/registry/nacos/src/lib.rs index 204846b2..0507452c 100644 --- a/registry/nacos/src/lib.rs +++ b/registry/nacos/src/lib.rs @@ -256,7 +256,7 @@ impl Extension for NacosRegistry { "nacos".to_string() } - async fn create(url: &Url) -> Result { + async fn create(url: Url) -> Result { // url example: // extension://0.0.0.0?extension-type=registry&extension-name=nacos®istry=nacos://127.0.0.1:8848 let registry_url = url.query::().unwrap(); @@ -446,7 +446,7 @@ pub mod tests { extension_url.add_query_param(ExtensionName::new("nacos".to_string())); extension_url.add_query_param(RegistryUrl::new("nacos://127.0.0.1:8848/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-triple-api-provider&dubbo=2.0.2&interface=org.apache.dubbo.registry.RegistryService&pid=7015".parse().unwrap())); - let registry = NacosRegistry::create(&extension_url).await.unwrap(); + let registry = NacosRegistry::create(extension_url).await.unwrap(); let mut service_url: Url = "tri://127.0.0.1:50052/org.apache.dubbo.demo.GreeterService?anyhost=true&application=dubbo-demo-triple-api-provider&background=false&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.GreeterService&methods=sayHello,sayHelloAsync&pid=7015&service-name-mapping=true&side=provider×tamp=1670060843807".parse().unwrap(); @@ -478,7 +478,7 @@ pub mod tests { extension_url.add_query_param(ExtensionName::new("nacos".to_string())); extension_url.add_query_param(RegistryUrl::new("nacos://127.0.0.1:8848/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-triple-api-provider&dubbo=2.0.2&interface=org.apache.dubbo.registry.RegistryService&pid=7015".parse().unwrap())); - let registry = NacosRegistry::create(&extension_url).await.unwrap(); + let registry = NacosRegistry::create(extension_url).await.unwrap(); let mut service_url: Url = "tri://127.0.0.1:50052/org.apache.dubbo.demo.GreeterService?anyhost=true&application=dubbo-demo-triple-api-provider&background=false&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.GreeterService&methods=sayHello,sayHelloAsync&pid=7015&service-name-mapping=true&side=provider×tamp=1670060843807".parse().unwrap(); @@ -518,7 +518,7 @@ pub mod tests { extension_url.add_query_param(ExtensionName::new("nacos".to_string())); extension_url.add_query_param(RegistryUrl::new("nacos://127.0.0.1:8848/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-triple-api-provider&dubbo=2.0.2&interface=org.apache.dubbo.registry.RegistryService&pid=7015".parse().unwrap())); - let registry = NacosRegistry::create(&extension_url).await.unwrap(); + let registry = NacosRegistry::create(extension_url).await.unwrap(); let mut service_url: Url = "tri://127.0.0.1:50052/org.apache.dubbo.demo.GreeterService?anyhost=true&application=dubbo-demo-triple-api-provider&background=false&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.GreeterService&methods=sayHello,sayHelloAsync&pid=7015&service-name-mapping=true&side=provider×tamp=1670060843807".parse().unwrap(); @@ -562,7 +562,7 @@ pub mod tests { extension_url.add_query_param(ExtensionName::new("nacos".to_string())); extension_url.add_query_param(RegistryUrl::new("nacos://127.0.0.1:8848/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-triple-api-provider&dubbo=2.0.2&interface=org.apache.dubbo.registry.RegistryService&pid=7015".parse().unwrap())); - let registry = NacosRegistry::create(&extension_url).await.unwrap(); + let registry = NacosRegistry::create(extension_url).await.unwrap(); let mut service_url: Url = "tri://127.0.0.1:50052/org.apache.dubbo.demo.GreeterService?anyhost=true&application=dubbo-demo-triple-api-provider&background=false&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.GreeterService&methods=sayHello,sayHelloAsync&pid=7015&service-name-mapping=true&side=provider×tamp=1670060843807".parse().unwrap(); From 110c7df3874dfc576c0d82287dad9f5d8f7f6355 Mon Sep 17 00:00:00 2001 From: yangyang <962032265@qq.com> Date: Thu, 21 Mar 2024 20:14:37 +0800 Subject: [PATCH 44/48] refeat(extensions): add sync for Registry trait --- dubbo/src/extension/registry_extension.rs | 18 +- dubbo/src/registry/registry.rs | 2 +- dubbo/src/triple/client/triple.rs | 4 +- dubbo/src/triple/transport/connection.rs | 2 +- .../echo/src/generated/grpc.examples.echo.rs | 355 ++++++++++++++++++ 5 files changed, 367 insertions(+), 14 deletions(-) create mode 100644 examples/echo/src/generated/grpc.examples.echo.rs diff --git a/dubbo/src/extension/registry_extension.rs b/dubbo/src/extension/registry_extension.rs index ce998bce..4895b7a3 100644 --- a/dubbo/src/extension/registry_extension.rs +++ b/dubbo/src/extension/registry_extension.rs @@ -61,12 +61,12 @@ pub trait Registry { fn url(&self) -> &Url; } -impl crate::extension::Sealed for T where T: Registry + Send + 'static {} +impl crate::extension::Sealed for T where T: Registry + Send +Sync + 'static {} impl ExtensionMetaInfo for T where - T: Registry + Send + 'static, - T: Extension>, + T: Registry + Send+Sync + 'static, + T: Extension>, { fn extension_type() -> ExtensionType { ExtensionType::Registry @@ -75,8 +75,8 @@ where impl ConvertToExtensionFactories for T where - T: Registry + Send + 'static, - T: Extension>, + T: Registry + Send+Sync + 'static, + T: Extension>, { fn convert_to_extension_factories() -> ExtensionFactories { ExtensionFactories::RegistryExtensionFactory(RegistryExtensionFactory::new( @@ -118,7 +118,7 @@ impl RegistryExtensionLoader { type RegistryConstructor = fn( Url, ) -> Pin< - Box, StdError>> + Send>, + Box, StdError>> + Send>, >; pub(crate) struct RegistryExtensionFactory { @@ -156,7 +156,7 @@ impl RegistryExtensionFactory { Box::pin(async move { let registry = registry.await?; let proxy = - >>::from(registry); + >>::from(registry); Ok(proxy) }) as Pin< @@ -334,8 +334,8 @@ pub mod proxy { } } - impl From> for RegistryProxy { - fn from(registry: Box) -> Self { + impl From> for RegistryProxy { + fn from(registry: Box) -> Self { let url = registry.url().clone(); let (sender, mut receiver) = tokio::sync::mpsc::channel(1024); diff --git a/dubbo/src/registry/registry.rs b/dubbo/src/registry/registry.rs index f998778b..27ded7e6 100644 --- a/dubbo/src/registry/registry.rs +++ b/dubbo/src/registry/registry.rs @@ -194,7 +194,7 @@ impl Registry for StaticRegistry { #[async_trait::async_trait] impl Extension for StaticRegistry { - type Target = Box; + type Target = Box; fn name() -> String { "static".to_string() diff --git a/dubbo/src/triple/client/triple.rs b/dubbo/src/triple/client/triple.rs index 3966ec42..a50ac015 100644 --- a/dubbo/src/triple/client/triple.rs +++ b/dubbo/src/triple/client/triple.rs @@ -16,18 +16,16 @@ */ use futures_util::{future, stream, StreamExt, TryStreamExt}; - +use tower_service::Service; use aws_smithy_http::body::SdkBody; use http::HeaderValue; use prost::Message; use serde::{Deserialize, Serialize}; -use super::builder::ClientBuilder; use crate::codegen::{ProstCodec, RpcInvocation, SerdeCodec}; use crate::{ invocation::{IntoStreamingRequest, Metadata, Request, Response}, - protocol::BoxInvoker, status::Status, svc::NewService, triple::{ diff --git a/dubbo/src/triple/transport/connection.rs b/dubbo/src/triple/transport/connection.rs index cd5335bd..4369b82a 100644 --- a/dubbo/src/triple/transport/connection.rs +++ b/dubbo/src/triple/transport/connection.rs @@ -67,7 +67,7 @@ impl Connection { pub fn build(mut self) -> Self { let builder = self.builder.clone().http2_only(true).to_owned(); - let hyper_connect: HyperConnect = Connect::new(get_connector(self.connector), builder); + let hyper_connect: HyperConnect = Connect::new(get_connector(&self.connector), builder); self.connect = Some(hyper_connect); self } diff --git a/examples/echo/src/generated/grpc.examples.echo.rs b/examples/echo/src/generated/grpc.examples.echo.rs new file mode 100644 index 00000000..ee8cc1e6 --- /dev/null +++ b/examples/echo/src/generated/grpc.examples.echo.rs @@ -0,0 +1,355 @@ +/// EchoRequest is the request for echo. +#[derive(serde::Serialize, serde::Deserialize)] +#[serde(default)] +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct EchoRequest { + #[prost(string, tag = "1")] + pub message: ::prost::alloc::string::String, +} +/// EchoResponse is the response for echo. +#[derive(serde::Serialize, serde::Deserialize)] +#[serde(default)] +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct EchoResponse { + #[prost(string, tag = "1")] + pub message: ::prost::alloc::string::String, +} +/// Generated client implementations. +pub mod echo_client { + #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] + use dubbo::codegen::*; + /// Echo is the echo service. + #[derive(Clone)] + pub struct EchoClient { + inner: TripleClient, + } + impl EchoClient { + pub fn connect(host: String) -> Self { + let cli = TripleClient::connect(host); + EchoClient { inner: cli } + } + pub fn new(builder: ClientBuilder) -> Self { + Self { + inner: TripleClient::new(builder), + } + } + /// UnaryEcho is unary echo. + pub async fn unary_echo( + &mut self, + request: Request, + ) -> Result, dubbo::status::Status> { + let invocation = RpcInvocation::default() + .with_service_unique_name(String::from("grpc.examples.echo.Echo")) + .with_method_name(String::from("UnaryEcho")); + let path = http::uri::PathAndQuery::from_static( + "/grpc.examples.echo.Echo/UnaryEcho", + ); + self.inner.unary(request, path, invocation).await + } + /// ServerStreamingEcho is server side streaming. + pub async fn server_streaming_echo( + &mut self, + request: Request, + ) -> Result>, dubbo::status::Status> { + let invocation = RpcInvocation::default() + .with_service_unique_name(String::from("grpc.examples.echo.Echo")) + .with_method_name(String::from("ServerStreamingEcho")); + let path = http::uri::PathAndQuery::from_static( + "/grpc.examples.echo.Echo/ServerStreamingEcho", + ); + self.inner.server_streaming(request, path, invocation).await + } + /// ClientStreamingEcho is client side streaming. + pub async fn client_streaming_echo( + &mut self, + request: impl IntoStreamingRequest, + ) -> Result, dubbo::status::Status> { + let invocation = RpcInvocation::default() + .with_service_unique_name(String::from("grpc.examples.echo.Echo")) + .with_method_name(String::from("ClientStreamingEcho")); + let path = http::uri::PathAndQuery::from_static( + "/grpc.examples.echo.Echo/ClientStreamingEcho", + ); + self.inner.client_streaming(request, path, invocation).await + } + /// BidirectionalStreamingEcho is bidi streaming. + pub async fn bidirectional_streaming_echo( + &mut self, + request: impl IntoStreamingRequest, + ) -> Result>, dubbo::status::Status> { + let invocation = RpcInvocation::default() + .with_service_unique_name(String::from("grpc.examples.echo.Echo")) + .with_method_name(String::from("BidirectionalStreamingEcho")); + let path = http::uri::PathAndQuery::from_static( + "/grpc.examples.echo.Echo/BidirectionalStreamingEcho", + ); + self.inner.bidi_streaming(request, path, invocation).await + } + } +} +/// Generated server implementations. +pub mod echo_server { + #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] + use dubbo::codegen::*; + ///Generated trait containing gRPC methods that should be implemented for use with EchoServer. + #[async_trait] + pub trait Echo: Send + Sync + 'static { + /// UnaryEcho is unary echo. + async fn unary_echo( + &self, + request: Request, + ) -> Result, dubbo::status::Status>; + ///Server streaming response type for the ServerStreamingEcho method. + type ServerStreamingEchoStream: futures_util::Stream< + Item = Result, + > + + Send + + 'static; + /// ServerStreamingEcho is server side streaming. + async fn server_streaming_echo( + &self, + request: Request, + ) -> Result, dubbo::status::Status>; + /// ClientStreamingEcho is client side streaming. + async fn client_streaming_echo( + &self, + request: Request>, + ) -> Result, dubbo::status::Status>; + ///Server streaming response type for the BidirectionalStreamingEcho method. + type BidirectionalStreamingEchoStream: futures_util::Stream< + Item = Result, + > + + Send + + 'static; + /// BidirectionalStreamingEcho is bidi streaming. + async fn bidirectional_streaming_echo( + &self, + request: Request>, + ) -> Result< + Response, + dubbo::status::Status, + >; + } + /// Echo is the echo service. + #[derive(Debug)] + pub struct EchoServer { + inner: _Inner, + } + struct _Inner(Arc); + impl EchoServer { + pub fn new(inner: T) -> Self { + Self { + inner: _Inner(Arc::new(inner)), + } + } + pub fn with_filter(inner: T, filter: F) -> FilterService + where + F: Filter, + { + FilterService::new(Self::new(inner), filter) + } + } + impl Service> for EchoServer + where + T: Echo, + B: Body + Send + 'static, + B::Error: Into + Send + 'static, + { + type Response = http::Response; + type Error = std::convert::Infallible; + type Future = BoxFuture; + fn poll_ready( + &mut self, + _cx: &mut Context<'_>, + ) -> Poll> { + Poll::Ready(Ok(())) + } + fn call(&mut self, req: http::Request) -> Self::Future { + let inner = self.inner.clone(); + match req.uri().path() { + "/grpc.examples.echo.Echo/UnaryEcho" => { + #[allow(non_camel_case_types)] + struct UnaryEchoServer { + inner: _Inner, + } + impl UnarySvc for UnaryEchoServer { + type Response = super::EchoResponse; + type Future = BoxFuture< + Response, + dubbo::status::Status, + >; + fn call( + &mut self, + request: Request, + ) -> Self::Future { + let inner = self.inner.0.clone(); + let fut = async move { inner.unary_echo(request).await }; + Box::pin(fut) + } + } + let fut = async move { + let mut server = TripleServer::< + super::EchoRequest, + super::EchoResponse, + >::new(); + let res = server.unary(UnaryEchoServer { inner }, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/grpc.examples.echo.Echo/ServerStreamingEcho" => { + #[allow(non_camel_case_types)] + struct ServerStreamingEchoServer { + inner: _Inner, + } + impl ServerStreamingSvc + for ServerStreamingEchoServer { + type Response = super::EchoResponse; + type ResponseStream = T::ServerStreamingEchoStream; + type Future = BoxFuture< + Response, + dubbo::status::Status, + >; + fn call( + &mut self, + request: Request, + ) -> Self::Future { + let inner = self.inner.0.clone(); + let fut = async move { + inner.server_streaming_echo(request).await + }; + Box::pin(fut) + } + } + let fut = async move { + let mut server = TripleServer::< + super::EchoRequest, + super::EchoResponse, + >::new(); + let res = server + .server_streaming(ServerStreamingEchoServer { inner }, req) + .await; + Ok(res) + }; + Box::pin(fut) + } + "/grpc.examples.echo.Echo/ClientStreamingEcho" => { + #[allow(non_camel_case_types)] + struct ClientStreamingEchoServer { + inner: _Inner, + } + impl ClientStreamingSvc + for ClientStreamingEchoServer { + type Response = super::EchoResponse; + type Future = BoxFuture< + Response, + dubbo::status::Status, + >; + fn call( + &mut self, + request: Request>, + ) -> Self::Future { + let inner = self.inner.0.clone(); + let fut = async move { + inner.client_streaming_echo(request).await + }; + Box::pin(fut) + } + } + let fut = async move { + let mut server = TripleServer::< + super::EchoRequest, + super::EchoResponse, + >::new(); + let res = server + .client_streaming(ClientStreamingEchoServer { inner }, req) + .await; + Ok(res) + }; + Box::pin(fut) + } + "/grpc.examples.echo.Echo/BidirectionalStreamingEcho" => { + #[allow(non_camel_case_types)] + struct BidirectionalStreamingEchoServer { + inner: _Inner, + } + impl StreamingSvc + for BidirectionalStreamingEchoServer { + type Response = super::EchoResponse; + type ResponseStream = T::BidirectionalStreamingEchoStream; + type Future = BoxFuture< + Response, + dubbo::status::Status, + >; + fn call( + &mut self, + request: Request>, + ) -> Self::Future { + let inner = self.inner.0.clone(); + let fut = async move { + inner.bidirectional_streaming_echo(request).await + }; + Box::pin(fut) + } + } + let fut = async move { + let mut server = TripleServer::< + super::EchoRequest, + super::EchoResponse, + >::new(); + let res = server + .bidi_streaming( + BidirectionalStreamingEchoServer { + inner, + }, + req, + ) + .await; + Ok(res) + }; + Box::pin(fut) + } + _ => { + Box::pin(async move { + Ok( + http::Response::builder() + .status(200) + .header("grpc-status", "12") + .header("content-type", "application/grpc") + .body(empty_body()) + .unwrap(), + ) + }) + } + } + } + } + impl Clone for EchoServer { + fn clone(&self) -> Self { + let inner = self.inner.clone(); + Self { inner } + } + } + impl Clone for _Inner { + fn clone(&self) -> Self { + Self(self.0.clone()) + } + } + impl std::fmt::Debug for _Inner { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self.0) + } + } + pub fn register_server(server: T) { + let s = EchoServer::new(server); + dubbo::protocol::triple::TRIPLE_SERVICES + .write() + .unwrap() + .insert( + "grpc.examples.echo.Echo".to_string(), + dubbo::utils::boxed_clone::BoxCloneService::new(s), + ); + } +} From 6a2091a4e616eb24c9c5f8bca73d8b6799438949 Mon Sep 17 00:00:00 2001 From: yangyang <962032265@qq.com> Date: Sat, 23 Mar 2024 01:33:09 +0800 Subject: [PATCH 45/48] chore: cargo fmt --- dubbo/src/extension/registry_extension.rs | 21 +++++++++++---------- dubbo/src/registry/memory_registry.rs | 1 - dubbo/src/registry/mod.rs | 6 ++---- dubbo/src/registry/protocol.rs | 3 ++- dubbo/src/registry/registry.rs | 4 ++-- dubbo/src/triple/client/triple.rs | 7 ++++--- registry/nacos/src/lib.rs | 2 +- 7 files changed, 22 insertions(+), 22 deletions(-) diff --git a/dubbo/src/extension/registry_extension.rs b/dubbo/src/extension/registry_extension.rs index 4895b7a3..b1f75ba4 100644 --- a/dubbo/src/extension/registry_extension.rs +++ b/dubbo/src/extension/registry_extension.rs @@ -61,12 +61,12 @@ pub trait Registry { fn url(&self) -> &Url; } -impl crate::extension::Sealed for T where T: Registry + Send +Sync + 'static {} +impl crate::extension::Sealed for T where T: Registry + Send + Sync + 'static {} impl ExtensionMetaInfo for T where - T: Registry + Send+Sync + 'static, - T: Extension>, + T: Registry + Send + Sync + 'static, + T: Extension>, { fn extension_type() -> ExtensionType { ExtensionType::Registry @@ -75,8 +75,8 @@ where impl ConvertToExtensionFactories for T where - T: Registry + Send+Sync + 'static, - T: Extension>, + T: Registry + Send + Sync + 'static, + T: Extension>, { fn convert_to_extension_factories() -> ExtensionFactories { ExtensionFactories::RegistryExtensionFactory(RegistryExtensionFactory::new( @@ -118,7 +118,7 @@ impl RegistryExtensionLoader { type RegistryConstructor = fn( Url, ) -> Pin< - Box, StdError>> + Send>, + Box, StdError>> + Send>, >; pub(crate) struct RegistryExtensionFactory { @@ -155,8 +155,9 @@ impl RegistryExtensionFactory { let registry = constructor(url); Box::pin(async move { let registry = registry.await?; - let proxy = - >>::from(registry); + let proxy = >>::from( + registry, + ); Ok(proxy) }) as Pin< @@ -334,8 +335,8 @@ pub mod proxy { } } - impl From> for RegistryProxy { - fn from(registry: Box) -> Self { + impl From> for RegistryProxy { + fn from(registry: Box) -> Self { let url = registry.url().clone(); let (sender, mut receiver) = tokio::sync::mpsc::channel(1024); diff --git a/dubbo/src/registry/memory_registry.rs b/dubbo/src/registry/memory_registry.rs index db878d6f..093c16e2 100644 --- a/dubbo/src/registry/memory_registry.rs +++ b/dubbo/src/registry/memory_registry.rs @@ -15,7 +15,6 @@ * limitations under the License. */ -#![allow(unused_variables, dead_code, missing_docs)] use dubbo_logger::tracing::debug; use std::{ diff --git a/dubbo/src/registry/mod.rs b/dubbo/src/registry/mod.rs index 08ae175b..ac4c0125 100644 --- a/dubbo/src/registry/mod.rs +++ b/dubbo/src/registry/mod.rs @@ -15,8 +15,6 @@ * limitations under the License. */ -#![allow(unused_variables, dead_code, missing_docs)] - use crate::{extension, extension::registry_extension::proxy::RegistryProxy}; use dubbo_base::{StdError, Url}; use std::{ @@ -47,11 +45,11 @@ impl Service<()> for MkRegistryService { type Future = Pin> + Send + 'static>>; - fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } - fn call(&mut self, req: ()) -> Self::Future { + fn call(&mut self, _req: ()) -> Self::Future { let fut = extension::EXTENSIONS.load_registry(self.registry_url.clone()); Box::pin(fut) } diff --git a/dubbo/src/registry/protocol.rs b/dubbo/src/registry/protocol.rs index 76350a42..f5963336 100644 --- a/dubbo/src/registry/protocol.rs +++ b/dubbo/src/registry/protocol.rs @@ -15,6 +15,7 @@ * limitations under the License. */ +#[allow(unused_variables, dead_code, missing_docs)] use dubbo_base::{registry_param::InterfaceName, url::UrlParam, Url}; use dubbo_logger::tracing; use std::{ @@ -96,7 +97,7 @@ impl Protocol for RegistryProtocol { } } - async fn refer(self, url: Url) -> Self::Invoker { + async fn refer(self, _url: Url) -> Self::Invoker { // getRegisterUrl // get Registry from registry_url // init directory based on registry_url and Registry diff --git a/dubbo/src/registry/registry.rs b/dubbo/src/registry/registry.rs index 27ded7e6..6a821077 100644 --- a/dubbo/src/registry/registry.rs +++ b/dubbo/src/registry/registry.rs @@ -183,7 +183,7 @@ impl Registry for StaticRegistry { Ok(change_rx) } - async fn unsubscribe(&self, url: Url) -> Result<(), StdError> { + async fn unsubscribe(&self, _url: Url) -> Result<(), StdError> { Ok(()) } @@ -194,7 +194,7 @@ impl Registry for StaticRegistry { #[async_trait::async_trait] impl Extension for StaticRegistry { - type Target = Box; + type Target = Box; fn name() -> String { "static".to_string() diff --git a/dubbo/src/triple/client/triple.rs b/dubbo/src/triple/client/triple.rs index a50ac015..46f8e9ca 100644 --- a/dubbo/src/triple/client/triple.rs +++ b/dubbo/src/triple/client/triple.rs @@ -15,12 +15,12 @@ * limitations under the License. */ -use futures_util::{future, stream, StreamExt, TryStreamExt}; -use tower_service::Service; use aws_smithy_http::body::SdkBody; +use futures_util::{future, stream, StreamExt, TryStreamExt}; use http::HeaderValue; use prost::Message; use serde::{Deserialize, Serialize}; +use tower_service::Service; use crate::codegen::{ProstCodec, RpcInvocation, SerdeCodec}; @@ -33,7 +33,8 @@ use crate::{ compression::CompressionEncoding, decode::Decoding, encode::encode, - }}; + }, +}; use super::builder::{ClientBuilder, ServiceMK}; diff --git a/registry/nacos/src/lib.rs b/registry/nacos/src/lib.rs index 0507452c..9ac7022e 100644 --- a/registry/nacos/src/lib.rs +++ b/registry/nacos/src/lib.rs @@ -250,7 +250,7 @@ impl Registry for NacosRegistry { #[async_trait] impl Extension for NacosRegistry { - type Target = Box; + type Target = Box; fn name() -> String { "nacos".to_string() From 8984192b2f3cab9225819381fbc0462735f46970 Mon Sep 17 00:00:00 2001 From: yangyang <962032265@qq.com> Date: Sat, 23 Mar 2024 01:36:08 +0800 Subject: [PATCH 46/48] chore: cargo fmt --- .../echo/src/generated/grpc.examples.echo.rs | 127 +++++------------- 1 file changed, 37 insertions(+), 90 deletions(-) diff --git a/examples/echo/src/generated/grpc.examples.echo.rs b/examples/echo/src/generated/grpc.examples.echo.rs index ee8cc1e6..fc48dc5c 100644 --- a/examples/echo/src/generated/grpc.examples.echo.rs +++ b/examples/echo/src/generated/grpc.examples.echo.rs @@ -43,9 +43,7 @@ pub mod echo_client { let invocation = RpcInvocation::default() .with_service_unique_name(String::from("grpc.examples.echo.Echo")) .with_method_name(String::from("UnaryEcho")); - let path = http::uri::PathAndQuery::from_static( - "/grpc.examples.echo.Echo/UnaryEcho", - ); + let path = http::uri::PathAndQuery::from_static("/grpc.examples.echo.Echo/UnaryEcho"); self.inner.unary(request, path, invocation).await } /// ServerStreamingEcho is server side streaming. @@ -102,9 +100,7 @@ pub mod echo_server { request: Request, ) -> Result, dubbo::status::Status>; ///Server streaming response type for the ServerStreamingEcho method. - type ServerStreamingEchoStream: futures_util::Stream< - Item = Result, - > + type ServerStreamingEchoStream: futures_util::Stream> + Send + 'static; /// ServerStreamingEcho is server side streaming. @@ -118,19 +114,14 @@ pub mod echo_server { request: Request>, ) -> Result, dubbo::status::Status>; ///Server streaming response type for the BidirectionalStreamingEcho method. - type BidirectionalStreamingEchoStream: futures_util::Stream< - Item = Result, - > + type BidirectionalStreamingEchoStream: futures_util::Stream> + Send + 'static; /// BidirectionalStreamingEcho is bidi streaming. async fn bidirectional_streaming_echo( &self, request: Request>, - ) -> Result< - Response, - dubbo::status::Status, - >; + ) -> Result, dubbo::status::Status>; } /// Echo is the echo service. #[derive(Debug)] @@ -160,10 +151,7 @@ pub mod echo_server { type Response = http::Response; type Error = std::convert::Infallible; type Future = BoxFuture; - fn poll_ready( - &mut self, - _cx: &mut Context<'_>, - ) -> Poll> { + fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } fn call(&mut self, req: http::Request) -> Self::Future { @@ -176,24 +164,16 @@ pub mod echo_server { } impl UnarySvc for UnaryEchoServer { type Response = super::EchoResponse; - type Future = BoxFuture< - Response, - dubbo::status::Status, - >; - fn call( - &mut self, - request: Request, - ) -> Self::Future { + type Future = BoxFuture, dubbo::status::Status>; + fn call(&mut self, request: Request) -> Self::Future { let inner = self.inner.0.clone(); let fut = async move { inner.unary_echo(request).await }; Box::pin(fut) } } let fut = async move { - let mut server = TripleServer::< - super::EchoRequest, - super::EchoResponse, - >::new(); + let mut server = + TripleServer::::new(); let res = server.unary(UnaryEchoServer { inner }, req).await; Ok(res) }; @@ -204,30 +184,20 @@ pub mod echo_server { struct ServerStreamingEchoServer { inner: _Inner, } - impl ServerStreamingSvc - for ServerStreamingEchoServer { + impl ServerStreamingSvc for ServerStreamingEchoServer { type Response = super::EchoResponse; type ResponseStream = T::ServerStreamingEchoStream; - type Future = BoxFuture< - Response, - dubbo::status::Status, - >; - fn call( - &mut self, - request: Request, - ) -> Self::Future { + type Future = + BoxFuture, dubbo::status::Status>; + fn call(&mut self, request: Request) -> Self::Future { let inner = self.inner.0.clone(); - let fut = async move { - inner.server_streaming_echo(request).await - }; + let fut = async move { inner.server_streaming_echo(request).await }; Box::pin(fut) } } let fut = async move { - let mut server = TripleServer::< - super::EchoRequest, - super::EchoResponse, - >::new(); + let mut server = + TripleServer::::new(); let res = server .server_streaming(ServerStreamingEchoServer { inner }, req) .await; @@ -240,29 +210,21 @@ pub mod echo_server { struct ClientStreamingEchoServer { inner: _Inner, } - impl ClientStreamingSvc - for ClientStreamingEchoServer { + impl ClientStreamingSvc for ClientStreamingEchoServer { type Response = super::EchoResponse; - type Future = BoxFuture< - Response, - dubbo::status::Status, - >; + type Future = BoxFuture, dubbo::status::Status>; fn call( &mut self, request: Request>, ) -> Self::Future { let inner = self.inner.0.clone(); - let fut = async move { - inner.client_streaming_echo(request).await - }; + let fut = async move { inner.client_streaming_echo(request).await }; Box::pin(fut) } } let fut = async move { - let mut server = TripleServer::< - super::EchoRequest, - super::EchoResponse, - >::new(); + let mut server = + TripleServer::::new(); let res = server .client_streaming(ClientStreamingEchoServer { inner }, req) .await; @@ -275,54 +237,39 @@ pub mod echo_server { struct BidirectionalStreamingEchoServer { inner: _Inner, } - impl StreamingSvc - for BidirectionalStreamingEchoServer { + impl StreamingSvc for BidirectionalStreamingEchoServer { type Response = super::EchoResponse; type ResponseStream = T::BidirectionalStreamingEchoStream; - type Future = BoxFuture< - Response, - dubbo::status::Status, - >; + type Future = + BoxFuture, dubbo::status::Status>; fn call( &mut self, request: Request>, ) -> Self::Future { let inner = self.inner.0.clone(); - let fut = async move { - inner.bidirectional_streaming_echo(request).await - }; + let fut = + async move { inner.bidirectional_streaming_echo(request).await }; Box::pin(fut) } } let fut = async move { - let mut server = TripleServer::< - super::EchoRequest, - super::EchoResponse, - >::new(); + let mut server = + TripleServer::::new(); let res = server - .bidi_streaming( - BidirectionalStreamingEchoServer { - inner, - }, - req, - ) + .bidi_streaming(BidirectionalStreamingEchoServer { inner }, req) .await; Ok(res) }; Box::pin(fut) } - _ => { - Box::pin(async move { - Ok( - http::Response::builder() - .status(200) - .header("grpc-status", "12") - .header("content-type", "application/grpc") - .body(empty_body()) - .unwrap(), - ) - }) - } + _ => Box::pin(async move { + Ok(http::Response::builder() + .status(200) + .header("grpc-status", "12") + .header("content-type", "application/grpc") + .body(empty_body()) + .unwrap()) + }), } } } From db28e4639056d212e817d0614445c891da655a39 Mon Sep 17 00:00:00 2001 From: yangyang <962032265@qq.com> Date: Sat, 23 Mar 2024 01:47:14 +0800 Subject: [PATCH 47/48] chore: cargo fmt --- dubbo/src/registry/protocol.rs | 2 +- dubbo/src/registry/registry.rs | 6 +- .../echo/src/generated/grpc.examples.echo.rs | 127 +++++++++++++----- 3 files changed, 93 insertions(+), 42 deletions(-) diff --git a/dubbo/src/registry/protocol.rs b/dubbo/src/registry/protocol.rs index f5963336..f909988b 100644 --- a/dubbo/src/registry/protocol.rs +++ b/dubbo/src/registry/protocol.rs @@ -15,7 +15,6 @@ * limitations under the License. */ -#[allow(unused_variables, dead_code, missing_docs)] use dubbo_base::{registry_param::InterfaceName, url::UrlParam, Url}; use dubbo_logger::tracing; use std::{ @@ -36,6 +35,7 @@ pub struct RegistryProtocol { // registerAddr: Registry registries: Vec, // providerUrl: Exporter + #[allow(dead_code)] exporters: Arc>>, // serviceName: registryUrls services: HashMap>, diff --git a/dubbo/src/registry/registry.rs b/dubbo/src/registry/registry.rs index 6a821077..e0074c4e 100644 --- a/dubbo/src/registry/registry.rs +++ b/dubbo/src/registry/registry.rs @@ -18,7 +18,7 @@ use std::collections::{HashMap, HashSet}; use async_trait::async_trait; use itertools::Itertools; -use thiserror::Error; + use tokio::sync::{ mpsc::{self}, Mutex, @@ -217,6 +217,4 @@ impl Extension for StaticRegistry { Ok(Box::new(static_registry)) } } -#[derive(Error, Debug)] -#[error("static registry error: {0}")] -struct StaticRegistryError(String); + diff --git a/examples/echo/src/generated/grpc.examples.echo.rs b/examples/echo/src/generated/grpc.examples.echo.rs index fc48dc5c..ee8cc1e6 100644 --- a/examples/echo/src/generated/grpc.examples.echo.rs +++ b/examples/echo/src/generated/grpc.examples.echo.rs @@ -43,7 +43,9 @@ pub mod echo_client { let invocation = RpcInvocation::default() .with_service_unique_name(String::from("grpc.examples.echo.Echo")) .with_method_name(String::from("UnaryEcho")); - let path = http::uri::PathAndQuery::from_static("/grpc.examples.echo.Echo/UnaryEcho"); + let path = http::uri::PathAndQuery::from_static( + "/grpc.examples.echo.Echo/UnaryEcho", + ); self.inner.unary(request, path, invocation).await } /// ServerStreamingEcho is server side streaming. @@ -100,7 +102,9 @@ pub mod echo_server { request: Request, ) -> Result, dubbo::status::Status>; ///Server streaming response type for the ServerStreamingEcho method. - type ServerStreamingEchoStream: futures_util::Stream> + type ServerStreamingEchoStream: futures_util::Stream< + Item = Result, + > + Send + 'static; /// ServerStreamingEcho is server side streaming. @@ -114,14 +118,19 @@ pub mod echo_server { request: Request>, ) -> Result, dubbo::status::Status>; ///Server streaming response type for the BidirectionalStreamingEcho method. - type BidirectionalStreamingEchoStream: futures_util::Stream> + type BidirectionalStreamingEchoStream: futures_util::Stream< + Item = Result, + > + Send + 'static; /// BidirectionalStreamingEcho is bidi streaming. async fn bidirectional_streaming_echo( &self, request: Request>, - ) -> Result, dubbo::status::Status>; + ) -> Result< + Response, + dubbo::status::Status, + >; } /// Echo is the echo service. #[derive(Debug)] @@ -151,7 +160,10 @@ pub mod echo_server { type Response = http::Response; type Error = std::convert::Infallible; type Future = BoxFuture; - fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { + fn poll_ready( + &mut self, + _cx: &mut Context<'_>, + ) -> Poll> { Poll::Ready(Ok(())) } fn call(&mut self, req: http::Request) -> Self::Future { @@ -164,16 +176,24 @@ pub mod echo_server { } impl UnarySvc for UnaryEchoServer { type Response = super::EchoResponse; - type Future = BoxFuture, dubbo::status::Status>; - fn call(&mut self, request: Request) -> Self::Future { + type Future = BoxFuture< + Response, + dubbo::status::Status, + >; + fn call( + &mut self, + request: Request, + ) -> Self::Future { let inner = self.inner.0.clone(); let fut = async move { inner.unary_echo(request).await }; Box::pin(fut) } } let fut = async move { - let mut server = - TripleServer::::new(); + let mut server = TripleServer::< + super::EchoRequest, + super::EchoResponse, + >::new(); let res = server.unary(UnaryEchoServer { inner }, req).await; Ok(res) }; @@ -184,20 +204,30 @@ pub mod echo_server { struct ServerStreamingEchoServer { inner: _Inner, } - impl ServerStreamingSvc for ServerStreamingEchoServer { + impl ServerStreamingSvc + for ServerStreamingEchoServer { type Response = super::EchoResponse; type ResponseStream = T::ServerStreamingEchoStream; - type Future = - BoxFuture, dubbo::status::Status>; - fn call(&mut self, request: Request) -> Self::Future { + type Future = BoxFuture< + Response, + dubbo::status::Status, + >; + fn call( + &mut self, + request: Request, + ) -> Self::Future { let inner = self.inner.0.clone(); - let fut = async move { inner.server_streaming_echo(request).await }; + let fut = async move { + inner.server_streaming_echo(request).await + }; Box::pin(fut) } } let fut = async move { - let mut server = - TripleServer::::new(); + let mut server = TripleServer::< + super::EchoRequest, + super::EchoResponse, + >::new(); let res = server .server_streaming(ServerStreamingEchoServer { inner }, req) .await; @@ -210,21 +240,29 @@ pub mod echo_server { struct ClientStreamingEchoServer { inner: _Inner, } - impl ClientStreamingSvc for ClientStreamingEchoServer { + impl ClientStreamingSvc + for ClientStreamingEchoServer { type Response = super::EchoResponse; - type Future = BoxFuture, dubbo::status::Status>; + type Future = BoxFuture< + Response, + dubbo::status::Status, + >; fn call( &mut self, request: Request>, ) -> Self::Future { let inner = self.inner.0.clone(); - let fut = async move { inner.client_streaming_echo(request).await }; + let fut = async move { + inner.client_streaming_echo(request).await + }; Box::pin(fut) } } let fut = async move { - let mut server = - TripleServer::::new(); + let mut server = TripleServer::< + super::EchoRequest, + super::EchoResponse, + >::new(); let res = server .client_streaming(ClientStreamingEchoServer { inner }, req) .await; @@ -237,39 +275,54 @@ pub mod echo_server { struct BidirectionalStreamingEchoServer { inner: _Inner, } - impl StreamingSvc for BidirectionalStreamingEchoServer { + impl StreamingSvc + for BidirectionalStreamingEchoServer { type Response = super::EchoResponse; type ResponseStream = T::BidirectionalStreamingEchoStream; - type Future = - BoxFuture, dubbo::status::Status>; + type Future = BoxFuture< + Response, + dubbo::status::Status, + >; fn call( &mut self, request: Request>, ) -> Self::Future { let inner = self.inner.0.clone(); - let fut = - async move { inner.bidirectional_streaming_echo(request).await }; + let fut = async move { + inner.bidirectional_streaming_echo(request).await + }; Box::pin(fut) } } let fut = async move { - let mut server = - TripleServer::::new(); + let mut server = TripleServer::< + super::EchoRequest, + super::EchoResponse, + >::new(); let res = server - .bidi_streaming(BidirectionalStreamingEchoServer { inner }, req) + .bidi_streaming( + BidirectionalStreamingEchoServer { + inner, + }, + req, + ) .await; Ok(res) }; Box::pin(fut) } - _ => Box::pin(async move { - Ok(http::Response::builder() - .status(200) - .header("grpc-status", "12") - .header("content-type", "application/grpc") - .body(empty_body()) - .unwrap()) - }), + _ => { + Box::pin(async move { + Ok( + http::Response::builder() + .status(200) + .header("grpc-status", "12") + .header("content-type", "application/grpc") + .body(empty_body()) + .unwrap(), + ) + }) + } } } } From bd2a0b18585ab37324725652c7db8a81b5a0dded Mon Sep 17 00:00:00 2001 From: yangyang <962032265@qq.com> Date: Sat, 23 Mar 2024 01:50:49 +0800 Subject: [PATCH 48/48] chore: cargo fmt --- dubbo/src/registry/registry.rs | 1 - .../echo/src/generated/grpc.examples.echo.rs | 127 +++++------------- 2 files changed, 37 insertions(+), 91 deletions(-) diff --git a/dubbo/src/registry/registry.rs b/dubbo/src/registry/registry.rs index e0074c4e..2fc82587 100644 --- a/dubbo/src/registry/registry.rs +++ b/dubbo/src/registry/registry.rs @@ -217,4 +217,3 @@ impl Extension for StaticRegistry { Ok(Box::new(static_registry)) } } - diff --git a/examples/echo/src/generated/grpc.examples.echo.rs b/examples/echo/src/generated/grpc.examples.echo.rs index ee8cc1e6..fc48dc5c 100644 --- a/examples/echo/src/generated/grpc.examples.echo.rs +++ b/examples/echo/src/generated/grpc.examples.echo.rs @@ -43,9 +43,7 @@ pub mod echo_client { let invocation = RpcInvocation::default() .with_service_unique_name(String::from("grpc.examples.echo.Echo")) .with_method_name(String::from("UnaryEcho")); - let path = http::uri::PathAndQuery::from_static( - "/grpc.examples.echo.Echo/UnaryEcho", - ); + let path = http::uri::PathAndQuery::from_static("/grpc.examples.echo.Echo/UnaryEcho"); self.inner.unary(request, path, invocation).await } /// ServerStreamingEcho is server side streaming. @@ -102,9 +100,7 @@ pub mod echo_server { request: Request, ) -> Result, dubbo::status::Status>; ///Server streaming response type for the ServerStreamingEcho method. - type ServerStreamingEchoStream: futures_util::Stream< - Item = Result, - > + type ServerStreamingEchoStream: futures_util::Stream> + Send + 'static; /// ServerStreamingEcho is server side streaming. @@ -118,19 +114,14 @@ pub mod echo_server { request: Request>, ) -> Result, dubbo::status::Status>; ///Server streaming response type for the BidirectionalStreamingEcho method. - type BidirectionalStreamingEchoStream: futures_util::Stream< - Item = Result, - > + type BidirectionalStreamingEchoStream: futures_util::Stream> + Send + 'static; /// BidirectionalStreamingEcho is bidi streaming. async fn bidirectional_streaming_echo( &self, request: Request>, - ) -> Result< - Response, - dubbo::status::Status, - >; + ) -> Result, dubbo::status::Status>; } /// Echo is the echo service. #[derive(Debug)] @@ -160,10 +151,7 @@ pub mod echo_server { type Response = http::Response; type Error = std::convert::Infallible; type Future = BoxFuture; - fn poll_ready( - &mut self, - _cx: &mut Context<'_>, - ) -> Poll> { + fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } fn call(&mut self, req: http::Request) -> Self::Future { @@ -176,24 +164,16 @@ pub mod echo_server { } impl UnarySvc for UnaryEchoServer { type Response = super::EchoResponse; - type Future = BoxFuture< - Response, - dubbo::status::Status, - >; - fn call( - &mut self, - request: Request, - ) -> Self::Future { + type Future = BoxFuture, dubbo::status::Status>; + fn call(&mut self, request: Request) -> Self::Future { let inner = self.inner.0.clone(); let fut = async move { inner.unary_echo(request).await }; Box::pin(fut) } } let fut = async move { - let mut server = TripleServer::< - super::EchoRequest, - super::EchoResponse, - >::new(); + let mut server = + TripleServer::::new(); let res = server.unary(UnaryEchoServer { inner }, req).await; Ok(res) }; @@ -204,30 +184,20 @@ pub mod echo_server { struct ServerStreamingEchoServer { inner: _Inner, } - impl ServerStreamingSvc - for ServerStreamingEchoServer { + impl ServerStreamingSvc for ServerStreamingEchoServer { type Response = super::EchoResponse; type ResponseStream = T::ServerStreamingEchoStream; - type Future = BoxFuture< - Response, - dubbo::status::Status, - >; - fn call( - &mut self, - request: Request, - ) -> Self::Future { + type Future = + BoxFuture, dubbo::status::Status>; + fn call(&mut self, request: Request) -> Self::Future { let inner = self.inner.0.clone(); - let fut = async move { - inner.server_streaming_echo(request).await - }; + let fut = async move { inner.server_streaming_echo(request).await }; Box::pin(fut) } } let fut = async move { - let mut server = TripleServer::< - super::EchoRequest, - super::EchoResponse, - >::new(); + let mut server = + TripleServer::::new(); let res = server .server_streaming(ServerStreamingEchoServer { inner }, req) .await; @@ -240,29 +210,21 @@ pub mod echo_server { struct ClientStreamingEchoServer { inner: _Inner, } - impl ClientStreamingSvc - for ClientStreamingEchoServer { + impl ClientStreamingSvc for ClientStreamingEchoServer { type Response = super::EchoResponse; - type Future = BoxFuture< - Response, - dubbo::status::Status, - >; + type Future = BoxFuture, dubbo::status::Status>; fn call( &mut self, request: Request>, ) -> Self::Future { let inner = self.inner.0.clone(); - let fut = async move { - inner.client_streaming_echo(request).await - }; + let fut = async move { inner.client_streaming_echo(request).await }; Box::pin(fut) } } let fut = async move { - let mut server = TripleServer::< - super::EchoRequest, - super::EchoResponse, - >::new(); + let mut server = + TripleServer::::new(); let res = server .client_streaming(ClientStreamingEchoServer { inner }, req) .await; @@ -275,54 +237,39 @@ pub mod echo_server { struct BidirectionalStreamingEchoServer { inner: _Inner, } - impl StreamingSvc - for BidirectionalStreamingEchoServer { + impl StreamingSvc for BidirectionalStreamingEchoServer { type Response = super::EchoResponse; type ResponseStream = T::BidirectionalStreamingEchoStream; - type Future = BoxFuture< - Response, - dubbo::status::Status, - >; + type Future = + BoxFuture, dubbo::status::Status>; fn call( &mut self, request: Request>, ) -> Self::Future { let inner = self.inner.0.clone(); - let fut = async move { - inner.bidirectional_streaming_echo(request).await - }; + let fut = + async move { inner.bidirectional_streaming_echo(request).await }; Box::pin(fut) } } let fut = async move { - let mut server = TripleServer::< - super::EchoRequest, - super::EchoResponse, - >::new(); + let mut server = + TripleServer::::new(); let res = server - .bidi_streaming( - BidirectionalStreamingEchoServer { - inner, - }, - req, - ) + .bidi_streaming(BidirectionalStreamingEchoServer { inner }, req) .await; Ok(res) }; Box::pin(fut) } - _ => { - Box::pin(async move { - Ok( - http::Response::builder() - .status(200) - .header("grpc-status", "12") - .header("content-type", "application/grpc") - .body(empty_body()) - .unwrap(), - ) - }) - } + _ => Box::pin(async move { + Ok(http::Response::builder() + .status(200) + .header("grpc-status", "12") + .header("content-type", "application/grpc") + .body(empty_body()) + .unwrap()) + }), } } }