前面介绍了Nacos服务发现(Naming)的客户端的实现,了解到客户端会协助我们与Nacos服务端通信,进行注册、获取服务、心跳等操作。但在实际使用过程中,很少会直接去使用Nacos提供的客户端,官方也提供了与Spring Cloud集成的方式,我们来一起看看服务发现(Naming)的客户端是如何与Spring Cloud结合的。

使用简介

引入依赖

dependencyManagement中添加如下配置。

1
2
3
4
5
6
7
8
9
10
11
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>0.2.1.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

引入 Nacos Discovery Starter。

1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

配置Nacos服务端地址

1
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848

开启服务发现

通过@EnableDiscoveryClient注解开启服务注册与发现功能。

Ribbon

Nacos Discovery Starter默认集成了Ribbon,通过在RestTemplate Bean上增加@LoadBalanced注解或者使用FeignClient即可实现负载均衡。

实现原理

背景知识

自动装配是Spring Boot的一大特性。一般来说,Spring Boot或第三方库会提供一些以Enable开头的注解来供使用者配置开启某些功能,例如@EnableWebMvc@EnableScheduling以及上面的@EnableDiscoveryClient等。
实现的原理是借助@Import注解。@Import注解可导入的内容有三种:

  • 标记有@Configuration的配置类。
  • 实现ImportSelector接口或继承其实现类的类。
  • 实现ImportBeanDefinitionRegistrar接口的类。

更多详细信息如果感兴趣可以自行了解一下。

@EnableDiscoveryClient

与Eureka一样,Nacos同样利用@EnableDiscoveryClient开启服务注册与发现,降低了从Eureka迁移到Nacos的成本。

1
2
3
4
@Import(EnableDiscoveryClientImportSelector.class)
public @interface EnableDiscoveryClient {
boolean autoRegister() default true;
}

@EnableDiscoveryClient导入的类为EnableDiscoveryClientImportSelector,继承自SpringFactoryImportSelectorSpringFactoryImportSelector会读取META-INF/spring.factories,加载其中配置的类。

spring-cloud-alibaba-nacos-discovery中有Nacos Discovery的spring.factories文件,其内容如下。

1
2
3
4
5
6
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.alibaba.nacos.NacosDiscoveryAutoConfiguration,\
org.springframework.cloud.alibaba.nacos.ribbon.RibbonNacosAutoConfiguration,\
org.springframework.cloud.alibaba.nacos.endpoint.NacosDiscoveryEndpointAutoConfiguration
org.springframework.cloud.client.discovery.EnableDiscoveryClient=\
org.springframework.cloud.alibaba.nacos.NacosDiscoveryClientAutoConfiguration

因此,启用自动装配后,会加载NacosDiscoveryAutoConfigurationRibbonNacosAutoConfigurationNacosDiscoveryEndpointAutoConfiguration这三个类,添加@EnableDiscoveryClient后将加载NacosDiscoveryClientAutoConfiguration

NacosDiscoveryClientAutoConfiguration

NacosDiscoveryClientAutoConfiguration会注册DiscoveryClient和NacosDiscoveryProperties两个Bean,当然有一些条件,只有当前不存在DiscoveryClient的Bean并且满足ConditionalOnNacosDiscoveryEnabled时才会注册。
ConditionalOnNacosDiscoveryEnabledspring.cloud.nacos.discovery.enabled配置为true时满足条件,默认为true。换句话来说,我们可以把这个设置为false来停掉Nacos Discovery。
NacosDiscoveryProperties还额外有不存在该Bean时才注册的条件,我们也可以在代码里注册NacosDiscoveryProperties Bean来改变官方对它的实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Configuration
@ConditionalOnMissingBean(DiscoveryClient.class)
@ConditionalOnNacosDiscoveryEnabled
@EnableConfigurationProperties
public class NacosDiscoveryClientAutoConfiguration {

@Bean
public DiscoveryClient nacosDiscoveryClient() {
return new NacosDiscoveryClient();
}

@Bean
@ConditionalOnMissingBean
public NacosDiscoveryProperties nacosProperties() {
return new NacosDiscoveryProperties();
}

}

