Skip to main content

nacos_sdk/api/
naming.rs

1use std::fmt::Debug;
2use std::{collections::HashMap, sync::Arc};
3
4use crate::api::plugin;
5use crate::{api::error::Result, naming::NacosNamingService};
6use serde::{Deserialize, Serialize};
7
8use super::props::ClientProps;
9
10const DEFAULT_CLUSTER_NAME: &str = "DEFAULT";
11
12/// ServiceInstance for api.
13#[derive(Clone, Debug, Serialize, Deserialize)]
14#[serde(rename_all = "camelCase")]
15pub struct ServiceInstance {
16    pub instance_id: Option<String>,
17
18    pub ip: String,
19
20    pub port: i32,
21
22    pub weight: f64,
23
24    pub healthy: bool,
25
26    pub enabled: bool,
27
28    pub ephemeral: bool,
29
30    pub cluster_name: Option<String>,
31
32    pub service_name: Option<String>,
33
34    pub metadata: HashMap<String, String>,
35}
36
37impl ServiceInstance {
38    pub fn instance_id(&self) -> Option<&String> {
39        self.instance_id.as_ref()
40    }
41
42    pub fn ip(&self) -> &str {
43        &self.ip
44    }
45
46    pub fn port(&self) -> i32 {
47        self.port
48    }
49
50    pub fn weight(&self) -> f64 {
51        self.weight
52    }
53
54    pub fn healthy(&self) -> bool {
55        self.healthy
56    }
57
58    pub fn enabled(&self) -> bool {
59        self.enabled
60    }
61
62    pub fn ephemeral(&self) -> bool {
63        self.ephemeral
64    }
65
66    pub fn cluster_name(&self) -> Option<&String> {
67        self.cluster_name.as_ref()
68    }
69
70    pub fn service_name(&self) -> Option<&String> {
71        self.service_name.as_ref()
72    }
73
74    pub fn metadata(&self) -> &HashMap<String, String> {
75        &self.metadata
76    }
77
78    pub fn ip_and_port(&self) -> String {
79        format!("{}:{}", self.ip, self.port)
80    }
81
82    pub fn is_same_instance(&self, other: &ServiceInstance) -> bool {
83        self.instance_id == other.instance_id
84            && self.ip == other.ip
85            && self.port == other.port
86            && self.weight == other.weight
87            && self.healthy == other.healthy
88            && self.enabled == other.enabled
89            && self.ephemeral == other.ephemeral
90            && self.cluster_name == other.cluster_name
91            && self.service_name == other.service_name
92            && self.metadata == other.metadata
93    }
94}
95
96impl Default for ServiceInstance {
97    fn default() -> Self {
98        Self {
99            instance_id: Default::default(),
100            ip: Default::default(),
101            port: Default::default(),
102            weight: 1.0,
103            healthy: true,
104            enabled: true,
105            ephemeral: true,
106            cluster_name: Some(DEFAULT_CLUSTER_NAME.to_owned()),
107            service_name: Default::default(),
108            metadata: Default::default(),
109        }
110    }
111}
112
113/// NamingChangeEvent when Instance change.
114#[derive(Clone, Debug)]
115pub struct NamingChangeEvent {
116    pub service_name: String,
117    pub group_name: String,
118    pub clusters: String,
119    pub instances: Option<Vec<ServiceInstance>>,
120}
121
122pub trait InstanceChooser {
123    fn choose(self) -> Option<ServiceInstance>;
124}
125
126/// The NamingEventListener receive an event of [`NamingChangeEvent`].
127pub trait NamingEventListener: Send + Sync + 'static {
128    fn event(&self, event: Arc<NamingChangeEvent>);
129}
130
131/// Api [`NamingService`].
132///
133/// # Examples
134///
135/// ```no_run
136/// # async fn run() -> nacos_sdk::api::error::Result<()> {
137/// let naming_service = nacos_sdk::api::naming::NamingServiceBuilder::new(
138///       nacos_sdk::api::props::ClientProps::new()
139///          .server_addr("127.0.0.1:8848")
140///          // Attention! "public" is "", it is recommended to customize the namespace with clear meaning.
141///          .namespace("")
142///          .app_name("todo-your-app-name"),
143///  )
144///  .build()
145///  .await?;
146/// # Ok(())
147/// # }
148/// ```
149#[doc(alias("naming", "sdk", "api"))]
150#[derive(Clone, Debug)]
151pub struct NamingService {
152    inner: Arc<NacosNamingService>,
153}
154
155impl NamingService {
156    pub async fn register_instance(
157        &self,
158        service_name: String,
159        group_name: Option<String>,
160        service_instance: ServiceInstance,
161    ) -> Result<()> {
162        crate::common::util::check_not_blank(&service_name, "service_name")?;
163        self.inner
164            .register_instance(service_name, group_name, service_instance)
165            .await
166    }
167
168    pub async fn deregister_instance(
169        &self,
170        service_name: String,
171        group_name: Option<String>,
172        service_instance: ServiceInstance,
173    ) -> Result<()> {
174        crate::common::util::check_not_blank(&service_name, "service_name")?;
175        self.inner
176            .deregister_instance(service_name, group_name, service_instance)
177            .await
178    }
179
180    pub async fn batch_register_instance(
181        &self,
182        service_name: String,
183        group_name: Option<String>,
184        service_instances: Vec<ServiceInstance>,
185    ) -> Result<()> {
186        crate::common::util::check_not_blank(&service_name, "service_name")?;
187        self.inner
188            .batch_register_instance(service_name, group_name, service_instances)
189            .await
190    }
191
192    pub async fn get_all_instances(
193        &self,
194        service_name: String,
195        group_name: Option<String>,
196        clusters: Vec<String>,
197        subscribe: bool,
198    ) -> Result<Vec<ServiceInstance>> {
199        crate::common::util::check_not_blank(&service_name, "service_name")?;
200        self.inner
201            .get_all_instances(service_name, group_name, clusters, subscribe)
202            .await
203    }
204
205    pub async fn select_instances(
206        &self,
207        service_name: String,
208        group_name: Option<String>,
209        clusters: Vec<String>,
210        subscribe: bool,
211        healthy: bool,
212    ) -> Result<Vec<ServiceInstance>> {
213        crate::common::util::check_not_blank(&service_name, "service_name")?;
214        self.inner
215            .select_instances(service_name, group_name, clusters, subscribe, healthy)
216            .await
217    }
218
219    pub async fn select_one_healthy_instance(
220        &self,
221        service_name: String,
222        group_name: Option<String>,
223        clusters: Vec<String>,
224        subscribe: bool,
225    ) -> Result<ServiceInstance> {
226        crate::common::util::check_not_blank(&service_name, "service_name")?;
227        self.inner
228            .select_one_healthy_instance(service_name, group_name, clusters, subscribe)
229            .await
230    }
231
232    pub async fn get_service_list(
233        &self,
234        page_no: i32,
235        page_size: i32,
236        group_name: Option<String>,
237    ) -> Result<(Vec<String>, i32)> {
238        self.inner
239            .get_service_list(page_no, page_size, group_name)
240            .await
241    }
242
243    pub async fn subscribe(
244        &self,
245        service_name: String,
246        group_name: Option<String>,
247        clusters: Vec<String>,
248        event_listener: Arc<dyn NamingEventListener>,
249    ) -> Result<()> {
250        crate::common::util::check_not_blank(&service_name, "service_name")?;
251        self.inner
252            .subscribe(service_name, group_name, clusters, event_listener)
253            .await
254    }
255
256    pub async fn unsubscribe(
257        &self,
258        service_name: String,
259        group_name: Option<String>,
260        clusters: Vec<String>,
261        event_listener: Arc<dyn NamingEventListener>,
262    ) -> Result<()> {
263        crate::common::util::check_not_blank(&service_name, "service_name")?;
264        self.inner
265            .unsubscribe(service_name, group_name, clusters, event_listener)
266            .await
267    }
268}
269
270/// Builder of api [`NamingService`].
271///
272/// # Examples
273///
274/// ```no_run
275/// # async fn run() -> nacos_sdk::api::error::Result<()> {
276/// let naming_service = nacos_sdk::api::naming::NamingServiceBuilder::new(
277///       nacos_sdk::api::props::ClientProps::new()
278///          .server_addr("127.0.0.1:8848")
279///          // Attention! "public" is "", it is recommended to customize the namespace with clear meaning.
280///          .namespace("")
281///          .app_name("todo-your-app-name"),
282///  )
283///  .build()
284///  .await?;
285/// # Ok(())
286/// # }
287/// ```
288#[doc(alias("naming", "builder"))]
289pub struct NamingServiceBuilder {
290    client_props: ClientProps,
291    auth_plugin: Option<Arc<dyn plugin::AuthPlugin>>,
292}
293
294impl NamingServiceBuilder {
295    pub fn new(client_props: ClientProps) -> Self {
296        NamingServiceBuilder {
297            client_props,
298            auth_plugin: None,
299        }
300    }
301
302    #[cfg(feature = "auth-by-http")]
303    pub fn enable_auth_plugin_http(self) -> Self {
304        self.with_auth_plugin(Arc::new(plugin::HttpLoginAuthPlugin::default()))
305    }
306
307    #[cfg(feature = "auth-by-aliyun")]
308    pub fn enable_auth_plugin_aliyun(self) -> Self {
309        self.with_auth_plugin(Arc::new(plugin::AliyunRamAuthPlugin::default()))
310    }
311
312    /// Set [`plugin::AuthPlugin`]
313    pub fn with_auth_plugin(mut self, auth_plugin: Arc<dyn plugin::AuthPlugin>) -> Self {
314        self.auth_plugin = Some(auth_plugin);
315        self
316    }
317
318    pub async fn build(self) -> Result<NamingService> {
319        #[cfg(feature = "tracing-log")]
320        {
321            // $HOME/logs/nacos
322            let log_path = crate::common::util::HOME_DIR.to_owned() + "/logs/nacos";
323            let log_level = "INFO".to_string();
324            crate::common::log::init(log_path, log_level);
325        }
326
327        let auth_plugin = match self.auth_plugin {
328            None => Arc::new(plugin::NoopAuthPlugin::default()),
329            Some(plugin) => plugin,
330        };
331        let inner = NacosNamingService::new(self.client_props, auth_plugin).await?;
332        let inner = Arc::new(inner);
333        Ok(NamingService { inner })
334    }
335}
336
337impl Default for NamingServiceBuilder {
338    fn default() -> Self {
339        NamingServiceBuilder {
340            client_props: ClientProps::new(),
341            auth_plugin: None,
342        }
343    }
344}