spring-cloud + Zookeeper 服务发现

Demo

见 https://blog.csdn.net/justsomebody126/article/details/102993806

spring-cloud-zookeeper 源码

https://github.com/spring-cloud/spring-cloud-zookeeper     (参照其说明文档进行编译测试)

以下分析 spring-cloud-zookeeper 源码来分析 Producer 是如何注册自身,Customer 又是如何发现 Producer 的。

测试

  • 编译 mvn --settings .settings.xml package -Dmaven.test.skip      (跳过测试)
  • 执行 sample

       java -jar ./spring-cloud-zookeeper-sample/target/spring-cloud-zookeeper-sample-2.2.0.BUILD-SNAPSHOT.jar

  • 测试 localhost:8085    (端口在 application.yml 中配置)

      localhost: 8085 访问的后端代码,可见是通过 loadBalancer 从当前已注册的 server 中选取一个。

@RequestMapping("/")
public ServiceInstance lb() {
   return this.loadBalancer.choose(this.appName);
}

       返回结果:

{
	"serviceId": "testZookeeperApp",
	"server": {
		"host": "30.5.99.156",
		"port": 8085,
		"scheme": null,
		"id": "30.5.99.156:8085",
		"zone": "UNKNOWN",
		"readyToServe": true,
		"metaInfo": {
			"instanceId": "75d35968-0dcd-426b-96e2-9ec3afde4613",
			"appName": "testZookeeperApp",
			"serverGroup": null,
			"serviceIdForDiscovery": null
		},
		"instance": {
			"name": "testZookeeperApp",
			"id": "75d35968-0dcd-426b-96e2-9ec3afde4613",
			"address": "30.5.99.156",
			"port": 8085,
			"sslPort": null,
			"payload": {
				"id": "testZookeeperApp-1",
				"name": "testZookeeperApp",
				"metadata": {}
			},
			"registrationTimeUTC": 1573445760704,
			"serviceType": "DYNAMIC",
			"uriSpec": {
				"parts": [{
					"value": "scheme",
					"variable": true
				}, {
					"value": "://",
					"variable": false
				}, {
					"value": "address",
					"variable": true
				}, {
					"value": ":",
					"variable": false
				}, {
					"value": "port",
					"variable": true
				}]
			},
			"enabled": true
		},
		"alive": true,
		"hostPort": "30.5.99.156:8085"
	},
	"secure": false,
	"metadata": {},
	"scheme": null,
	"host": "30.5.99.156",
	"port": 8085,
	"instanceId": "30.5.99.156:8085",
	"uri": "http://30.5.99.156:8085"
}
  • 启动另外一个 sample

       java -jar ./spring-cloud-zookeeper-sample/target/spring-cloud-zookeeper-sample-2.2.0.BUILD-SNAPSHOT.jar --server.port=8086

       再执行两次 localhost: 8086,因有 loadBalancer,故返回的结果不同。

Producer 注册原理

  • Producer 注册自己到 ZK 的代码
// org/springframework/cloud/zookeeper/serviceregistry/ZookeeperServiceRegistry.java
@Override
public void register(ZookeeperRegistration registration) {
  try {
    getServiceDiscovery().registerService(registration.getServiceInstance());
  }
  catch (Exception e) {
    rethrowRuntimeException(e);
  }
}
  • 相应执行堆栈

  • 那么是什么控制 Producer 启动时调用  ZookeeperServiceRegistry.register() 将自己注册到 ZK 的呢?
// org/springframework/cloud/zookeeper/serviceregistry/ZookeeperAutoServiceRegistration.java	
@Override
protected void register() {
  if (!this.properties.isRegister()) {
    log.debug("Registration disabled.");
    return;
  }
  if (this.registration.getPort() == 0) {
    this.registration.setPort(getPort().get());
  }
  super.register();
}
// properties 是 ZookeeperDiscoveryProperties.java 这个类的实例,
// 可以通过 setRegister(false) 来决定是否注册。

// ZookeeperDiscoveryProperties 与 application.properties 中配置绑定
@ConfigurationProperties("spring.cloud.zookeeper.discovery")
public class ZookeeperDiscoveryProperties { ... }

// ZookeeperAutoServiceRegistration 唯一被用到的地方
// org/springframework/cloud/zookeeper/serviceregistry/ZookeeperAutoServiceRegistrationAutoConfiguration.java
@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);
  }

// @ConditionalOnMissingBean(type = "org.springframework.cloud.zookeeper.discovery.ZookeeperLifecycle") : 
如果容器中已经存在 ZookeeperLifecycle bean 了,
就不需要装载 ZookeeperAutoServiceRegistrationAutoConfiguration,否则装载。

// @ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true) :
如果 application.properties 中 spring.cloud.service-registry.auto-registration.enabled 
属性为 true,就装载本 bean,否则不装载,默认为 true。

// @AutoConfigureAfter({ ZookeeperServiceRegistryAutoConfiguration.class }) :
本 bean 在 ZookeeperServiceRegistryAutoConfiguration 后装载。

// @AutoConfigureBefore({ AutoServiceRegistrationAutoConfiguration.class,  ZookeeperDiscoveryAutoConfiguration.class }) :
本 bean 在 AutoServiceRegistrationAutoConfiguration 和 ZookeeperDiscoveryAutoConfiguration 前装载

// @ConditionalOnZookeeperDiscoveryEnabled :
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@ConditionalOnZookeeperEnabled
@ConditionalOnProperty(value = "spring.cloud.zookeeper.discovery.enabled", matchIfMissing = true)
public @interface ConditionalOnZookeeperDiscoveryEnabled {
}

// @ConditionalOnZookeeperEnabled:
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@ConditionalOnProperty(value = "spring.cloud.zookeeper.enabled", matchIfMissing = true)
public @interface ConditionalOnZookeeperEnabled {
}

// ZookeeperAutoServiceRegistration 的构造函数
// org/springframework/cloud/zookeeper/serviceregistry/ZookeeperAutoServiceRegistration.java
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());
  }
}
  • 综上分析,并没有用户代码来明确调用 ZookeeperServiceRegistry.register() 将 Producer 注册到 ZK,而是通过 bean + application.properties 文件的方式,在容器启动时,决定是否注册。

Cusumer 发现 Producer 原理

(以下分析基于 Demo https://blog.csdn.net/justsomebody126/article/details/102993806 中的 consumer)

这里要解决这两个问题:

1. Consumer 怎么从 zookeeper 获取到 Producer 的 server 列表。
2. Consumer 怎么通过 Ribbon 实现负载均衡。

通过参考 https://my.oschina.net/13426421702/blog/3064709 以及本地 debug,可知:

  • 请求通过 LoadBalancerFeignClient 调用 executeWithLoadBalancer 来实现负载均衡

获取要访问的真实 producer 地址的地方。

获取到的真实 Producer 的 server

  • 下面分析一下 reconstructURIWithServer 是怎么通过 zookeeper 获取真实的 Producer server 的

跟一下代码,可见采用的是 Round Robin 算法。

getAllServerList:

  • 那么 allServerList 是什么时候更新的?

从代码看,有一个 serverListUpdater 定时更新 Producer server 列表,就本例来说,就是从 zookeeper 获取。

发布了34 篇原创文章 · 获赞 0 · 访问量 1388

猜你喜欢

转载自blog.csdn.net/justsomebody126/article/details/103001557