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#[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#[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
126pub trait NamingEventListener: Send + Sync + 'static {
128 fn event(&self, event: Arc<NamingChangeEvent>);
129}
130
131#[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#[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 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 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}