在Spring Cloud源码分析:EnableDiscoveryClient与EnableEurekaClient的区别(Edgware版本)提到:
在Spring Cloud的Dalston及其之前的版本中:
- 从2014年的Spring Cloud 1.0.0.RC1版本开始,官方就推荐使用
EnableDiscoveryClient来取代EnableEurekaClient
; - EnableEurekaClient源码中使用了注解EnableDiscoveryClient,因此如果要使用eureka的注册发现服务,两者功能是一样的;
- EnableDiscoveryClient注解在spring.factories配置中通过配置项EurekaDiscoveryClientConfiguration来开启服务注册发现功能;
在Dalston之后的版本中(不含Dalston):
- 在spring.factories配置中,配置类EurekaDiscoveryClientConfiguration被配置到springboot的自动配置注解中,与EnableDiscoveryClient注解没有关系了,也就是说只要开启了springboot的自动配置,服务注册发现功能就会启用;
- EnableEurekaClient源码中没有使用注解EnableDiscoveryClient,此时EnableEurekaClient已经没用了;
本文来探究一下Zookeeper是怎么做服务注册发现的
老规矩:打开spring.factories
版本:spring-cloud-zookeeper-discovery-2.1.0.RELEASE.jar
既然EurekaDiscoveryClientConfiguration是Eureka用来做服务注册发现的,那么zookeeper不难猜出是ZookeeperDiscoveryAutoConfiguration
ZookeeperDiscoveryAutoConfiguration源码概览
/*
* Copyright 2013-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.zookeeper.discovery;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.x.discovery.ServiceDiscovery;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.autoconfigure.health.ConditionalOnEnabledHealthIndicator;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cloud.client.CommonsClientAutoConfiguration;
import org.springframework.cloud.client.discovery.noop.NoopDiscoveryClientAutoConfiguration;
import org.springframework.cloud.commons.util.InetUtils;
import org.springframework.cloud.zookeeper.discovery.dependency.ZookeeperDependencies;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author Spencer Gibb
* @since 1.1.0
*/
@Configuration
@ConditionalOnBean(ZookeeperDiscoveryClientConfiguration.Marker.class)
@ConditionalOnZookeeperDiscoveryEnabled
@AutoConfigureBefore({CommonsClientAutoConfiguration.class, NoopDiscoveryClientAutoConfiguration.class})
@AutoConfigureAfter({ZookeeperDiscoveryClientConfiguration.class})
public class ZookeeperDiscoveryAutoConfiguration {
@Autowired(required = false)
private ZookeeperDependencies zookeeperDependencies;
@Autowired
private CuratorFramework curator;
@Bean
@ConditionalOnMissingBean
public ZookeeperDiscoveryProperties zookeeperDiscoveryProperties(InetUtils inetUtils) {
return new ZookeeperDiscoveryProperties(inetUtils);
}
@Bean
@ConditionalOnMissingBean
// currently means auto-registration is false. That will change when ZookeeperServiceDiscovery is gone
public ZookeeperDiscoveryClient zookeeperDiscoveryClient(
ServiceDiscovery<ZookeeperInstance> serviceDiscovery,
ZookeeperDiscoveryProperties zookeeperDiscoveryProperties) {
return new ZookeeperDiscoveryClient(serviceDiscovery, this.zookeeperDependencies,
zookeeperDiscoveryProperties);
}
@Configuration
@ConditionalOnEnabledHealthIndicator("zookeeper")
@ConditionalOnClass(Endpoint.class)
protected static class ZookeeperDiscoveryHealthConfig {
@Autowired(required = false)
private ZookeeperDependencies zookeeperDependencies;
@Bean
@ConditionalOnMissingBean
public ZookeeperDiscoveryHealthIndicator zookeeperDiscoveryHealthIndicator(
CuratorFramework curatorFramework,
ServiceDiscovery<ZookeeperInstance> serviceDiscovery,
ZookeeperDiscoveryProperties properties) {
return new ZookeeperDiscoveryHealthIndicator(curatorFramework,
serviceDiscovery, this.zookeeperDependencies, properties);
}
}
@Bean
public ZookeeperServiceWatch zookeeperServiceWatch(ZookeeperDiscoveryProperties zookeeperDiscoveryProperties) {
return new ZookeeperServiceWatch(this.curator, zookeeperDiscoveryProperties);
}
}
1.@ConditionalOnBean(ZookeeperDiscoveryClientConfiguration.Marker.class)
@Configuration
@ConditionalOnProperty(value = "spring.cloud.zookeeper.discovery.enabled", matchIfMissing = true)
public class ZookeeperDiscoveryClientConfiguration {
是否开启zk服务发现功能,默认开启
2.@ConditionalOnZookeeperDiscoveryEnabled
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@ConditionalOnZookeeperEnabled
@ConditionalOnProperty(value = "spring.cloud.zookeeper.discovery.enabled", matchIfMissing = true)
public @interface ConditionalOnZookeeperDiscoveryEnabled {
}
相比上一个注解,增加了新的注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@ConditionalOnProperty(value = "spring.cloud.zookeeper.enabled", matchIfMissing = true)
public @interface ConditionalOnZookeeperEnabled {
}
要求spring.cloud.zookeeper.enabled属性为true,默认为true
3.@AutoConfigureBefore({CommonsClientAutoConfiguration.class, NoopDiscoveryClientAutoConfiguration.class})
要求在NoopDiscoveryClientAutoConfiguration.class和CommonsClientAutoConfiguration.class配置之前进行配置
4.@AutoConfigureAfter({ZookeeperDiscoveryClientConfiguration.class})
要求在ZookeeperDiscoveryClientConfiguration配置之后进行配置
5.ZookeeperDiscoveryAutoConfiguration注册了ZookeeperDiscoveryProperties、ZookeeperDiscoveryClient、ZookeeperServiceWatch、ZookeeperDiscoveryHealthIndicator
ZookeeperDiscoveryProperties
ZookeeperDiscoveryProperties定义了enabled、root、uriSpec、instanceId、instanceHost、instanceIpAddress、preferIpAddress、instancePort、instanceSslPort、register、metadata、initialStatus、order属性
@ConfigurationProperties("spring.cloud.zookeeper.discovery")
public class ZookeeperDiscoveryProperties {
public static final String DEFAULT_URI_SPEC = "{scheme}://{address}:{port}";
private InetUtils.HostInfo hostInfo;
private boolean enabled = true;
/**
* Root Zookeeper folder in which all instances are registered
*/
private String root = "/services";
/**
* The URI specification to resolve during service registration in Zookeeper
*/
private String uriSpec = DEFAULT_URI_SPEC;
/** Id used to register with zookeeper. Defaults to a random UUID. */
private String instanceId;
/**
* Predefined host with which a service can register itself in Zookeeper. Corresponds
* to the {code address} from the URI spec.
*/
private String instanceHost;
/** IP address to use when accessing service (must also set preferIpAddress
to use) */
private String instanceIpAddress;
/**
* Use ip address rather than hostname during registration
*/
private boolean preferIpAddress = false;
/** Port to register the service under (defaults to listening port) */
private Integer instancePort;
/** Ssl port of the registered service. */
private Integer instanceSslPort;
/**
* Register as a service in zookeeper.
*/
private boolean register = true;
/**
* Gets the metadata name/value pairs associated with this instance. This information
* is sent to zookeeper and can be used by other instances.
*/
private Map<String, String> metadata = new HashMap<>();
/**
* The initial status of this instance (defaults to {@link StatusConstants#STATUS_UP}).
*/
private String initialStatus = StatusConstants.STATUS_UP;
/**
* Order of the discovery client used by `CompositeDiscoveryClient` for sorting available clients.
*/
private int order = 0;
// Visible for Testing
protected ZookeeperDiscoveryProperties() {}
public ZookeeperDiscoveryProperties(InetUtils inetUtils) {
this.hostInfo = inetUtils.findFirstNonLoopbackHostInfo();
this.instanceHost = this.hostInfo.getHostname();
this.instanceIpAddress = this.hostInfo.getIpAddress();
}
}
该类中就包括了服务的一些基本信息以及后面创建zk节点的默认根节点("/services")
ZookeeperDiscoveryClient
public class ZookeeperDiscoveryClient implements DiscoveryClient {
private static final Log log = LogFactory.getLog(ZookeeperDiscoveryClient.class);
private final ZookeeperDependencies zookeeperDependencies;
private final ServiceDiscovery<ZookeeperInstance> serviceDiscovery;
private final ZookeeperDiscoveryProperties zookeeperDiscoveryProperties;
public ZookeeperDiscoveryClient(ServiceDiscovery<ZookeeperInstance> serviceDiscovery,
ZookeeperDependencies zookeeperDependencies,
ZookeeperDiscoveryProperties zookeeperDiscoveryProperties) {
this.serviceDiscovery = serviceDiscovery;
this.zookeeperDependencies = zookeeperDependencies;
this.zookeeperDiscoveryProperties = zookeeperDiscoveryProperties;
}
@Override
public String description() {
return "Spring Cloud Zookeeper Discovery Client";
}
private static org.springframework.cloud.client.ServiceInstance createServiceInstance(String serviceId, ServiceInstance<ZookeeperInstance> serviceInstance) {
return new ZookeeperServiceInstance(serviceId, serviceInstance);
}
@Override
public List<org.springframework.cloud.client.ServiceInstance> getInstances(
final String serviceId) {
try {
if (getServiceDiscovery() == null) {
return Collections.EMPTY_LIST;
}
String serviceIdToQuery = getServiceIdToQuery(serviceId);
Collection<ServiceInstance<ZookeeperInstance>> zkInstances = getServiceDiscovery().queryForInstances(serviceIdToQuery);
List<org.springframework.cloud.client.ServiceInstance> instances = new ArrayList<>();
for (ServiceInstance<ZookeeperInstance> instance : zkInstances) {
instances.add(createServiceInstance(serviceIdToQuery, instance));
}
return instances;
} catch (KeeperException.NoNodeException e) {
if (log.isDebugEnabled()) {
log.debug("Error getting instances from zookeeper. Possibly, no service has registered.", e);
}
// this means that nothing has registered as a service yes
return Collections.emptyList();
} catch (Exception exception) {
rethrowRuntimeException(exception);
}
return new ArrayList<>();
}
private ServiceDiscovery<ZookeeperInstance> getServiceDiscovery() {
return this.serviceDiscovery;
}
private String getServiceIdToQuery(String serviceId) {
if (this.zookeeperDependencies != null && this.zookeeperDependencies.hasDependencies()) {
String pathForAlias = this.zookeeperDependencies.getPathForAlias(serviceId);
return pathForAlias.isEmpty() ? serviceId : pathForAlias;
}
return serviceId;
}
@Override
public List<String> getServices() {
List<String> services = null;
if (getServiceDiscovery() == null) {
log.warn("Service Discovery is not yet ready - returning empty list of services");
return Collections.emptyList();
}
try {
Collection<String> names = getServiceDiscovery().queryForNames();
if (names == null) {
return Collections.emptyList();
}
services = new ArrayList<>(names);
}
catch (KeeperException.NoNodeException e) {
if (log.isDebugEnabled()) {
log.debug("Error getting services from zookeeper. Possibly, no service has registered.", e);
}
// this means that nothing has registered as a service yes
return Collections.emptyList();
}
catch (Exception e) {
rethrowRuntimeException(e);
}
return services;
}
@Override
public int getOrder() {
return this.zookeeperDiscoveryProperties.getOrder();
}
}
ZookeeperDiscoveryClient实现了org.springframework.cloud.client.discovery.DiscoveryClient
接口
public interface DiscoveryClient extends Ordered {
int DEFAULT_ORDER = 0;
/**
* A human-readable description of the implementation, used in HealthIndicator.
*
* @return The description.
*/
String description();
/**
* Gets all ServiceInstances associated with a particular serviceId.
*
* @param serviceId The serviceId to query.
* @return A List of ServiceInstance.
*/
List<ServiceInstance> getInstances(String serviceId);
/**
* @return All known service IDs.
*/
List<String> getServices();
/**
* Default implementation for getting order of discovery clients.
*
* @return order
*/
@Override
default int getOrder() {
return DEFAULT_ORDER;
}
}
ZookeeperDiscoveryClient的具体实现:(对应的还有EnableDiscoveryClient等等)
getInstances使用curator的ServiceDiscovery.queryForInstances获取服务实例信息,然后转换为org.springframework.cloud.client.ServiceInstance类型;
for (ServiceInstance<ZookeeperInstance> instance : zkInstances) {
instances.add(createServiceInstance(serviceIdToQuery, instance));
}
private static org.springframework.cloud.client.ServiceInstance createServiceInstance(String serviceId, ServiceInstance<ZookeeperInstance> serviceInstance) {
return new ZookeeperServiceInstance(serviceId, serviceInstance);
}
public class ZookeeperServiceInstance implements ServiceInstance
getServices则是使用curator的ServiceDiscovery.queryForNames获取服务名信息
ZookeeperDiscoveryHealthIndicator
public class ZookeeperDiscoveryHealthIndicator implements DiscoveryHealthIndicator {
private static final Log log = LogFactory
.getLog(ZookeeperDiscoveryHealthIndicator.class);
private CuratorFramework curatorFramework;
private ServiceDiscovery<ZookeeperInstance> serviceDiscovery;
private final ZookeeperDependencies zookeeperDependencies;
private final ZookeeperDiscoveryProperties zookeeperDiscoveryProperties;
public ZookeeperDiscoveryHealthIndicator(CuratorFramework curatorFramework,
ServiceDiscovery<ZookeeperInstance> serviceDiscovery,
ZookeeperDependencies zookeeperDependencies,
ZookeeperDiscoveryProperties zookeeperDiscoveryProperties) {
this.curatorFramework = curatorFramework;
this.serviceDiscovery = serviceDiscovery;
this.zookeeperDependencies = zookeeperDependencies;
this.zookeeperDiscoveryProperties = zookeeperDiscoveryProperties;
}
@Override
public String getName() {
return "zookeeper";
}
@Override
public Health health() {
Health.Builder builder = Health.unknown();
try {
Iterable<ServiceInstance<ZookeeperInstance>> allInstances =
new ZookeeperServiceInstances(this.curatorFramework,
this.serviceDiscovery, this.zookeeperDependencies,
this.zookeeperDiscoveryProperties);
builder.up().withDetail("services", allInstances);
}
catch (Exception e) {
log.error("Error", e);
builder.down(e);
}
return builder.build();
}
}
ZookeeperDiscoveryHealthIndicator实现了DiscoveryHealthIndicator接口,其health方法创建ZookeeperServiceInstances
ZookeeperServiceInstances
public class ZookeeperServiceInstances
implements Iterable<ServiceInstance<ZookeeperInstance>> {
private static final Log log = LogFactory.getLog(ZookeeperServiceInstances.class);
private ServiceDiscovery<ZookeeperInstance> serviceDiscovery;
private final ZookeeperDependencies zookeeperDependencies;
private final ZookeeperDiscoveryProperties zookeeperDiscoveryProperties;
private final List<ServiceInstance<ZookeeperInstance>> allInstances;
private final CuratorFramework curator;
public ZookeeperServiceInstances(CuratorFramework curator,
ServiceDiscovery<ZookeeperInstance> serviceDiscovery,
ZookeeperDependencies zookeeperDependencies,
ZookeeperDiscoveryProperties zookeeperDiscoveryProperties) {
this.curator = curator;
this.serviceDiscovery = serviceDiscovery;
this.zookeeperDependencies = zookeeperDependencies;
this.zookeeperDiscoveryProperties = zookeeperDiscoveryProperties;
this.allInstances = getZookeeperInstances();
}
private List<ServiceInstance<ZookeeperInstance>> getZookeeperInstances() {
ArrayList<ServiceInstance<ZookeeperInstance>> allInstances = new ArrayList<>();
try {
Collection<String> namesToQuery = getNamesToQuery();
if (log.isDebugEnabled()) {
log.debug("Querying the following names [" + namesToQuery + "]");
}
for (String name : namesToQuery) {
allInstances.addAll(nestedInstances(allInstances, name));
}
return allInstances;
}
catch (Exception e) {
log.debug("Exception occurred while trying to build the list of instances",
e);
return allInstances;
}
}
private List<ServiceInstance<ZookeeperInstance>> nestedInstances(
List<ServiceInstance<ZookeeperInstance>> accumulator, String name)
throws Exception {
String parentPath = prepareQueryName(name);
Collection<ServiceInstance<ZookeeperInstance>> childrenInstances = tryToGetInstances(
parentPath);
if (childrenInstances != null) {
return convertCollectionToList(childrenInstances);
}
try {
List<String> children = this.curator.getChildren().forPath(parentPath);
return iterateOverChildren(accumulator, parentPath, children);
} catch (Exception e) {
if (log.isTraceEnabled()) {
log.trace("Exception occurred while trying to retrieve children of [" + parentPath + "]", e);
}
return injectZookeeperServiceInstances(accumulator, parentPath);
}
}
private String prepareQueryName(String name) {
String root = this.zookeeperDiscoveryProperties.getRoot();
return name.startsWith(root) ? name : root + name;
}
private Collection<ServiceInstance<ZookeeperInstance>> tryToGetInstances(
String path) {
try {
return getServiceDiscovery()
.queryForInstances(getPathWithoutRoot(path));
}
catch (Exception e) {
log.trace("Exception occurred while trying to retrieve instances of [" + path
+ "]", e);
return null;
}
}
private ServiceDiscovery<ZookeeperInstance> getServiceDiscovery() {
return this.serviceDiscovery;
}
private String getPathWithoutRoot(String path) {
return path.substring(this.zookeeperDiscoveryProperties.getRoot().length());
}
private List<ServiceInstance<ZookeeperInstance>> injectZookeeperServiceInstances(
List<ServiceInstance<ZookeeperInstance>> accumulator, String name)
throws Exception {
Collection<ServiceInstance<ZookeeperInstance>> instances = getServiceDiscovery().queryForInstances(name);
accumulator.addAll(convertCollectionToList(instances));
return accumulator;
}
private List<ServiceInstance<ZookeeperInstance>> convertCollectionToList(
Collection<ServiceInstance<ZookeeperInstance>> instances) {
List<ServiceInstance<ZookeeperInstance>> serviceInstances = new ArrayList<>();
for (ServiceInstance<ZookeeperInstance> instance : instances) {
serviceInstances.add(instance);
}
return serviceInstances;
}
private List<ServiceInstance<ZookeeperInstance>> iterateOverChildren(
List<ServiceInstance<ZookeeperInstance>> accumulator, String parentPath,
List<String> children) throws Exception {
List<ServiceInstance<ZookeeperInstance>> lists = new ArrayList<>();
for (String child : children) {
lists.addAll(nestedInstances(accumulator, parentPath + "/" + child));
}
return lists;
}
private Collection<String> getNamesToQuery() throws Exception {
if (this.zookeeperDependencies == null) {
if (log.isDebugEnabled()) {
log.debug("Using direct name resolution instead of dependency based one");
}
List<String> names = new ArrayList<>();
for (String name : getServiceDiscovery().queryForNames()) {
names.add(sanitize(name));
}
return names;
}
if (log.isDebugEnabled()) {
log.debug("Using dependency based names to query");
}
return this.zookeeperDependencies.getDependencyNames();
}
@Override
public Iterator<ServiceInstance<ZookeeperInstance>> iterator() {
return this.allInstances.iterator();
}
}
ZookeeperServiceInstances的构造器会调用getZookeeperInstances拉取ServiceInstance
ServiceInstance记录id,port,address等信息
public class ServiceInstance<T>
{
private final String name;
private final String id;
private final String address;
private final Integer port;
private final Integer sslPort;
private final T payload;
private final long registrationTimeUTC;
private final ServiceType serviceType;
private final UriSpec uriSpec;
private final boolean enabled;
}
ZookeeperServiceWatch
public class ZookeeperServiceWatch implements
ApplicationListener<InstanceRegisteredEvent<?>>, TreeCacheListener,
ApplicationEventPublisherAware {
private final CuratorFramework curator;
private final ZookeeperDiscoveryProperties properties;
private final AtomicLong cacheChange = new AtomicLong(0);
private ApplicationEventPublisher publisher;
private TreeCache cache;
public ZookeeperServiceWatch(CuratorFramework curator,
ZookeeperDiscoveryProperties properties) {
this.curator = curator;
this.properties = properties;
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
public TreeCache getCache() {
return this.cache;
}
@Override
public void onApplicationEvent(InstanceRegisteredEvent<?> event) {
this.cache = TreeCache.newBuilder(this.curator, this.properties.getRoot()).build();
this.cache.getListenable().addListener(this);
try {
this.cache.start();
}
catch (Exception e) {
ReflectionUtils.rethrowRuntimeException(e);
}
}
@PreDestroy
public void stop() throws Exception {
if (this.cache != null) {
this.cache.close();
}
}
@Override
public void childEvent(CuratorFramework client, TreeCacheEvent event) throws Exception {
if (event.getType().equals(TreeCacheEvent.Type.NODE_ADDED)
|| event.getType().equals(TreeCacheEvent.Type.NODE_REMOVED)
|| event.getType().equals(TreeCacheEvent.Type.NODE_UPDATED)) {
long newCacheChange = this.cacheChange.incrementAndGet();
this.publisher.publishEvent(new HeartbeatEvent(this, newCacheChange));
}
}
}
ZookeeperServiceWatch实现了ApplicationListener、TreeCacheListener、ApplicationEventPublisherAware接口;其childEvent方法在event类型是NODE_ADDED、NODE_REMOVED、NODE_UPDATED类型时会发布HeartbeatEvent事件
至此,ZookeeperDiscoveryAutoConfiguration这个配置类分析完毕为服务发现提供基础能力,spring.factories中我们还注意到一个配置类:ZookeeperAutoServiceRegistrationAutoConfiguration,而这里是真正执行服务注册的地方
ZookeeperAutoServiceRegistrationAutoConfiguration
zookeeper服务自动注册自动配置类
@Configuration
@ConditionalOnMissingBean(type = "org.springframework.cloud.zookeeper.discovery.ZookeeperLifecycle")
@ConditionalOnZookeeperDiscoveryEnabled
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)
@AutoConfigureAfter( { ZookeeperServiceRegistryAutoConfiguration.class} )
@AutoConfigureBefore( {AutoServiceRegistrationAutoConfiguration.class,ZookeeperDiscoveryAutoConfiguration.class} )
public class ZookeeperAutoServiceRegistrationAutoConfiguration {
@Bean
public ZookeeperAutoServiceRegistration zookeeperAutoServiceRegistration(
ZookeeperServiceRegistry registry, ZookeeperRegistration registration,
ZookeeperDiscoveryProperties properties) {
return new ZookeeperAutoServiceRegistration(registry, registration, properties);
}
@Bean
@ConditionalOnMissingBean(ZookeeperRegistration.class)
public ServiceInstanceRegistration serviceInstanceRegistration(
ApplicationContext context, ZookeeperDiscoveryProperties properties) {
String appName = context.getEnvironment().getProperty("spring.application.name",
"application");
String host = properties.getInstanceHost();
if (!StringUtils.hasText(host)) {
throw new IllegalStateException("instanceHost must not be empty");
}
ZookeeperInstance zookeeperInstance = new ZookeeperInstance(context.getId(),
appName, properties.getMetadata());
RegistrationBuilder builder = ServiceInstanceRegistration.builder().address(host)
.name(appName).payload(zookeeperInstance)
.uriSpec(properties.getUriSpec());
if (properties.getInstanceSslPort() != null) {
builder.sslPort(properties.getInstanceSslPort());
}
if (properties.getInstanceId() != null) {
builder.id(properties.getInstanceId());
}
// TODO add customizer?
return builder.build();
}
}
服务启动之后,会先在这个类中根据application.yml文件中的配置内容生成一个ServiceInstanceRegistration服务实例注册类;
这个类中包含了两个对象:
ServiceInstance服务实例对象
ServiceInstanceBuilder服务实例构造对象
ServiceInstance服务实例中的属性就包含了服务的ip,port,name以及zk节点属性等属性;后面会根据这些信息创建一个zk节点
public class ServiceInstance<T> {
private final String name; 服务名
private final String id; 服务id
private final String address; 服务地址(不填默认取主机名)
private final Integer port; 服务端口
private final Integer sslPort; https协议端口
private final T payload; 其实就是zookeeperinstance对象
private final long registrationTimeUTC; 注册时间
private final ServiceType serviceType; 生成的zk节点属性(默认是EPHEMERAL)
private final UriSpec uriSpec; 包括了ip+端口
private final boolean enabled; 该服务实例是否可用
}
ZookeeperInstance zk实例中包含了id,name以及metadata属性
public class ZookeeperInstance {
private String id;
private String name;
private Map<String, String> metadata = new HashMap();
}
ZookeeperAutoServiceRegistration
ZookeeperAutoServiceRegistration zk节点自动注册服务;
@Bean
public ZookeeperAutoServiceRegistration zookeeperAutoServiceRegistration(
ZookeeperServiceRegistry registry, ZookeeperRegistration registration,
ZookeeperDiscoveryProperties properties) {
return new ZookeeperAutoServiceRegistration(registry, registration, properties);
}
看一下这个类
public ZookeeperAutoServiceRegistration(ZookeeperServiceRegistry registry,
ZookeeperRegistration registration,
ZookeeperDiscoveryProperties properties) {
this(registry, registration, properties, null);
}
public ZookeeperAutoServiceRegistration(ZookeeperServiceRegistry registry,
ZookeeperRegistration registration,
ZookeeperDiscoveryProperties properties,
AutoServiceRegistrationProperties arProperties) {
super(registry, arProperties);
this.registration = registration;
this.properties = properties;
if (this.properties.getInstancePort() != null) {
this.registration.setPort(this.properties.getInstancePort());
}
}
我们需要关注的是该类下有一个register()方法
protected void register() {
if (!this.properties.isRegister()) {
log.debug("Registration disabled.");
return;
}
if (this.registration.getPort() == 0) {
this.registration.setPort(getPort().get());
}
super.register();
}
跟踪到父类
protected void register() {
this.serviceRegistry.register(getRegistration());
}
定位到org.springframework.cloud.zookeeper.serviceregistry.ZookeeperServiceRegistry
public void register(ZookeeperRegistration registration) {
try {
getServiceDiscovery().registerService(registration.getServiceInstance());
} catch (Exception e) {
rethrowRuntimeException(e);
}
}
核心逻辑
@VisibleForTesting
protected void internalRegisterService(ServiceInstance<T> service) throws Exception
{
byte[] bytes = serializer.serialize(service);
String path = pathForInstance(service.getName(), service.getId());
final int MAX_TRIES = 2;
boolean isDone = false;
for ( int i = 0; !isDone && (i < MAX_TRIES); ++i )
{
try
{
CreateMode mode;
switch (service.getServiceType()) {
case DYNAMIC:
mode = CreateMode.EPHEMERAL;
break;
case DYNAMIC_SEQUENTIAL:
mode = CreateMode.EPHEMERAL_SEQUENTIAL;
break;
default:
mode = CreateMode.PERSISTENT;
break;
}
client.create().creatingParentContainersIfNeeded().withMode(mode).forPath(path, bytes);
isDone = true;
}
catch ( KeeperException.NodeExistsException e )
{
client.delete().forPath(path); // must delete then re-create so that watchers fire
}
}
}
在这个方法中首先判断得出节点的属性,然后通过CuratorFramework来创建zk节点;至此服务就注册到zk上面了