diff --git a/.github/workflows/github-actions.yml b/.github/workflows/github-actions.yml index b02b3bc9..1db92165 100644 --- a/.github/workflows/github-actions.yml +++ b/.github/workflows/github-actions.yml @@ -94,5 +94,4 @@ jobs: ../../target/debug/greeter-client env: ZOOKEEPER_SERVERS: 127.0.0.1:2181 - DUBBO_CONFIG_PATH: ./application.yaml working-directory: examples/greeter diff --git a/Cargo.toml b/Cargo.toml index 735eda40..9a1f0501 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,6 +49,7 @@ protocol-dubbo2 = {path="./protocol/dubbo2"} protocol-triple = {path="./protocol/triple"} registry-zookeeper = {path="./registry/zookeeper"} registry-nacos = {path="./registry/nacos"} +config = {path="./config"} anyhow = "1.0.66" thiserror = "1.0.30" dubbo = { path = "./dubbo/" } @@ -57,6 +58,8 @@ serde_yaml = "0.9.4" # yaml file parser once_cell = "1.16.0" itertools = "0.10.1" bytes = "1.0" - - - +ctor = "0.1.23" +getset = "0.1.2" +validator = "0.15" +tracing = "0.1.29" +tracing-subscriber = "0.3.1" diff --git a/common/base/Cargo.toml b/common/base/Cargo.toml index 7397c195..3293c4eb 100644 --- a/common/base/Cargo.toml +++ b/common/base/Cargo.toml @@ -8,4 +8,5 @@ edition = "2021" [dependencies] urlencoding.workspace = true http = "0.2" -logger.workspace = true \ No newline at end of file +tracing.workspace=true +anyhow.workspace = true \ No newline at end of file diff --git a/common/base/src/constants.rs b/common/base/src/constants.rs index b1faf270..bb3ff3f9 100644 --- a/common/base/src/constants.rs +++ b/common/base/src/constants.rs @@ -18,6 +18,9 @@ pub const REGISTRY_PROTOCOL: &str = "registry_protocol"; pub const PROTOCOL: &str = "protocol"; pub const REGISTRY: &str = "registry"; +pub const REGISTRY_TYPE_SERVICE: &str = "service"; +pub const REGISTRY_TYPE_APPLICATION: &str = "application"; + // URL key pub const DUBBO_KEY: &str = "dubbo"; pub const PROVIDERS_KEY: &str = "providers"; @@ -29,3 +32,12 @@ pub const INTERFACE_KEY: &str = "interface"; pub const ANYHOST_KEY: &str = "anyhost"; pub const SIDE_KEY: &str = "side"; pub const TIMESTAMP_KEY: &str = "timestamp"; + +// file name +pub const DEFAULT_CONFIG_FILE: &str = "dubbo.yaml"; + +// env keys +pub const ENV_DUBBO_CONFIG_PATH: &str = "DUBBO_CONFIG_PATH"; +pub const ENV_DUBBO_CONFIG_FILE: &str = "DUBBO_CONFIG_FILE"; + +// config keys diff --git a/common/base/src/lib.rs b/common/base/src/lib.rs index b97b342f..aec127c0 100644 --- a/common/base/src/lib.rs +++ b/common/base/src/lib.rs @@ -20,6 +20,7 @@ )] pub mod constants; pub mod node; +pub mod types; pub mod url; pub use node::Node; diff --git a/common/base/src/types/alias.rs b/common/base/src/types/alias.rs new file mode 100644 index 00000000..6beb6c2b --- /dev/null +++ b/common/base/src/types/alias.rs @@ -0,0 +1,38 @@ +/* + * 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. + */ +// type for registryName;can be customized;RegistryKey eg. zookeeper/nacos/consul +pub type RegistryId = String; +pub type RegistryKey = String; +// service/application +pub type RegistryType = String; +// protocolKey defined in protocol layer, mean specified protocol +pub type ServiceName = String; +pub type ServiceKey = String; +pub type SerializationKey = String; +// +pub type ProtocolId = String; +pub type ProtocolKey = String; +pub type GroupId = String; +pub type VersionNumber = String; + +pub type InterfaceName = String; + +pub type ClusterStrategy = String; +pub type FilterKey = String; +pub type ParamKey = String; + +pub type Port = String; diff --git a/common/base/src/types/error.rs b/common/base/src/types/error.rs new file mode 100644 index 00000000..2944f981 --- /dev/null +++ b/common/base/src/types/error.rs @@ -0,0 +1,16 @@ +/* + * 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. + */ diff --git a/common/base/src/types/mod.rs b/common/base/src/types/mod.rs new file mode 100644 index 00000000..6356be89 --- /dev/null +++ b/common/base/src/types/mod.rs @@ -0,0 +1,18 @@ +/* + * 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 alias; +pub mod error; diff --git a/common/base/src/url.rs b/common/base/src/url.rs index 48fbc1eb..168f6fbe 100644 --- a/common/base/src/url.rs +++ b/common/base/src/url.rs @@ -20,7 +20,10 @@ use std::{ fmt::{Display, Formatter}, }; -use crate::constants::{GROUP_KEY, INTERFACE_KEY, VERSION_KEY}; +use crate::{ + constants::{GROUP_KEY, INTERFACE_KEY, VERSION_KEY}, + types::alias::{ProtocolKey, ServiceKey, ServiceName}, +}; use http::Uri; #[derive(Debug, Clone, Default, PartialEq)] @@ -48,7 +51,7 @@ impl Url { let uri = url .parse::() .map_err(|err| { - logger::tracing::error!("fail to parse url({}), err: {:?}", url, err); + tracing::error!("fail to parse url({}), err: {:?}", url, err); }) .unwrap(); let query = uri.path_and_query().unwrap().query(); @@ -70,11 +73,11 @@ impl Url { Some(url_inst) } - pub fn get_service_key(&self) -> String { + pub fn get_service_key(&self) -> ServiceKey { self.service_key.clone() } - pub fn get_service_name(&self) -> String { + pub fn get_service_name(&self) -> ServiceName { self.service_name.clone() } diff --git a/common/logger/Cargo.toml b/common/logger/Cargo.toml index e965706e..354cbb15 100644 --- a/common/logger/Cargo.toml +++ b/common/logger/Cargo.toml @@ -6,7 +6,8 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -tracing = "0.1" -tracing-subscriber = "0.3" +tracing.workspace=true +tracing-subscriber.workspace=true once_cell.workspace = true -utils.workspace = true \ No newline at end of file +utils.workspace = true +config.workspace = true \ No newline at end of file diff --git a/common/logger/src/tracing_configurer.rs b/common/logger/src/tracing_configurer.rs index 1e2081f8..79ad0ede 100644 --- a/common/logger/src/tracing_configurer.rs +++ b/common/logger/src/tracing_configurer.rs @@ -18,17 +18,12 @@ // https://github.com/tokio-rs/tracing/issues/971 use crate::level::LevelWrapper; -use std::path::PathBuf; +use config::{resolve_config_location, util::yaml_key_reader}; use tracing::debug; -use utils::{path_util, yaml_util}; pub(crate) fn default() { - let path_buf = PathBuf::new() - .join(path_util::app_root_dir()) - .join("application.yaml"); - let level: LevelWrapper = yaml_util::yaml_key_reader(path_buf, "logging.level") - .unwrap() - .into(); + let path_buf = resolve_config_location(); + let level: LevelWrapper = yaml_key_reader(path_buf, "logging.level").unwrap().into(); tracing_subscriber::fmt() .compact() .with_line_number(true) diff --git a/common/utils/Cargo.toml b/common/utils/Cargo.toml index 0b8c84f1..df4519f5 100644 --- a/common/utils/Cargo.toml +++ b/common/utils/Cargo.toml @@ -12,4 +12,4 @@ project-root = "0.2.2" anyhow.workspace=true once_cell.workspace = true local-ip-address = "0.5.1" -port-selector = "0.1.6" \ No newline at end of file +port-selector = "0.1.6" diff --git a/common/utils/src/env_util.rs b/common/utils/src/env_util.rs new file mode 100644 index 00000000..41348740 --- /dev/null +++ b/common/utils/src/env_util.rs @@ -0,0 +1,40 @@ +/* + * 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::env; + +pub fn get_env_value(env_key: &str) -> Option { + env::var(env_key).ok() +} + +pub fn get_env_value_for_i32(env_key: &str) -> Option { + get_env_value(env_key).map(|v| v.parse::().unwrap()) +} + +#[cfg(test)] +mod tests { + use crate::env_util::{get_env_value, get_env_value_for_i32}; + use std::env; + + #[test] + fn test_get_env_value() { + env::set_var("TEST_ENV", "testxxx1"); + env::set_var("TEST_ENV3", "999"); + assert!(get_env_value("TEST_ENV").is_some()); + assert!(get_env_value("TEST_ENV2").is_none()); + assert_eq!(get_env_value_for_i32("TEST_ENV3"), Some(999_i32)) + } +} diff --git a/common/utils/src/host_util.rs b/common/utils/src/host_util.rs index 0b029ef8..fba432e6 100644 --- a/common/utils/src/host_util.rs +++ b/common/utils/src/host_util.rs @@ -22,18 +22,18 @@ pub use port_selector::Port; // get local ip for linux/macos/windows #[allow(dead_code)] -pub(crate) fn local_ip() -> IpAddr { +pub fn local_ip() -> IpAddr { local_ip_address::local_ip().unwrap() } #[allow(dead_code)] -pub(crate) fn is_free_port(port: Port) -> bool { +pub fn is_free_port(port: Port) -> bool { is_free(port) } // scan from the give port #[allow(dead_code)] -pub(crate) fn scan_free_port(port: Port) -> Port { +pub fn scan_free_port(port: Port) -> Port { for selected_port in port..65535 { if is_free_port(selected_port) { return selected_port; diff --git a/common/utils/src/lib.rs b/common/utils/src/lib.rs index 7a0a45b8..72e665dc 100644 --- a/common/utils/src/lib.rs +++ b/common/utils/src/lib.rs @@ -14,6 +14,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +pub mod env_util; pub mod host_util; pub mod path_util; -pub mod yaml_util; diff --git a/common/utils/src/path_util.rs b/common/utils/src/path_util.rs index 347f1088..f57eee89 100644 --- a/common/utils/src/path_util.rs +++ b/common/utils/src/path_util.rs @@ -32,7 +32,7 @@ mod tests { #[test] fn test_app_root_dir() { - let dir = app_root_dir().join("application.yaml"); + let dir = app_root_dir().join("../../../dubbo.yaml"); println!("dir: {}", dir.display()); } } diff --git a/common/utils/tests/application.yaml b/common/utils/tests/application.yaml deleted file mode 100644 index a40e4fe3..00000000 --- a/common/utils/tests/application.yaml +++ /dev/null @@ -1,4 +0,0 @@ -logging: - level: INFO - file: - path: /tmp/test.log diff --git a/config/Cargo.toml b/config/Cargo.toml index b4e19d47..06b89932 100644 --- a/config/Cargo.toml +++ b/config/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "dubbo-config" +name = "config" version = "0.3.0" edition = "2021" license = "Apache-2.0" @@ -15,4 +15,11 @@ serde_yaml.workspace = true lazy_static.workspace = true once_cell.workspace = true utils.workspace = true -logger.workspace=true \ No newline at end of file +base.workspace = true +tracing.workspace = true +tokio.workspace = true +getset.workspace = true +thiserror.workspace = true +anyhow.workspace = true +[dev-dependencies] +ctor.workspace = true diff --git a/config/src/api.rs b/config/src/api.rs new file mode 100644 index 00000000..a632b06f --- /dev/null +++ b/config/src/api.rs @@ -0,0 +1,278 @@ +/* + * 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 crate::error::ConfigError; +use anyhow::{anyhow, Error, Result}; +use base::types::alias::ServiceName; + +use crate::{ + types::{consumer::Reference, protocol::Protocol, registry::Registry, services::Service}, + ConfigWrapper, +}; + +// could be used for config_center + +pub trait ConfigApi { + fn dubbo_protocol_set(&self, protocol: &str, pairs: Vec<(&str, &str)>) -> Result<(), Error>; + fn dubbo_protocol_get(&self, protocol: &str) -> Result; + fn dubbo_registry_ids(&self) -> Option>; + fn dubbo_registry_get(&self, registry_id: &str) -> Result; + fn dubbo_registry_set(&self, registry_id: &str, pairs: Vec<(&str, &str)>) -> Result<(), Error>; + fn dubbo_services_name(&self) -> Option>; + fn dubbo_services_get(&self, service_name: &str) -> Result; + fn dubbo_services_set(&self, service_name: &str, pairs: Vec<(&str, &str)>) + -> Result<(), Error>; + fn dubbo_provider_services_name(&self) -> Option>; + fn dubbo_provider_services_get(&self, service_name: &str) -> Result; + fn dubbo_provider_services_set( + &self, + service_name: &str, + pairs: Vec<(&str, &str)>, + ) -> Result<(), Error>; + + fn dubbo_consumer_references_get(&self, service_name: &str) -> Result; + fn dubbo_consumer_references_set( + &self, + service_name: &str, + pairs: Vec<(&str, &str)>, + ) -> Result<(), Error>; +} + +impl ConfigApi for ConfigWrapper { + fn dubbo_protocol_set(&self, protocol: &str, pairs: Vec<(&str, &str)>) -> Result<(), Error> { + let mut guard = self.inner.lock().unwrap(); + if !guard.protocols.contains_key(protocol) { + guard + .protocols + .insert(protocol.to_string(), Protocol::default()); + } + let x = guard.protocols.get_mut(protocol).unwrap(); + for pair in pairs { + let value = pair.1.to_string(); + match pair.0 { + "ip" => x.ip = value, + "port" => x.port = value, + "name" => x.name = value, + _ => { + HashMap::insert(&mut x.params, pair.0.to_string(), value); + } + } + } + + Ok(()) + } + + fn dubbo_protocol_get(&self, protocol: &str) -> Result { + let guard = self.inner.lock().unwrap(); + if !guard.protocols.contains_key(protocol) { + return Err(anyhow!(ConfigError::ProtocolNotFound(protocol.to_string()))); + } + Ok(guard.protocols.get(protocol).unwrap().clone()) + } + + fn dubbo_registry_ids(&self) -> Option> { + let guard = self.inner.lock().unwrap(); + Some(guard.registries.keys().map(|x| x.to_string()).collect()) + } + + fn dubbo_registry_get(&self, registry_id: &str) -> Result { + let guard = self.inner.lock().unwrap(); + if !guard.registries.contains_key(registry_id) { + return Err(anyhow!(ConfigError::RegistryNotFound( + registry_id.to_string() + ))); + } + Ok(guard.registries.get(registry_id).unwrap().clone()) + } + + fn dubbo_registry_set(&self, registry_id: &str, pairs: Vec<(&str, &str)>) -> Result<(), Error> { + let mut guard = self.inner.lock().unwrap(); + if !guard.registries.contains_key(registry_id) { + guard + .registries + .insert(registry_id.to_string(), Registry::default()); + } + let x = guard.registries.get_mut(registry_id).unwrap(); + for pair in pairs { + let value = pair.1.to_string(); + match pair.0 { + "protocol" => x.protocol = value, + "registry_type" => { + x.registry_type = value.split(",").map(|x| x.to_string()).collect() + } + "address" => x.address = value, + "password" => x.password = value, + "username" => x.username = value, + "timeout" => x.timeout = value, + _ => { + HashMap::insert(&mut x.params, pair.0.to_string(), value); + } + } + } + + Ok(()) + } + + fn dubbo_services_name(&self) -> Option> { + let guard = self.inner.lock().unwrap(); + Some(guard.services.keys().map(|x| x.to_string()).collect()) + } + + fn dubbo_services_get(&self, service_name: &str) -> Result { + let guard = self.inner.lock().unwrap(); + if !guard.services.contains_key(service_name) { + return Err(anyhow!(ConfigError::ServiceNotFound( + service_name.to_string() + ))); + } + Ok(guard.services.get(service_name).unwrap().clone()) + } + + fn dubbo_services_set( + &self, + service_name: &str, + pairs: Vec<(&str, &str)>, + ) -> Result<(), Error> { + let mut guard = self.inner.lock().unwrap(); + if !guard.services.contains_key(service_name) { + guard + .services + .insert(service_name.to_string(), Service::default()); + } + let x = guard.services.get_mut(service_name).unwrap(); + for pair in pairs { + let value = pair.1.to_string(); + match pair.0 { + "protocol" => x.protocol = value, + "interface" => x.interface = value, + "group" => x.group = value, + "version" => x.version = value, + "serialization" => x.serialization = value, + _ => { + return Err(anyhow!(ConfigError::UnsupportedKey( + "services".to_string(), + pair.0.to_string() + ))); + } + } + } + + Ok(()) + } + + fn dubbo_provider_services_name(&self) -> Option> { + let guard = self.inner.lock().unwrap(); + Some( + guard + .provider + .services + .keys() + .map(|x| x.to_string()) + .collect(), + ) + } + + fn dubbo_provider_services_get(&self, service_name: &str) -> Result { + let guard = self.inner.lock().unwrap(); + if !guard.services.contains_key(service_name) { + return Err(anyhow!(ConfigError::ServiceNotFound( + service_name.to_string() + ))); + } + Ok(guard.services.get(service_name).unwrap().clone()) + } + + fn dubbo_provider_services_set( + &self, + service_name: &str, + pairs: Vec<(&str, &str)>, + ) -> Result<(), Error> { + let mut guard = self.inner.lock().unwrap(); + if !guard.provider.services.contains_key(service_name) { + guard + .services + .insert(service_name.to_string(), Service::default()); + } + let x = guard.provider.services.get_mut(service_name).unwrap(); + for pair in pairs { + let value = pair.1.to_string(); + match pair.0 { + "protocol" => x.protocol = value, + "interface" => x.interface = value, + "group" => x.group = value, + "version" => x.version = value, + "serialization" => x.serialization = value, + _ => { + return Err(anyhow!(ConfigError::UnsupportedKey( + "provider.services".to_string(), + pair.0.to_string() + ))); + } + } + } + + Ok(()) + } + + fn dubbo_consumer_references_get(&self, service_name: &str) -> Result { + let guard = self.inner.lock().unwrap(); + if !guard.consumer.references.contains_key(service_name) { + return Err(anyhow!(ConfigError::ServiceNotFound( + service_name.to_string() + ))); + } + Ok(guard.consumer.references.get(service_name).unwrap().clone()) + } + + fn dubbo_consumer_references_set( + &self, + service_name: &str, + pairs: Vec<(&str, &str)>, + ) -> Result<(), Error> { + let mut guard = self.inner.lock().unwrap(); + if !guard.consumer.references.contains_key(service_name) { + guard + .services + .insert(service_name.to_string(), Service::default()); + } + let x = guard.consumer.references.get_mut(service_name).unwrap(); + for pair in pairs { + let value = pair.1.to_string(); + match pair.0 { + "protocol" => x.protocol = value, + "interface" => x.interface = value, + "group" => x.group = value, + "cluster" => x.cluster = value, + "retries" => x.retries = value, + "url" => x.url = value, + "registry_ids" => { + x.registry_ids = value.split(',').map(|x| x.to_string()).collect() + } + _ => { + return Err(anyhow!(ConfigError::UnsupportedKey( + "consumer.references".to_string(), + pair.0.to_string() + ))); + } + } + } + + Ok(()) + } +} diff --git a/config/src/config.rs b/config/src/config.rs deleted file mode 100644 index f63b490c..00000000 --- a/config/src/config.rs +++ /dev/null @@ -1,192 +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, env, path::PathBuf}; - -use crate::{protocol::Protocol, registry::RegistryConfig}; -use logger::tracing; -use once_cell::sync::OnceCell; -use serde::{Deserialize, Serialize}; -use utils::yaml_util::yaml_file_parser; - -use super::{protocol::ProtocolConfig, provider::ProviderConfig, service::ServiceConfig}; - -pub const DUBBO_CONFIG_PATH: &str = "application.yaml"; - -pub static GLOBAL_ROOT_CONFIG: OnceCell = OnceCell::new(); -pub const DUBBO_CONFIG_PREFIX: &str = "dubbo"; - -/// used to storage all structed config, from some source: cmd, file..; -/// Impl Config trait, business init by read Config trait -#[allow(dead_code)] -#[derive(Debug, Default, Serialize, Deserialize, Clone)] -pub struct RootConfig { - #[serde(default)] - pub protocols: ProtocolConfig, - - #[serde(default)] - pub provider: ProviderConfig, - - #[serde(default)] - pub registries: HashMap, - - #[serde(default)] - pub data: HashMap, -} - -pub fn get_global_config() -> &'static RootConfig { - GLOBAL_ROOT_CONFIG.get_or_init(|| { - tracing::debug!("current path: {:?}", env::current_dir()); - RootConfig::new() - .load() - .unwrap_or_else(|err| panic!("Failed to load global config, error: {}", err)) - }) -} - -impl RootConfig { - pub fn new() -> Self { - Self { - protocols: HashMap::new(), - registries: HashMap::new(), - provider: ProviderConfig::new(), - data: HashMap::new(), - } - } - - pub fn load(&self) -> std::io::Result { - let config_path = match env::var("DUBBO_CONFIG_PATH") { - Ok(v) => { - tracing::info!("read config_path from env: {:?}", v); - v - } - Err(err) => { - tracing::warn!( - "error loading config_path: {:?}, use default path: {:?}", - err, - DUBBO_CONFIG_PATH - ); - utils::path_util::app_root_dir() - .join(DUBBO_CONFIG_PATH) - .to_str() - .unwrap() - .to_string() - } - }; - - let conf: HashMap = - yaml_file_parser(PathBuf::new().join(config_path)).unwrap(); - let root_config: RootConfig = conf.get(DUBBO_CONFIG_PREFIX).unwrap().clone(); - tracing::debug!("origin config: {:?}", conf); - Ok(root_config) - } - - pub fn test_config(&mut self) { - let mut provider = ProviderConfig::new(); - provider.protocol_ids = vec!["triple".to_string()]; - provider.registry_ids = vec![]; - - let service_config = ServiceConfig::default() - .group("test".to_string()) - .version("1.0.0".to_string()) - .protocol("triple".to_string()) - .interface("grpc.examples.echo.Echo".to_string()); - - self.provider - .services - .insert("grpc.examples.echo.Echo".to_string(), service_config); - self.provider.services.insert( - "helloworld.Greeter".to_string(), - ServiceConfig::default() - .group("test".to_string()) - .version("1.0.0".to_string()) - .interface("helloworld.Greeter".to_string()) - .protocol("triple".to_string()), - ); - self.protocols.insert( - "triple".to_string(), - Protocol::default() - .name("triple".to_string()) - .ip("0.0.0.0".to_string()) - .port("8889".to_string()), - ); - - self.provider = provider.clone(); - println!("provider config: {:?}", provider); - // 通过环境变量读取某个文件。加在到内存中 - self.data.insert( - "dubbo.provider.url".to_string(), - "dubbo://127.0.0.1:8888/?serviceName=hellworld".to_string(), - ); - // self.data.insert("dubbo.consume.", v) - } - - #[inline] - pub fn leak(self) -> &'static Self { - Box::leak(Box::new(self)) - } -} - -impl Config for RootConfig { - fn bool(&self, key: String) -> bool { - match self.data.get(&key) { - None => false, - Some(val) => match val.parse::() { - Ok(v) => v, - Err(_err) => { - tracing::error!("key: {}, val: {} is not boolean", key, val); - false - } - }, - } - } - - fn string(&self, key: String) -> String { - match self.data.get(&key) { - None => "".to_string(), - Some(val) => val.to_string(), - } - } -} - -pub trait BusinessConfig { - fn init() -> Self; - fn load() -> Result<(), std::convert::Infallible>; -} - -pub trait Config { - fn bool(&self, key: String) -> bool; - fn string(&self, key: String) -> String; -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_config() { - let mut r = RootConfig::new(); - r.test_config(); - } - - #[test] - fn test_load() { - logger::init(); - let r = RootConfig::new(); - let r = r.load().unwrap(); - println!("{:#?}", r); - } -} diff --git a/config/src/error.rs b/config/src/error.rs new file mode 100644 index 00000000..af019435 --- /dev/null +++ b/config/src/error.rs @@ -0,0 +1,31 @@ +/* + * 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 thiserror::Error; +#[derive(Error, Debug)] +pub enum ConfigError { + #[error("protocol {0} not found.")] + ProtocolNotFound(String), + #[error("registry {0} not found.")] + RegistryNotFound(String), + #[error("service {0} not found.")] + ServiceNotFound(String), + #[error("unsupported key {1} for {0}.")] + UnsupportedKey(String, String), + #[error("Service error")] + Unknown, +} diff --git a/config/src/lib.rs b/config/src/lib.rs index 0748c667..55a20caa 100644 --- a/config/src/lib.rs +++ b/config/src/lib.rs @@ -15,10 +15,21 @@ * limitations under the License. */ -pub use config::*; +use once_cell::sync::Lazy; +use std::sync::{Arc, Mutex}; -pub mod config; -pub mod protocol; -pub mod provider; -pub mod registry; -pub mod service; +pub use crate::types::{ConfigWrapper, RootConfig}; +pub use location::resolve_config_location; + +pub mod api; +pub mod error; +pub mod location; +pub mod types; +pub mod util; + +pub(crate) static DUBBO_CONFIG: Lazy = + Lazy::new(|| ConfigWrapper::new(Arc::new(Mutex::new(RootConfig::default())))); + +pub fn get_dubbo_config() -> ConfigWrapper { + DUBBO_CONFIG.clone() +} diff --git a/config/src/location.rs b/config/src/location.rs new file mode 100644 index 00000000..eaa4a3dc --- /dev/null +++ b/config/src/location.rs @@ -0,0 +1,42 @@ +/* + * 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 base::constants::{DEFAULT_CONFIG_FILE, ENV_DUBBO_CONFIG_FILE, ENV_DUBBO_CONFIG_PATH}; +use std::path::PathBuf; +use utils::{env_util::get_env_value, path_util::app_root_dir}; + +// resolve yaml config file +pub fn resolve_config_location() -> PathBuf { + let mut path_buf = PathBuf::new(); + // resolve config path + if get_env_value(ENV_DUBBO_CONFIG_PATH).is_some() { + path_buf = path_buf.join(get_env_value(ENV_DUBBO_CONFIG_PATH).unwrap()); + } else { + path_buf = path_buf.join(app_root_dir()); + } + // resolve config filename + if get_env_value(ENV_DUBBO_CONFIG_FILE).is_some() { + path_buf = path_buf.join(get_env_value(ENV_DUBBO_CONFIG_FILE).unwrap()); + } else { + path_buf = path_buf.join(DEFAULT_CONFIG_FILE); + } + path_buf +} + +pub fn set_config_file_path(path: String) { + std::env::set_var(ENV_DUBBO_CONFIG_PATH, path); +} diff --git a/config/src/protocol.rs b/config/src/protocol.rs deleted file mode 100644 index 86ff0531..00000000 --- a/config/src/protocol.rs +++ /dev/null @@ -1,86 +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 serde::{Deserialize, Serialize}; - -pub const DEFAULT_PROTOCOL: &str = "triple"; - -#[derive(Default, Debug, Clone, Serialize, Deserialize)] -pub struct Protocol { - pub ip: String, - pub port: String, - pub name: String, - - #[serde(skip_serializing, skip_deserializing)] - pub params: HashMap, -} - -pub type ProtocolConfig = HashMap; - -pub trait ProtocolRetrieve { - fn get_protocol(&self, protocol_key: &str) -> Option; - fn get_protocol_or_default(&self, protocol_key: &str) -> Protocol; -} - -impl Protocol { - pub fn name(self, name: String) -> Self { - Self { name, ..self } - } - - pub fn ip(self, ip: String) -> Self { - Self { ip, ..self } - } - - pub fn port(self, port: String) -> Self { - Self { port, ..self } - } - - pub fn params(self, params: HashMap) -> Self { - Self { params, ..self } - } - - pub fn to_url(self) -> String { - format!("{}://{}:{}", self.name, self.ip, self.port) - } -} - -impl ProtocolRetrieve for ProtocolConfig { - fn get_protocol(&self, protocol_key: &str) -> Option { - let result = self.get(protocol_key); - if let Some(..) = result { - Some(result.unwrap().clone()) - } else { - None - } - } - - fn get_protocol_or_default(&self, protocol_key: &str) -> Protocol { - let result = self.get_protocol(protocol_key); - if let Some(..) = result { - result.unwrap() - } else { - let result = self.get_protocol(protocol_key); - if let Some(..) = result { - panic!("default triple base dose not defined.") - } else { - result.unwrap() - } - } - } -} diff --git a/config/src/types/consumer.rs b/config/src/types/consumer.rs new file mode 100644 index 00000000..7cf405ea --- /dev/null +++ b/config/src/types/consumer.rs @@ -0,0 +1,67 @@ +/* + * 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 anyhow::Error; +use std::collections::HashMap; + +use serde::{Deserialize, Serialize}; + +use crate::types::{default::*, ConfigValidator}; +use base::types::alias::{ + ClusterStrategy, FilterKey, GroupId, InterfaceName, ParamKey, ProtocolKey, RegistryId, + ServiceName, +}; + +#[derive(Debug, Default, Serialize, Deserialize, Clone)] +pub struct ConsumerConfig { + #[serde(default)] + pub registry_ids: Vec, + #[serde(default)] + pub filter: FilterKey, + #[serde(default)] + pub protocol_ids: String, + #[serde(default)] + pub references: ReferenceConfig, +} + +pub type ReferenceConfig = HashMap; + +#[derive(Debug, Default, Serialize, Deserialize, Clone)] +pub struct Reference { + #[serde(default)] + pub url: String, + #[serde(default)] + pub protocol: ProtocolKey, + #[serde(default = "default_group_id")] + pub group: GroupId, + #[serde(default)] + pub interface: InterfaceName, + #[serde(default)] + pub registry_ids: Vec, + #[serde(default)] + pub cluster: ClusterStrategy, + #[serde(default)] + pub params: HashMap, + #[serde(default = "default_retries")] + pub retries: String, +} + +impl ConfigValidator for Reference { + fn validate(&self) -> Result<(), Error> { + todo!() + } +} diff --git a/config/src/types/default.rs b/config/src/types/default.rs new file mode 100644 index 00000000..9151fc9e --- /dev/null +++ b/config/src/types/default.rs @@ -0,0 +1,50 @@ +/* + * 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 base::{ + constants::REGISTRY_TYPE_SERVICE, + types::alias::{GroupId, RegistryType, VersionNumber}, +}; +use utils::host_util; + +pub fn default_group_id() -> GroupId { + "default".to_string() +} + +pub fn default_version_number() -> VersionNumber { + "0.1.0".to_string() +} + +pub fn default_retries() -> String { + "3".to_string() +} + +pub fn localhost() -> String { + host_util::local_ip().to_string() +} + +pub fn default_timeout() -> String { + "3000".to_string() +} + +pub fn default_port() -> String { + host_util::scan_free_port(28000).to_string() +} + +pub fn default_registry_type() -> Vec { + vec![REGISTRY_TYPE_SERVICE.to_string()] +} diff --git a/config/src/types/mod.rs b/config/src/types/mod.rs new file mode 100644 index 00000000..afe36de0 --- /dev/null +++ b/config/src/types/mod.rs @@ -0,0 +1,103 @@ +/* + * 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::{ + get_dubbo_config, resolve_config_location, + types::{ + consumer::ConsumerConfig, protocol::ProtocolConfig, provider::ProviderConfig, + registry::RegistryConfig, services::ServicesConfig, + }, + util::yaml_file_parser, +}; +use anyhow::Error; +use base::constants::DUBBO_KEY; +use getset::{CopyGetters, Getters, MutGetters, Setters}; +use serde::{Deserialize, Serialize}; +use std::{ + collections::HashMap, + path::PathBuf, + sync::{Arc, Mutex}, +}; + +pub mod consumer; +pub mod default; +pub mod protocol; +pub mod provider; +pub mod registry; +pub mod services; + +/// used to storage all structed config, from some source: cmd, file..; +/// Impl Config trait, business init by read Config trait +#[allow(dead_code)] +#[derive(Debug, Serialize, Deserialize, Clone, Getters, Setters, MutGetters, CopyGetters)] +pub struct RootConfig { + #[serde(default)] + pub location: PathBuf, + + #[serde(default)] + #[getset(get, set, get_mut)] + pub protocols: ProtocolConfig, + + #[serde(default)] + #[getset(get, set, get_mut)] + pub provider: ProviderConfig, + + #[serde(default)] + #[getset(get, set, get_mut)] + pub registries: RegistryConfig, + + #[serde(default)] + #[getset(get, set, get_mut)] + pub consumer: ConsumerConfig, + + #[serde(default)] + #[getset(get, set, get_mut)] + pub services: ServicesConfig, +} + +impl Default for RootConfig { + fn default() -> RootConfig { + let conf: HashMap = + yaml_file_parser(resolve_config_location()).unwrap(); + let mut root_config: RootConfig = conf.get(DUBBO_KEY).unwrap().clone(); + root_config.location = resolve_config_location(); + root_config + } +} + +impl ConfigWrapper { + pub fn leak_for_read(&self) -> &'static RootConfig { + let dubbo_config = get_dubbo_config(); + let guard = dubbo_config.inner.lock().unwrap(); + Box::leak(Box::new(guard.clone())) + } +} + +#[derive(Clone)] +pub struct ConfigWrapper { + pub inner: Arc>, +} + +impl ConfigWrapper { + pub fn new(inner: Arc>) -> Self { + ConfigWrapper { inner } + } +} + +pub trait ConfigValidator { + fn validate(&self) -> Result<(), Error>; +} diff --git a/config/src/types/protocol.rs b/config/src/types/protocol.rs new file mode 100644 index 00000000..9ef84dfa --- /dev/null +++ b/config/src/types/protocol.rs @@ -0,0 +1,57 @@ +/* + * 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 crate::types::default::*; +use anyhow::Error; +use serde::{Deserialize, Serialize}; + +use base::types::alias::{ParamKey, Port, ProtocolId, ProtocolKey}; + +use crate::types::ConfigValidator; + +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +pub struct Protocol { + #[serde(default = "localhost")] + pub ip: String, + #[serde(default = "default_port")] + pub port: Port, + #[serde(default)] + pub name: ProtocolKey, + + #[serde(skip_serializing, skip_deserializing)] + pub params: HashMap, +} + +pub type ProtocolConfig = HashMap; + +impl ConfigValidator for Protocol { + fn validate(&self) -> Result<(), Error> { + todo!() + } +} + +impl Protocol { + pub fn to_url_string(&self, interface: &str) -> String { + let mut url = format!("{}://{}:{}/{}", self.name, self.ip, self.port, interface); + for (k, v) in self.params.iter() { + url = format!("{}&{}={}", url, k, v); + } + url + } +} diff --git a/config/src/registry.rs b/config/src/types/provider.rs similarity index 78% rename from config/src/registry.rs rename to config/src/types/provider.rs index c2348b07..70b4b8e4 100644 --- a/config/src/registry.rs +++ b/config/src/types/provider.rs @@ -14,12 +14,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +use crate::types::services::ServicesConfig; +use base::types::alias::{ProtocolId, RegistryId}; use serde::{Deserialize, Serialize}; #[derive(Debug, Default, Serialize, Deserialize, Clone)] -pub struct RegistryConfig { +pub struct ProviderConfig { + #[serde(default)] + pub registry_ids: Vec, #[serde(default)] - pub protocol: String, + pub protocol_ids: Vec, #[serde(default)] - pub address: String, + pub services: ServicesConfig, } diff --git a/config/src/provider.rs b/config/src/types/registry.rs similarity index 56% rename from config/src/provider.rs rename to config/src/types/registry.rs index ccb9cf8b..fd1ad384 100644 --- a/config/src/provider.rs +++ b/config/src/types/registry.rs @@ -14,44 +14,36 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - +use anyhow::Error; use std::collections::HashMap; +use crate::types::default::*; use serde::{Deserialize, Serialize}; -use super::service::ServiceConfig; +use crate::types::ConfigValidator; +use base::types::alias::{ParamKey, RegistryId, RegistryType}; +pub type RegistryConfig = HashMap; #[derive(Debug, Default, Serialize, Deserialize, Clone)] -pub struct ProviderConfig { +pub struct Registry { + #[serde(default)] + pub protocol: RegistryId, + #[serde(default = "default_timeout")] + pub timeout: String, + #[serde(default)] + pub address: String, #[serde(default)] - pub registry_ids: Vec, + pub username: String, #[serde(default)] - pub protocol_ids: Vec, + pub password: String, #[serde(default)] - pub services: HashMap, + pub params: HashMap, + #[serde(default = "default_registry_type")] + pub registry_type: Vec, } -impl ProviderConfig { - pub fn new() -> Self { - ProviderConfig { - registry_ids: vec![], - protocol_ids: vec![], - services: HashMap::new(), - } - } - - pub fn with_registry_ids(mut self, registry_ids: Vec) -> Self { - self.registry_ids = registry_ids; - self - } - - pub fn with_protocol_ids(mut self, protocol_ids: Vec) -> Self { - self.protocol_ids = protocol_ids; - self - } - - pub fn with_services(mut self, services: HashMap) -> Self { - self.services = services; - self +impl ConfigValidator for Registry { + fn validate(&self) -> Result<(), Error> { + todo!() } } diff --git a/config/src/service.rs b/config/src/types/services.rs similarity index 52% rename from config/src/service.rs rename to config/src/types/services.rs index 1f85a926..7dcfa530 100644 --- a/config/src/service.rs +++ b/config/src/types/services.rs @@ -15,42 +15,32 @@ * limitations under the License. */ +use crate::types::{default::*, ConfigValidator}; +use anyhow::Error; +use base::types::alias::{ + GroupId, InterfaceName, ProtocolId, SerializationKey, ServiceName, VersionNumber, +}; use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +pub type ServicesConfig = HashMap; #[derive(Debug, Default, Serialize, Deserialize, Clone)] -pub struct ServiceConfig { - pub version: String, - pub group: String, - pub protocol: String, - pub interface: String, +pub struct Service { + #[serde(default = "default_version_number")] + pub version: VersionNumber, + #[serde(default = "default_group_id")] + pub group: GroupId, + #[serde(default)] + pub protocol: ProtocolId, + #[serde(default)] + pub interface: InterfaceName, + #[serde(default)] + pub serialization: SerializationKey, } -impl ServiceConfig { - pub fn interface(self, interface: String) -> Self { - Self { interface, ..self } - } - - pub fn version(self, version: String) -> Self { - Self { version, ..self } - } - - pub fn group(self, group: String) -> Self { - Self { group, ..self } +impl ConfigValidator for Service { + fn validate(&self) -> Result<(), Error> { + todo!() } - - 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/common/utils/src/yaml_util.rs b/config/src/util.rs similarity index 78% rename from common/utils/src/yaml_util.rs rename to config/src/util.rs index f8e0adfe..e32af141 100644 --- a/common/utils/src/yaml_util.rs +++ b/config/src/util.rs @@ -14,7 +14,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -use std::{collections::HashMap, fs, path::PathBuf, sync::Mutex}; +use std::{ + any::{Any, TypeId}, + collections::HashMap, + fs, + path::PathBuf, + sync::Mutex, +}; use anyhow::Error; use once_cell::sync::Lazy; @@ -61,14 +67,21 @@ pub fn yaml_key_reader(path: PathBuf, key: &str) -> Result, Error Ok(Some(value.as_str().unwrap().to_string())) } +pub fn is_empty_value(value: T) -> bool { + if TypeId::of::() == TypeId::of::() { + let value_str = value.to_string(); + value_str.is_empty() || value_str.to_string() == "null" || value_str.to_string() == "0" + } else { + false + } +} + #[cfg(test)] mod tests { use std::collections::HashMap; + use utils::path_util::app_root_dir; - use crate::{ - path_util::app_root_dir, - yaml_util::{yaml_file_parser, yaml_key_reader}, - }; + use crate::util::{is_empty_value, yaml_file_parser, yaml_key_reader}; #[test] fn test_yaml_file_parser() { @@ -76,7 +89,7 @@ mod tests { .join("common") .join("utils") .join("tests") - .join("application.yaml"); + .join("../../dubbo.yaml"); let config = yaml_file_parser::>>(path).unwrap(); println!("{:?}", config); } @@ -87,10 +100,17 @@ mod tests { .join("common") .join("utils") .join("tests") - .join("application.yaml"); + .join("../../dubbo.yaml"); let config = yaml_key_reader(path.clone(), "logging.level").unwrap(); println!("{:?}", config); let config = yaml_key_reader(path, "logging.file.path").unwrap(); println!("{:?}", config); } + + #[test] + fn test_is_empty_value() { + assert!(is_empty_value("0".to_string())); + assert!(is_empty_value("".to_string())); + println!("&str is not empty{}", is_empty_value(0.0)); + } } diff --git a/config/tests/dubbo.yaml b/config/tests/dubbo.yaml new file mode 100644 index 00000000..ddc876eb --- /dev/null +++ b/config/tests/dubbo.yaml @@ -0,0 +1,51 @@ +logging: + level: INFO + file: + path: /tmp/test.log +dubbo: + protocols: + triple: + ip: 0.0.0.0 + port: 8888 + name: tri + dubbo: + ip: 0.0.0.0 + port: 8888 + name: dubbo + registries: + demoZK: + protocol: zookeeper + address: 0.0.0.0:2181 + provider: + registry_ids: + - zk + - demoZK + services: + UserProvider: + serialization: hessian2 + interface: org.apache.dubbo.samples.UserProvider + UserProviderTriple: + serialization: hessian2 + interface: org.apache.dubbo.samples.UserProviderTriple + consumer: + filter: "tracing" + registry_ids: + - demoZK + references: + GreeterClientImpl: + url: tri://localhost:20000 + protocol: tri + retries: 5 + services: + "UserProvider": + registry_ids: + - demoZk + protocol: "dubbo" + interface: "org.apache.dubbo.UserProvider" + loadbalance: "random" + warmup: "100" + cluster: "failover" + methods: + - name: "GetUser" + retries: 1 + loadbalance: "random" \ No newline at end of file diff --git a/config/tests/test_api.rs b/config/tests/test_api.rs new file mode 100644 index 00000000..f55fda74 --- /dev/null +++ b/config/tests/test_api.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. + */ + +#[cfg(test)] +mod tests_api { + use anyhow::Error; + use std::{env, sync::Once}; + + use ctor::ctor; + + use config::{ + api::ConfigApi, + get_dubbo_config, + location::set_config_file_path, + types::{protocol::Protocol, registry::Registry}, + }; + + static INIT: Once = Once::new(); + + #[ctor] + fn setup() { + INIT.call_once(|| { + println!("load config file."); + set_config_file_path(format!( + "{}/{}", + env::current_dir() + .unwrap() + .into_os_string() + .to_str() + .unwrap(), + "tests" + )); + }); + } + + #[test] + fn test_dubbo_protocol_set_overwrite_yaml_by_api() -> Result<(), Error> { + let config_wrapper = get_dubbo_config(); + let old_config = config_wrapper.dubbo_protocol_get("dubbo")?; + assert_eq!(old_config.port, "8888".to_string()); + config_wrapper.dubbo_protocol_set( + "dubbo", + vec![ + ("ip", "122.22.22.22"), + ("port", "111"), + ("name", "dubbo"), + ("nam1e", "dubbo"), + ], + )?; + let new_config: Protocol = config_wrapper.dubbo_protocol_get("dubbo")?; + assert_eq!(new_config.port, "111".to_string()); + assert_eq!(new_config.name, "dubbo".to_string()); + assert_eq!( + new_config.params.get("nam1e").unwrap().clone(), + "dubbo".to_string() + ); + Ok(()) + } + + #[test] + fn test_registry_config() -> Result<(), Error> { + let zk_config: Registry = get_dubbo_config().dubbo_registry_get("demoZK")?; + assert_eq!("zookeeper", zk_config.protocol); + Ok(()) + } + + #[test] + fn test_default_value() -> Result<(), Error> { + let zk_config: Registry = get_dubbo_config().dubbo_registry_get("demoZK")?; + assert_eq!("3000", zk_config.timeout); + Ok(()) + } +} diff --git a/config/tests/test_use_root_dir.rs b/config/tests/test_use_root_dir.rs new file mode 100644 index 00000000..98cd8384 --- /dev/null +++ b/config/tests/test_use_root_dir.rs @@ -0,0 +1,66 @@ +/* + * 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. + */ + +#[cfg(test)] +mod tests_api { + use anyhow::Error; + + use config::{ + api::ConfigApi, + get_dubbo_config, + types::{protocol::Protocol, registry::Registry}, + }; + + #[test] + fn test_dubbo_protocol_set_overwrite_yaml_by_api() -> Result<(), Error> { + let config_wrapper = get_dubbo_config(); + let old_config: Protocol = config_wrapper.dubbo_protocol_get("dubbo")?; + assert_eq!(old_config.port, "8888"); + println!("{}", old_config.ip); + config_wrapper.dubbo_protocol_set( + "dubbo", + vec![ + ("ip", "122.22.22.22"), + ("port", "111"), + ("name", "dubbo"), + ("nam1e", "dubbo"), + ], + )?; + let new_config: Protocol = config_wrapper.dubbo_protocol_get("dubbo")?; + assert_eq!(new_config.port, "111".to_string()); + assert_eq!(new_config.name, "dubbo".to_string()); + assert_eq!( + new_config.params.get("nam1e").unwrap().clone(), + "dubbo".to_string() + ); + Ok(()) + } + + #[test] + fn test_registry_config() -> Result<(), Error> { + let zk_config: Registry = get_dubbo_config().dubbo_registry_get("demoZK")?; + assert_eq!("zookeeper", zk_config.protocol); + Ok(()) + } + + #[test] + fn test_default_value() -> Result<(), Error> { + let zk_config: Registry = get_dubbo_config().dubbo_registry_get("demoZK")?; + assert_eq!("3000", zk_config.timeout); + Ok(()) + } +} diff --git a/application.yaml b/dubbo.yaml similarity index 91% rename from application.yaml rename to dubbo.yaml index d357db14..2a294ad0 100644 --- a/application.yaml +++ b/dubbo.yaml @@ -6,6 +6,9 @@ dubbo: ip: 0.0.0.0 port: '8888' name: tri + dubbo: + port: 8888 + name: dubbo registries: demoZK: protocol: zookeeper diff --git a/dubbo/Cargo.toml b/dubbo/Cargo.toml index a814bd0d..54ef9257 100644 --- a/dubbo/Cargo.toml +++ b/dubbo/Cargo.toml @@ -20,8 +20,8 @@ futures-core = "0.3.23" tokio = { workspace = true, features = ["rt-multi-thread", "time", "fs", "macros", "net", "signal"] } prost = "0.10.4" async-trait = "0.1.56" -tower-layer.workspace = true -bytes.workspace = true +tower-layer = "0.3" +bytes = "1.0" pin-project.workspace = true rand = "0.8.5" serde_json.workspace = true @@ -35,9 +35,7 @@ itertools.workspace = true urlencoding.workspace = true lazy_static.workspace = true base.workspace = true +config.workspace = true logger.workspace = true - -dubbo-config = { path = "../config", version = "0.3.0" } - #对象存储 state = { version = "0.5", features = ["tls"] } diff --git a/dubbo/src/framework.rs b/dubbo/src/framework.rs index f91ee434..8985ae5d 100644 --- a/dubbo/src/framework.rs +++ b/dubbo/src/framework.rs @@ -22,6 +22,11 @@ use std::{ sync::{Arc, Mutex}, }; +use base::Url; +use config::{get_dubbo_config, RootConfig}; +use futures::{future, Future}; +use logger::tracing::{debug, info}; + use crate::{ protocol::{BoxExporter, Protocol}, registry::{ @@ -30,10 +35,6 @@ use crate::{ BoxRegistry, Registry, }, }; -use base::Url; -use dubbo_config::{get_global_config, protocol::ProtocolRetrieve, RootConfig}; -use futures::{future, Future}; -use logger::tracing; // Invoker是否可以基于hyper写一个通用的 @@ -41,7 +42,7 @@ use logger::tracing; pub struct Dubbo { protocols: HashMap>, registries: Option, - service_registry: HashMap>, // registry: Urls + service_registry: HashMap>, config: Option<&'static RootConfig>, } @@ -51,15 +52,10 @@ impl Dubbo { protocols: HashMap::new(), registries: None, service_registry: HashMap::new(), - config: None, + config: Some(get_dubbo_config().leak_for_read()), } } - pub fn with_config(mut self, config: RootConfig) -> Self { - self.config = Some(config.leak()); - self - } - pub fn add_registry(mut self, registry_key: &str, registry: BoxRegistry) -> Self { if self.registries.is_none() { self.registries = Some(Arc::new(Mutex::new(HashMap::new()))); @@ -72,30 +68,27 @@ impl Dubbo { } pub fn init(&mut self) -> Result<(), Box> { - if self.config.is_none() { - self.config = Some(get_global_config()) - } - let root_config = self.config.as_ref().unwrap(); - tracing::debug!("global conf: {:?}", root_config); - // env::set_var("ZOOKEEPER_SERVERS",root_config); + debug!("global conf: {:?}", root_config); for (_, service_config) in root_config.provider.services.iter() { - tracing::info!("init service name: {}", service_config.interface); + info!("init service name: {}", service_config.interface); let url = if root_config .protocols .contains_key(service_config.protocol.as_str()) { - 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(),); - tracing::info!("protocol_url: {:?}", protocol_url); + let protocol = root_config.protocols.get(service_config.protocol.as_str()); + if protocol.is_none() { + return Err(format!("protocol {:?} not exists", service_config.protocol).into()); + } + let protocol_url = protocol + .unwrap() + .to_url_string(service_config.interface.as_str()); + info!("protocol_url: {:?}", protocol_url); Url::from_url(&protocol_url) } else { return Err(format!("base {:?} not exists", service_config.protocol).into()); }; - tracing::info!("url: {:?}", url); + info!("url: {:?}", url); if url.is_none() { continue; } @@ -115,7 +108,7 @@ impl Dubbo { pub async fn start(&mut self) { self.init().unwrap(); - tracing::info!("starting..."); + info!("starting..."); // TODO: server registry let mem_reg = Box::new( RegistryProtocol::new() @@ -125,7 +118,7 @@ impl Dubbo { let mut async_vec: Vec + Send>>> = Vec::new(); for (name, items) in self.protocols.iter() { for url in items.iter() { - tracing::info!("base: {:?}, service url: {:?}", name, url); + info!("base: {:?}, service url: {:?}", name, url); let exporter = mem_reg.clone().export(url.to_owned()); async_vec.push(exporter); //TODO multiple registry diff --git a/dubbo/src/triple/server/triple.rs b/dubbo/src/triple/server/triple.rs index 2c0626ce..cf08b842 100644 --- a/dubbo/src/triple/server/triple.rs +++ b/dubbo/src/triple/server/triple.rs @@ -29,7 +29,6 @@ use crate::{ }, BoxBody, }; -use dubbo_config::BusinessConfig; pub const GRPC_ACCEPT_ENCODING: &str = "grpc-accept-encoding"; pub const GRPC_ENCODING: &str = "grpc-encoding"; @@ -281,13 +280,3 @@ where Ok(compression) } } - -impl BusinessConfig for TripleServer { - fn init() -> Self { - todo!() - } - - fn load() -> Result<(), std::convert::Infallible> { - todo!() - } -} diff --git a/dubbo/src/triple/transport/service.rs b/dubbo/src/triple/transport/service.rs index b306085d..83ffaafa 100644 --- a/dubbo/src/triple/transport/service.rs +++ b/dubbo/src/triple/transport/service.rs @@ -194,7 +194,7 @@ impl DubboServer { // impl BusinessConfig for DubboServer { // fn init() -> Self { -// let conf = config::get_global_config(); +// let conf = config::get_dubbo_config(); // DubboServer::new().with_accpet_http1(conf.bool("dubbo.server.accept_http2".to_string())) // } diff --git a/examples/echo/Cargo.toml b/examples/echo/Cargo.toml index 53f8b9a3..5f9da75a 100644 --- a/examples/echo/Cargo.toml +++ b/examples/echo/Cargo.toml @@ -22,19 +22,19 @@ path = "src/echo/client.rs" [dependencies] http = "0.2" http-body = "0.4.4" -futures-util = {version = "0.3", default-features = false} -tokio = { version = "1.0", features = [ "rt-multi-thread", "time", "fs", "macros", "net", "signal"] } -prost-derive = {version = "0.10", optional = true} +futures-util = { version = "0.3", default-features = false } +tokio = { version = "1.0", features = ["rt-multi-thread", "time", "fs", "macros", "net", "signal"] } +prost-derive = { version = "0.10", optional = true } prost = "0.10.4" async-trait = "0.1.56" tokio-stream = "0.1" -logger.workspace=true +logger.workspace = true -hyper = { version = "0.14.19", features = ["full"]} +hyper = { version = "0.14.19", features = ["full"] } -dubbo = {path = "../../dubbo", version = "0.3.0" } -dubbo-config = {path = "../../config", version = "0.3.0" } -registry-zookeeper.workspace=true +dubbo = { path = "../../dubbo", version = "0.3.0" } +config.workspace = true +registry-zookeeper.workspace = true [build-dependencies] -dubbo-build = {path = "../../dubbo-build", version = "0.3.0" } +dubbo-build = { path = "../../dubbo-build", version = "0.3.0" } diff --git a/examples/greeter/Cargo.toml b/examples/greeter/Cargo.toml index a8c6cacf..8a9925bb 100644 --- a/examples/greeter/Cargo.toml +++ b/examples/greeter/Cargo.toml @@ -30,7 +30,7 @@ async-trait = "0.1.56" tokio-stream = "0.1" logger = { path = "../../common/logger" } dubbo = { path = "../../dubbo", version = "0.3.0" } -dubbo-config = { path = "../../config", version = "0.3.0" } +config.workspace = true registry-zookeeper.workspace = true registry-nacos.workspace = true base.workspace = true diff --git a/examples/greeter/src/greeter/server.rs b/examples/greeter/src/greeter/server.rs index 32931e5f..d82a7deb 100644 --- a/examples/greeter/src/greeter/server.rs +++ b/examples/greeter/src/greeter/server.rs @@ -23,7 +23,6 @@ use tokio::sync::mpsc; use tokio_stream::wrappers::ReceiverStream; use dubbo::{codegen::*, Dubbo}; -use dubbo_config::RootConfig; use logger::{ tracing::{info, span}, Level, @@ -51,14 +50,7 @@ async fn main() { name: "greeter".to_string(), }); let zkr = ZookeeperRegistry::default(); - let r = RootConfig::new(); - let r = match r.load() { - Ok(config) => config, - Err(_err) => panic!("err: {:?}", _err), // response was droped - }; - let mut f = Dubbo::new() - .with_config(r) - .add_registry("zookeeper", Box::new(zkr)); + let mut f = Dubbo::new().add_registry("zookeeper", Box::new(zkr)); f.start().await; }