Spring Cloud Zookeeper源码解析-----服务注册原理

zookeeper作为一个分布式文件系统,可用于注册中心,配置中心(很少),分布式锁,命名服务等场景。同时已经集成到springcloud项目中----spring cloud zookeeper;接下来通过读spring cloud zookeeper的源码来了解它的服务注册原理

1、application.yml配置文件配置

server:
  port: 9000
spring:
  application:
    name: lh-zookeeper-registry
  cloud:
    zookeeper:
      connect-string: localhost:2181
      discovery:
        register: true
        enabled: true
        root: lh-registry
        instance-host: 172.20.249.247

spring cloud zookeeper discovery属性全部都来自于ZookeeperDiscoveryProperties类

1.1、ZookeeperDiscoveryProperties

@ConfigurationProperties("spring.cloud.zookeeper.discovery")
public class ZookeeperDiscoveryProperties {
    public static final String DEFAULT_URI_SPEC = "{scheme}://{address}:{port}";
    private HostInfo hostInfo;
    private boolean enabled = true;
    private String root = "/services";
    private String uriSpec = "{scheme}://{address}:{port}";
    private String instanceId;
    private String instanceHost;
    private String instanceIpAddress;
    private boolean preferIpAddress = false;
    private Integer instancePort;
    private Integer instanceSslPort;
    private boolean register = true;
    private Map metadata = new HashMap();
    private String initialStatus = "UP";
    private int order = 0;
}

 该类中就包括了服务的一些基本信息以及后面创建zk节点的默认根节点("/services")

2、ZookeeperAutoServiceRegistrationAutoConfiguration

zookeeper服务自动注册自动配置类

@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");
        } else {
            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().intValue());
            }

            if (properties.getInstanceId() != null) {
                builder.id(properties.getInstanceId());
            }

            return builder.build();
        }
    }

服务启动之后,会先在这个类中根据application.yml文件中的配置内容生成一个ServiceInstanceRegistration服务实例注册类;

这个类中包含了两个对象:

  • ServiceInstance服务实例对象
  • ServiceInstanceBuilder服务实例构造对象 

Spring Cloud Zookeeper源码解析-----服务注册原理_第1张图片

ServiceInstance服务实例中的属性就包含了服务的ip,port,name以及zk节点属性等属性;后面会根据这些信息创建一个zk节点

public class ServiceInstance {
    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 metadata = new HashMap();
}

2.1、ServiceInstanceRegistration

回到ZookeeperAutoServiceRegistrationAutoConfiguration中的ServiceInstanceRegistration生成Bean的逻辑

@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");
        } else {
            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().intValue());
            }

            if (properties.getInstanceId() != null) {
                builder.id(properties.getInstanceId());
            }

            return builder.build();
        }
    }

首先获取服务名,默认为"application";

然后获取instance-host值(String host = properties.getInstanceHost();),如果配置文件中配置了instance-host的值(ip地址),那么host就取配置文件中的值,如果配置文件中没有配置的话,就默认取主机名;

接下来组装ZookeeperInstance 对象信息(非重点)

接下来通过前面获取的instance-host和name组装成一个服务实例构造对象ServiceInstanceBuilder

最后通过ServiceInstanceBuilder.build()方法来创建一个ServiceInstanceRegistration 服务实例注册对象

2.3、ZookeeperAutoServiceRegistration 

ZookeeperAutoServiceRegistration zk节点自动注册服务;

@Bean
    public ZookeeperAutoServiceRegistration zookeeperAutoServiceRegistration(ZookeeperServiceRegistry registry, ZookeeperRegistration registration, ZookeeperDiscoveryProperties properties) {
        return new ZookeeperAutoServiceRegistration(registry, registration, properties);
    }

这里通过ZookeeperServiceRegistry,ZookeeperRegistration和ZookeeperDiscoveryProperties来创建一个ZookeeperAutoServiceRegistration服务

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().intValue());
        }

    }

同时该类下有一个register()方法

protected void register() {
        if (!this.properties.isRegister()) {
            log.debug("Registration disabled.");
        } else {
            if (this.registration.getPort() == 0) {
                this.registration.setPort(this.getPort().get());
            }

            super.register();
        }
    }

一直进入register()方法,最后是在ZookeeperServiceRegistry类中调用register()方法

public void register(ZookeeperRegistration registration) {
        try {
            this.getServiceDiscovery().registerService(registration.getServiceInstance());
        } catch (Exception var3) {
            ReflectionUtils.rethrowRuntimeException(var3);
        }

    }

该方法中首先获取ServiceDiscovery对象

然后调用该对象中的registerService()方法

参数传递的是之前根据配置文件生成的ServiceInstanceRegistration对象得到的ServiceInstance对象

public ServiceInstance getServiceInstance() {
        if (this.serviceInstance == null) {
            this.build();
        }

        return this.serviceInstance;
    }

最终的逻辑实现是由ServiceDiscoveryImpl实现了ServiceDiscovery接口,然后实现了registerService()方法

public class ServiceDiscoveryImpl implements ServiceDiscovery {
    public void registerService(ServiceInstance service) throws Exception {
        ServiceDiscoveryImpl.Entry newEntry = new ServiceDiscoveryImpl.Entry(service, null);
        ServiceDiscoveryImpl.Entry oldEntry = (ServiceDiscoveryImpl.Entry)this.services.putIfAbsent(service.getId(), newEntry);
        ServiceDiscoveryImpl.Entry useEntry = oldEntry != null ? oldEntry : newEntry;
        synchronized(useEntry) {
            if (useEntry == newEntry) {
                useEntry.cache = this.makeNodeCache(service);
            }

            this.internalRegisterService(service);
        }
    }
}

核心逻辑是internalRegisterService()方法

@VisibleForTesting
    protected void internalRegisterService(ServiceInstance service) throws Exception {
        byte[] bytes = this.serializer.serialize(service);
        String path = this.pathForInstance(service.getName(), service.getId());
        int MAX_TRIES = true;
        boolean isDone = false;

        for(int i = 0; !isDone && i < 2; ++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;
                }

                ((ACLBackgroundPathAndBytesable)this.client.create().creatingParentContainersIfNeeded().withMode(mode)).forPath(path, bytes);
                isDone = true;
            } catch (NodeExistsException var8) {
                this.client.delete().forPath(path);
            }
        }

    }

在这个方法中首先判断得出节点的属性,然后通过CuratorFramework来创建zk节点;至此服务就注册到zk上面了

3、效果

启动程序,通过ZooInspector连接zk客户端;就可以看到zk上面的节点信息

Spring Cloud Zookeeper源码解析-----服务注册原理_第2张图片

NodeData节点数据为:

{
	"name": "lh-zookeeper-registry",
	"id": "d1675575-baa4-4242-ab04-3d344238fd05",
	"address": "172.20.249.247",
	"port": 9000,
	"sslPort": null,
	"payload": {
		"@class": "org.springframework.cloud.zookeeper.discovery.ZookeeperInstance",
		"id": "application-1",
		"name": "lh-zookeeper-registry",
		"metadata": {}
	},
	"registrationTimeUTC": 1587371140572,
	"serviceType": "DYNAMIC",
	"uriSpec": {
		"parts": [{
			"value": "scheme",
			"variable": true
		}, {
			"value": "://",
			"variable": false
		}, {
			"value": "address",
			"variable": true
		}, {
			"value": ":",
			"variable": false
		}, {
			"value": "port",
			"variable": true
		}]
	}
}

可以得出NodeData的数据同application.yml配置文件中的数据是对应的

你可能感兴趣的:(zookeeper)