NacosDiscoveryProperties

NacosDiscoveryProperties的配置加载自application.properties,前缀为spring.cloud.nacos.discovery,现把可配置项总结如下。

Key 默认值 说明
server-addr Nacos Server地址
service ${spring.application.name} 给当前的服务命名
weight 1 权重,1~100,数值越大,权重越大
network-interface 网卡名,默认取第一块网卡的地址
ip 注册的IP地址,默认自动探测
port -1 注册的端口,默认自动探测
namespace 命名空间
access-key 阿里云云账号名
secret-key 阿里云云账号密码
metadata 元数据信息
log-name 日志文件名
enpoint 接入点方式获取服务端地址
namingLoadCacheAtStart false 启动时是否加载本地保存的信息
registerEnabled true 是否注册当前应用
secure false 是否为https

NacosDiscoveryClient

Nacos的DiscoveryClient的实现类是NacosDiscoveryClient,利用Nacos Client对外提供的API实现了DiscoveryClient的获取实例列表和获取所有Service名的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Override
public List<ServiceInstance> getInstances(String serviceId) {
try {
List<Instance> instances = discoveryProperties.namingServiceInstance()
.selectInstances(serviceId, true);
return hostToServiceInstanceList(instances, serviceId);
}
catch (Exception e) {
throw new RuntimeException(
"Can not get hosts from nacos server. serviceId: " + serviceId, e);
}
}

@Override
public List<String> getServices() {

try {
ListView<String> services = discoveryProperties.namingServiceInstance()
.getServicesOfServer(1, Integer.MAX_VALUE);
return services.getData();
}
catch (Exception e) {
LOGGER.error("get service name from nacos server fail,", e);
return Collections.emptyList();
}
}

其他自动装配的配置类

NacosDiscoveryAutoConfiguration

NacosDiscoveryAutoConfiguration用于把当前服务向Nacos服务端注册,会注册3个Bean,分别为NacosServiceRegistry、NacosRegistration、NacosAutoServiceRegistration。如果不想自动向Nacos服务端注册,可通过spring.cloud.service-registry.auto-registration.enabled配置关闭。

NacosRegistration即保存了当前实例的IP、端口等信息,用于向Nacos服务端注册,这些信息都是从上面提到的NacosDiscoveryProperties中获取的。

NacosServiceRegistry实现了具体的注册、注销逻辑,即利用NacosRegistration的信息,通过Nacos Client的API向Nacos服务端注册。

NacosAutoServiceRegistration继承了AbstractAutoServiceRegistration,用于串联NacosRegistration和NacosServiceRegistry进行注册。

RibbonNacosAutoConfiguration

RibbonNacosAutoConfiguration用于集成Ribbon,在ribbon.nacos.enabled配置为true(默认为true)且classpath下有Ribbon相关类时会进行配置,利用@RibbonClients导入NacosRibbonClientConfiguration,通过继承Ribbon的AbstractServerListNacosServerList类,利用Nacos Client的相关API,实现Ribbon的客户端负载均衡。
具体可参照Ribbon的相关文档。

1
2
3
4
5
6
7
8
@Configuration
@EnableConfigurationProperties
@ConditionalOnBean(SpringClientFactory.class)
@ConditionalOnRibbonNacos
@AutoConfigureAfter(RibbonAutoConfiguration.class)
@RibbonClients(defaultConfiguration = NacosRibbonClientConfiguration.class)
public class RibbonNacosAutoConfiguration {
}

NacosDiscoveryEndpointAutoConfiguration

这个类在开启了Endpoint(Spring Boot的Endpoint,并非上面提到的配置Nacos服务端的Endpoint)的情况下,会将NacosDiscoveryEndpoint注册为Bean,并暴露一个名为nacos-discovery的Endpoint,实际调用的是NamingService#getSubscribeServices方法,会返回服务信息。

总结

借助Nacos Discovery Starter,我们无需改代码就可从Eureka迁移到Nacos,实现了服务注册发现、Ribbon负载均衡,从实现上讲,Spring/Spring Boot提供的整体框架还是相当优雅的。