文章目录
工程版本
<spring-boot.version>2.2.6.RELEASE</spring-boot.version>
<spring-cloud-alibaba.version>2.2.1.RELEASE</spring-cloud-alibaba.version>
<spring-cloud.version>Hoxton.SR4</spring-cloud.version>
@EnableDiscoveryClient
如果需要将服务注册到注册中心,需要在启动类加上@EnableDiscoveryClient
注解,该注解到底有什么作用?
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(EnableDiscoveryClientImportSelector.class)
public @interface EnableDiscoveryClient {
// 默认值为true
// 如果为true,ServiceRegistry将会自动把服务注册到注册中心
boolean autoRegister() default true;
}
从EnableDiscoveryClient
源码可以看出该接口有一个autoRegister()
方法默认返回值是true
,它还引用了EnableDiscoveryClientImportSelector
类。
@Order(Ordered.LOWEST_PRECEDENCE - 100)
public class EnableDiscoveryClientImportSelector
extends SpringFactoryImportSelector<EnableDiscoveryClient> {
// 主要看这个方法
@Override
public String[] selectImports(AnnotationMetadata metadata) {
String[] imports = super.selectImports(metadata);
AnnotationAttributes attributes = AnnotationAttributes.fromMap(
metadata.getAnnotationAttributes(getAnnotationClass().getName(), true));
boolean autoRegister = attributes.getBoolean("autoRegister");
if (autoRegister) {
List<String> importsList = new ArrayList<>(Arrays.asList(imports));
importsList.add(
"org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationConfiguration");
imports = importsList.toArray(new String[0]);
}
else {
Environment env = getEnvironment();
if (ConfigurableEnvironment.class.isInstance(env)) {
ConfigurableEnvironment configEnv = (ConfigurableEnvironment) env;
LinkedHashMap<String, Object> map = new LinkedHashMap<>();
map.put("spring.cloud.service-registry.auto-registration.enabled", false);
MapPropertySource propertySource = new MapPropertySource(
"springCloudDiscoveryClient", map);
configEnv.getPropertySources().addLast(propertySource);
}
}
return imports;
}
@Override
protected boolean isEnabled() {
return getEnvironment().getProperty("spring.cloud.discovery.enabled",
Boolean.class, Boolean.TRUE);
}
@Override
protected boolean hasDefaultFactory() {
return true;
}
}
主要查看selectImports
方法,先获取@EnableDiscoveryClient
注解的autoRegister属性。
当autoRegister=true 时,将AutoServiceRegistrationConfiguration
类添加到自动装配中 ,系统就会去自动装配AutoServiceRegistrationConfiguration
类,在具体的实现中自动装配类都是在这个AutoServiceRegistrationConfiguration
类自动装配完成后才装配的,也就是说autoRegister=true就能够实现服务注册。
当autoRegister=false时,将spring.cloud.service-registry.auto-registration.enabled
设置成了 false,跟注册相关的自动配置类就不会生效,因为自动注册相关配置类都有一个条件装配@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)
。
总结:
默认情况下spring.cloud.service-registry.auto-registration.enabled=true
,因此,只要引入了
spring-cloud-starter-alibaba-nacos-discovery
依赖,即使启动类上不加@EnableDiscoveryClient
注解也会自动向注册中心进行注册。
如果引入了spring-cloud-starter-alibaba-nacos-discovery
依赖,又不想将本服务注册到注册中心,注解需要写成@EnableDiscoveryClient(autoRegister = false)
。
服务注册
Spring Cloud Commons
Spring Cloud 有 Euerka、ZK 等多种注册中心的实现,想要达到实现统一也必须有一套规范,Spring Cloud
Commons 就是约束 Spring Cloud 大部分实现的规范。
spring-cloud-commons
--org.springframework.cloud
--client
--discovery
--loadbalancer
--serviceregistry
Spring Cloud Commons 里面有 Discovery(服务发现)、Load Balancer(负载均衡)、Service Registry(服务注册)等接口的定义。
核心接口
Spring Cloud Commons里面的org.springframework.cloud.client.serviceregistry
包下面有 AutoServiceRegistration
、Registration
、ServiceRegistry
这三个接口,这是服务注册的核心接口。
AutoServiceRegistration
AutoServiceRegistration
用于服务自动注册。自动注册的意思就是,服务启动后自动把服务信息注册到注册中心。
public interface AutoServiceRegistration {
}
AutoServiceRegistration
没有定义方法,它的存在就是要规范实现必须要有自动注册。
Registration
Registration
存储服务信息,用于规范将什么信息注册到注册中心。
public interface Registration extends ServiceInstance {
}
Registration
继承 ServiceInstance
,ServiceInstance
定义了一个服务实例应该具有什么信息。
ServiceInstance
public interface ServiceInstance {
/**
* 实例的唯一标识
* @return The unique instance ID as registered.
*/
default String getInstanceId() {
return null;
}
/**
* 服务名称
* @return The service ID as registered.
*/
String getServiceId();
/**
* 主机地址
* @return The hostname of the registered service instance.
*/
String getHost();
/**
* 端口号
* @return The port of the registered service instance.
*/
int getPort();
/**
* 是否是HTTPS
* @return Whether the port of the registered service instance uses HTTPS.
*/
boolean isSecure();
/**
* 服务URI地址
* @return The service URI address.
*/
URI getUri();
/**
* 服务的元数据信息,如果我们需要给服务携带上其他额外的信息,就可以保存在这个里面
* @return The key / value pair metadata associated with the service instance.
*/
Map<String, String> getMetadata();
/**
* @return The scheme of the service instance.
*/
default String getScheme() {
return null;
}
}
ServiceRegistry
ServiceRegistry
是服务注册接口,用来向注册中心注册服务。
public interface ServiceRegistry<R extends Registration> {
/**
* Registers the registration. A registration typically has information about an
* instance, such as its hostname and port.
* @param registration registration meta data
*/
// 注册服务
void register(R registration);
/**
* Deregisters the registration.
* @param registration registration meta data
*/
// 反注册
void deregister(R registration);
/**
* Closes the ServiceRegistry. This is a lifecycle method.
*/
// 关闭
void close();
/**
* Sets the status of the registration. The status values are determined by the
* individual implementations.
* @param registration The registration to update.
* @param status The status to set.
* @see org.springframework.cloud.client.serviceregistry.endpoint.ServiceRegistryEndpoint
*/
// 设置服务状态
void setStatus(R registration, String status);
/**
* Gets the status of a particular registration.
* @param registration The registration to query.
* @param <T> The type of the status.
* @return The status of the registration.
* @see org.springframework.cloud.client.serviceregistry.endpoint.ServiceRegistryEndpoint
*/
// 获取服务状态
<T> T getStatus(R registration);
}
Nacos服务注册
Nacos将服务注册到注册中心,主要涉及如下几个类:
NacosRegistration
:保存服务的基本数据信息
NacosServiceRegistry
:实现服务注册
NacosServiceRegistryAutoConfiguration
:Nacos自动配置类
NacosRegistration
该类主要是管理服务的一些基本数据,如服务名,服务ip地址等信息。它实现了spring-cloud-commons 提供的Registration
、ServiceInstance
接口。
public class NacosRegistration implements Registration, ServiceInstance {
/**
* The metadata key of management port.
*/
public static final String MANAGEMENT_PORT = "management.port";
/**
* The metadata key of management context-path.
*/
public static final String MANAGEMENT_CONTEXT_PATH = "management.context-path";
/**
* The metadata key of management address.
*/
public static final String MANAGEMENT_ADDRESS = "management.address";
/**
* The metadata key of management endpoints web base path.
*/
public static final String MANAGEMENT_ENDPOINT_BASE_PATH = "management.endpoints.web.base-path";
private NacosDiscoveryProperties nacosDiscoveryProperties;
private ApplicationContext context;
// 构造函数
public NacosRegistration(NacosDiscoveryProperties nacosDiscoveryProperties,
ApplicationContext context) {
this.nacosDiscoveryProperties = nacosDiscoveryProperties;
this.context = context;
}
}
NacosServiceRegistry
该类实现了 spring-cloud-commons 提供的 ServiceRegistry
接口,在register方法中主要是将配置文件封装成Instance
实例,调用了namingService.registerInstance(serviceId, instance)
方法将服务注册到注册中心。
public class NacosServiceRegistry implements ServiceRegistry<Registration> {
private static final Logger log = LoggerFactory.getLogger(NacosServiceRegistry.class);
private final NacosDiscoveryProperties nacosDiscoveryProperties;
// 这个是用来与注册中心进行通信的,如注册服务到nacos注册中心
private final NamingService namingService;
public NacosServiceRegistry(NacosDiscoveryProperties nacosDiscoveryProperties) {
this.nacosDiscoveryProperties = nacosDiscoveryProperties;
this.namingService = nacosDiscoveryProperties.namingServiceInstance();
}
// 服务注册
@Override
public void register(Registration registration) {
if (StringUtils.isEmpty(registration.getServiceId())) {
log.warn("No service to register for nacos client...");
return;
}
String serviceId = registration.getServiceId();
String group = nacosDiscoveryProperties.getGroup();
// 将 Registration 转换成 Instance
Instance instance = getNacosInstanceFromRegistration(registration);
try {
// 将服务注册到注册中心
// 这个方法里面的底层还是发送http请求完成注册
namingService.registerInstance(serviceId, group, instance);
log.info("nacos registry, {} {} {}:{} register finished", group, serviceId,
instance.getIp(), instance.getPort());
}
catch (Exception e) {
log.error("nacos registry, {} register failed...{},", serviceId,
registration.toString(), e);
// rethrow a RuntimeException if the registration is failed.
// issue : https://github.com/alibaba/spring-cloud-alibaba/issues/1132
rethrowRuntimeException(e);
}
}
// 省略其它方法
}
Nacos自动配置类
NacosServiceRegistryAutoConfiguration
自动配置类
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties
// 向nacos注册中心注册服务开关,默认是true
@ConditionalOnNacosDiscoveryEnabled
// 自动注册开关,默认是true
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled",
matchIfMissing = true)
@AutoConfigureAfter({ AutoServiceRegistrationConfiguration.class,
AutoServiceRegistrationAutoConfiguration.class,
NacosDiscoveryAutoConfiguration.class })
public class NacosServiceRegistryAutoConfiguration {
@Bean
public NacosServiceRegistry nacosServiceRegistry(
NacosDiscoveryProperties nacosDiscoveryProperties) {
return new NacosServiceRegistry(nacosDiscoveryProperties);
}
@Bean
@ConditionalOnBean(AutoServiceRegistrationProperties.class)
public NacosRegistration nacosRegistration(
NacosDiscoveryProperties nacosDiscoveryProperties,
ApplicationContext context) {
return new NacosRegistration(nacosDiscoveryProperties, context);
}
@Bean
@ConditionalOnBean(AutoServiceRegistrationProperties.class)
public NacosAutoServiceRegistration nacosAutoServiceRegistration(
NacosServiceRegistry registry,
AutoServiceRegistrationProperties autoServiceRegistrationProperties,
NacosRegistration registration) {
return new NacosAutoServiceRegistration(registry,
autoServiceRegistrationProperties, registration);
}
}
这个自动配置类主要是往容器中注入了NacosServiceRegistry
,NacosRegistration
、NacosAutoServiceRegistration
。
NacosAutoServiceRegistration
是用来触发服务注册行为的。查看NacosAutoServiceRegistration
源码可以发现,NacosAutoServiceRegistration
实现了ApplicationListener
接口,项目启动成功后会调用ApplicationListener
的onApplicationEvent(WebServerInitializedEvent event)
方法,通过这里最终会调用ServiceRegistry.register(R registration)
方法将服务注册到注册中心。
测试类
@RunWith(SpringRunner.class)
@SpringBootTest
public class App1Test {
@Autowired
private SpringClientFactory springClientFactory;
@Autowired
private NacosDiscoveryProperties nacosDiscoveryProperties;
@Autowired
private NacosServiceRegistry nacosServiceRegistry;
@Test
public void test1() throws NacosException {
NamingService namingService = nacosDiscoveryProperties.namingServiceInstance();
List<Instance> allInstances = namingService.getAllInstances("app1");
NamingMaintainService namingMaintainService = nacosDiscoveryProperties.namingMaintainServiceInstance();
for (Instance instance : allInstances) {
instance.addMetadata("username",new Random().nextInt(1000000)+"");
namingMaintainService.updateInstance("app1",instance);
}
}
@Test
public void test2() throws NacosException {
ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer("app1");
}
}