前面介绍了Nacos服务发现(Naming)的客户端的实现,了解到客户端会协助我们与Nacos服务端通信,进行注册、获取服务、心跳等操作。但在实际使用过程中,很少会直接去使用Nacos提供的客户端,官方也提供了与Spring Cloud集成的方式,我们来一起看看服务发现(Naming)的客户端是如何与Spring Cloud结合的。
在 dependencyManagement
中添加如下配置。
<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。
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency>
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
通过 @EnableDiscoveryClient
注解开启服务注册与发现功能。
Nacos Discovery Starter默认集成了Ribbon,通过在RestTemplate Bean上增加 @LoadBalanced
注解或者使用FeignClient即可实现负载均衡。
自动装配是Spring Boot的一大特性。一般来说,Spring Boot或第三方库会提供一些以 Enable
开头的注解来供使用者配置开启某些功能,例如 @EnableWebMvc
、 @EnableScheduling
以及上面的 @EnableDiscoveryClient
等。
实现的原理是借助 @Import
注解。 @Import
注解可导入的内容有三种:
@Configuration ImportSelector ImportBeanDefinitionRegistrar
更多详细信息如果感兴趣可以自行了解一下。
与Eureka一样,Nacos同样利用 @EnableDiscoveryClient
开启服务注册与发现,降低了从Eureka迁移到Nacos的成本。
@Import(EnableDiscoveryClientImportSelector.class) public @interface EnableDiscoveryClient { boolean autoRegister() default true; }
@EnableDiscoveryClient
导入的类为 EnableDiscoveryClientImportSelector
,继承自 SpringFactoryImportSelector
, SpringFactoryImportSelector
会读取 META-INF/spring.factories
,加载其中配置的类。
spring-cloud-alibaba-nacos-discovery
中有Nacos Discovery的 spring.factories
文件,其内容如下。
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
因此,启用自动装配后,会加载 NacosDiscoveryAutoConfiguration
、 RibbonNacosAutoConfiguration
、 NacosDiscoveryEndpointAutoConfiguration
这三个类,添加 @EnableDiscoveryClient
后将加载 NacosDiscoveryClientAutoConfiguration
。
NacosDiscoveryClientAutoConfiguration会注册DiscoveryClient和NacosDiscoveryProperties两个Bean,当然有一些条件,只有当前不存在DiscoveryClient的Bean并且满足 ConditionalOnNacosDiscoveryEnabled
时才会注册。
ConditionalOnNacosDiscoveryEnabled
在 spring.cloud.nacos.discovery.enabled
配置为true时满足条件,默认为true。换句话来说,我们可以把这个设置为false来停掉Nacos Discovery。
NacosDiscoveryProperties还额外有不存在该Bean时才注册的条件,我们也可以在代码里注册NacosDiscoveryProperties Bean来改变官方对它的实现。
@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的配置加载自 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 |
Nacos的DiscoveryClient的实现类是NacosDiscoveryClient,利用Nacos Client对外提供的API实现了DiscoveryClient的获取实例列表和获取所有Service名的方法。
@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用于把当前服务向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用于集成Ribbon,在 ribbon.nacos.enabled
配置为true(默认为true)且classpath下有Ribbon相关类时会进行配置,利用 @RibbonClients
导入 NacosRibbonClientConfiguration
,通过继承Ribbon的 AbstractServerList
的 NacosServerList
类,利用Nacos Client的相关API,实现Ribbon的客户端负载均衡。
具体可参照Ribbon的相关文档。
@Configuration @EnableConfigurationProperties @ConditionalOnBean(SpringClientFactory.class) @ConditionalOnRibbonNacos @AutoConfigureAfter(RibbonAutoConfiguration.class) @RibbonClients(defaultConfiguration = NacosRibbonClientConfiguration.class) public class RibbonNacosAutoConfiguration { }
这个类在开启了Endpoint(Spring Boot的Endpoint,并非上面提到的配置Nacos服务端的Endpoint)的情况下,会将 NacosDiscoveryEndpoint
注册为Bean,并暴露一个名为 nacos-discovery
的Endpoint,实际调用的是 NamingService#getSubscribeServices
方法,会返回服务信息。
借助Nacos Discovery Starter,我们无需改代码就可从Eureka迁移到Nacos,实现了服务注册发现、Ribbon负载均衡,从实现上讲,Spring/Spring Boot提供的整体框架还是相当优雅的。