zookeeper服务发现实战及原理–spring-cloud-zookeeper源码分析

释放双眼,带上耳机,听听看~!
00:00
00:00

1.为什么要服务发现?

服务实例的网络位置都是动态分配的。由于扩展、失败和升级,服务实例会经常动态改变,因此,客户端代码需要使用更加复杂的服务发现机制。

2.常见的服务发现开源组件

etcd—用于共享配置和服务发现的高可用性、分布式、一致的键值存储。使用etcd的两个著名项目是Kubernetes和Cloud Foundry。
consul-发现和配置服务的工具。它提供了一个API,允许客户端注册和发现服务。领事可以执行健康检查,以确定服务的可用性。
Apache Zookeeper——一个广泛使用的分布式应用高性能协调服务。Apache Zookeeper最初是Hadoop的子项目,但现在是顶级项目。

zookeeper服务发现实战及原理--spring-cloud-zookeeper源码分析

 

3.zookeeper服务发现原理

  3.1 zookeeper是什么?  

  ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。

 3.2 为什么zookeeper?

  大部分分布式应用需要一个主控、协调器或控制器来管理物理分布的子进程(如资源、任务分配等)
  目前,大部分应用需要开发私有的协调程序,缺乏一个通用的机制
  协调程序的反复编写浪费,且难以形成通用、伸缩性好的协调器
  ZooKeeper:提供通用的分布式锁服务,用以协调分布式应用(如为HBase提供服务)

  3.3 Zookeeper在微服务框架中可以实现服务发现,该服务发现机制可作为云服务的注册中心。

  通过Spring Cloud Zookeeper为应用程序提供一种Spring Boot集成,将Zookeeper通过自动配置和绑定 的方式集成到Spring环境中.

4.准备工作

安装zookeeper和zooinspector见

window7环境下ZooKeeper的安装运行及监控查看

  此次安装的zookeeper版本为最新版

  zookeeper-3.5.4-beta 安装过程一样

  下载ZooInspector,参照指南运行java -Dfile.encoding=UTF-8 -jar zookeeper-dev-ZooInspector.jar即可

5.实战

  5.1 使用sts创建一个spring starter project名称为zk-discovery,如下图所示

zookeeper服务发现实战及原理--spring-cloud-zookeeper源码分析

此时自动生成的pom.xml的配置文件如下所示

  1. <?xml version=\"1.0\" encoding=\"UTF-8\"?>
  2. <project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"
  3. xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">
  4. <modelVersion>4.0.0</modelVersion>
  5. <parent>
  6. <groupId>org.springframework.boot</groupId>
  7. <artifactId>spring-boot-starter-parent</artifactId>
  8. <version>2.1.3.RELEASE</version>
  9. <relativePath/> <!-- lookup parent from repository -->
  10. </parent>
  11. <groupId>com.example</groupId>
  12. <artifactId>zk-discovery</artifactId>
  13. <version>0.0.1-SNAPSHOT</version>
  14. <name>zk-discovery</name>
  15. <description>Demo project for Spring Boot</description>
  16.  
  17. <properties>
  18. <java.version>1.8</java.version>
  19. <spring-cloud.version>Greenwich.SR1</spring-cloud.version>
  20. </properties>
  21.  
  22. <dependencies>
  23. <dependency>
  24. <groupId>org.springframework.boot</groupId>
  25. <artifactId>spring-boot-starter-web</artifactId>
  26. </dependency>
  27. <dependency>
  28. <groupId>org.springframework.cloud</groupId>
  29. <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
  30. </dependency>
  31.  
  32. <dependency>
  33. <groupId>org.springframework.boot</groupId>
  34. <artifactId>spring-boot-starter-test</artifactId>
  35. <scope>test</scope>
  36. </dependency>
  37. </dependencies>
  38.  
  39. <dependencyManagement>
  40. <dependencies>
  41. <dependency>
  42. <groupId>org.springframework.cloud</groupId>
  43. <artifactId>spring-cloud-dependencies</artifactId>
  44. <version>${spring-cloud.version}</version>
  45. <type>pom</type>
  46. <scope>import</scope>
  47. </dependency>
  48. </dependencies>
  49. </dependencyManagement>
  50.  
  51. <build>
  52. <plugins>
  53. <plugin>
  54. <groupId>org.springframework.boot</groupId>
  55. <artifactId>spring-boot-maven-plugin</artifactId>
  56. </plugin>
  57. </plugins>
  58. </build>
  59.  
  60. <repositories>
  61. <repository>
  62. <id>spring-milestones</id>
  63. <name>Spring Milestones</name>
  64. <url>https://repo.spring.io/milestone</url>
  65. </repository>
  66. </repositories>
  67.  
  68. </project>

 

5.2 ZkDiscoveryApplication增加服务发现注解@EnableDiscoveryClient

  1. package com.example.demo;
  2. import org.springframework.boot.SpringApplication;
  3. import org.springframework.boot.autoconfigure.SpringBootApplication;
  4. import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
  5. @SpringBootApplication
  6. @EnableDiscoveryClient
  7. public class ZkDiscoveryApplication {
  8. public static void main(String[] args) {
  9. SpringApplication.run(ZkDiscoveryApplication.class, args);
  10. }
  11. }

 5.3 增加Rest服务HelloWorldController.java

  1. package com.example.demo;
  2. import org.springframework.web.bind.annotation.GetMapping;
  3. import org.springframework.web.bind.annotation.RestController;
  4. @RestController
  5. public class HelloWorldController {
  6. @GetMapping(\"/helloworld\")
  7. public String HelloWorld() {
  8. return \"Hello World!\";
  9. }
  10. }

5.4. 属性文件配置

  1. spring:
  2. application:
  3. name: HelloWorld
  4. cloud:
  5. zookeeper:
  6. connect-string: localhost:2181
  7. discovery:
  8. enabled: true
  9. server:
  10. port: 8081
  11. logging:
  12. level:
  13. org.apache.zookeeper.ClientCnxn: WARN

6.源码分析

 以spring-boot app项目启动时注册服务ZookeeperAutoServiceRegistrationAutoConfiguration.java为入口

  1. @Bean
  2. @ConditionalOnMissingBean(ZookeeperRegistration.class)
  3. public ServiceInstanceRegistration serviceInstanceRegistration(
  4. ApplicationContext context, ZookeeperDiscoveryProperties properties) {
  5. String appName = context.getEnvironment().getProperty(\"spring.application.name\",
  6. \"application\");
  7. String host = properties.getInstanceHost();
  8. if (!StringUtils.hasText(host)) {
  9. throw new IllegalStateException(\"instanceHost must not be empty\");
  10. }
  11. ZookeeperInstance zookeeperInstance = new ZookeeperInstance(context.getId(),
  12. appName, properties.getMetadata());
  13. RegistrationBuilder builder = ServiceInstanceRegistration.builder() //1
              .address(host) //2
  14. .name(appName) //3
              .payload(zookeeperInstance) //4
  15. .uriSpec(properties.getUriSpec()); //5
  16. if (properties.getInstanceSslPort() != null) {
  17. builder.sslPort(properties.getInstanceSslPort());
  18. }
  19. if (properties.getInstanceId() != null) {
  20. builder.id(properties.getInstanceId());
  21. }
  22. // TODO add customizer?
  23.  
  24. return builder.build();
  25. }

6.1 创建ServiceInstance的builder

  1. /**
  2. * Return a new builder. The {@link #address} is set to the ip of the first
  3. * NIC in the system. The {@link #id} is set to a random UUID.
  4. *
  5. * @return builder
  6. * @throws Exception errors getting the local IP
  7. */
  8. public static<T> ServiceInstanceBuilder<T>builder() throws Exception
  9. {
  10. String address = null;
  11. Collection<InetAddress> ips = ServiceInstanceBuilder.getAllLocalIPs();
  12. if ( ips.size() > 0 )
  13. {
  14. address = ips.iterator().next().getHostAddress(); // default to the first address
  15. }
  16. String id = UUID.randomUUID().toString();
  17. return new ServiceInstanceBuilder<T>().address(address).id(id).registrationTimeUTC(System.currentTimeMillis());
  18. }

 观察zookeeper的生成情况

zookeeper服务发现实战及原理--spring-cloud-zookeeper源码分析

生成的helloWorld的服务信息如下:

  1. {
  2. \"name\": \"HelloWorld\",
  3. \"id\": \"ef95204e-f5e8-4c69-96e4-3f7cec8dce33\",
  4. \"address\": \"DESKTOP-405G2C8\",
  5. \"port\": 8081,
  6. \"sslPort\": null,
  7. \"payload\": {
  8. \"@class\": \"org.springframework.cloud.zookeeper.discovery.ZookeeperInstance\",
  9. \"id\": \"application-1\",
  10. \"name\": \"HelloWorld\",
  11. \"metadata\": {
  12. }
  13. },
  14. \"registrationTimeUTC\": 1552453808924,
  15. \"serviceType\": \"DYNAMIC\",
  16. \"uriSpec\": {
  17. \"parts\": [
  18. {
  19. \"value\": \"scheme\",
  20. \"variable\": true
  21. },
  22. {
  23. \"value\": \"://\",
  24. \"variable\": false
  25. },
  26. {
  27. \"value\": \"address\",
  28. \"variable\": true
  29. },
  30. {
  31. \"value\": \":\",
  32. \"variable\": false
  33. },
  34. {
  35. \"value\": \"port\",
  36. \"variable\": true
  37. }
  38. ]
  39. }
  40. }

7.碰到的问题

 Thrown \”KeeperErrorCode = Unimplemented for /services\” exception

  原因:Curator 和zookeeper的版本不一致

  解决方式:zookeeper升级到最新的版本后异常消失

8.Spring Cloud中的Eureka和Zookeeper的区别

     zookeeper服务发现实战及原理--spring-cloud-zookeeper源码分析

对于 zookeeper 来书,它是 CP 的。也就是说,zookeeper 是保证数据的一致性的。

Eureka 在设计时优先保证可用性,这就是 AP 原则。Eureka 各个节点都是平等的,几个节点挂掉不会影响正常节点的工作,剩余的节点依然可以提供注册和查询服务。

9.总结

   在微服务应用中,服务实例的运行环境会动态变化,实例网络地址也是如此。因此,客户端为了访问服务必须使用服务发现机制。

服务注册表是服务发现的关键部分。服务注册表是可用服务实例的数据库,提供管理 API 和查询 API。服务实例使用管理 API 来实现注册和注销,系统组件使用查询 API 来发现可用的服务实例。

服务发现有两种主要模式:客户端发现和服务端发现。在使用客户端服务发现的系统中,客户端查询服务注册表,选择可用的服务实例,然后发出请求。在使用服务端发现的系统中,客户端通过路由转发请求,路由器查询服务注册表并转发请求到可用的实例。

服务实例的注册和注销也有两种方式。一种是服务实例自己注册到服务注册表中,即自注册模式;另一种则是由其它系统组件处理注册和注销,也就是第三方注册模式。

在一些部署环境中,需要使用 Netflix Eureka、etcd、Apache Zookeeper 等服务发现来设置自己的服务发现基础设施。而另一些部署环境则内置了服务发现。例如,Kubernetes 和 Marathon 处理服务实例的注册和注销,它们也在每个集群主机上运行代理,这个代理具有服务端发现路由的功能。

HTTP 反向代理和 NGINX 这样的负载均衡器能够用做服务器端的服务发现均衡器。服务注册表能够将路由信息推送到 NGINX,激活配置更新,譬如使用 Cosul Template。NGINX Plus 支持额外的动态配置机制,能够通过 DNS 从注册表中获取服务实例的信息,并为远程配置提供 API。

参考文献

【1】https://cloud.spring.io/spring-cloud-zookeeper/1.2.x/multi/multi_spring-cloud-zookeeper-discovery.html

【2】https://cloud.spring.io/spring-cloud-zookeeper/1.2.x/multi/multi_spring-cloud-zookeeper-config.html

【3】http://www.enriquerecarte.com/2017-07-21/spring-cloud-config-series-introduction

【4】https://dzone.com/articles/spring-cloud-config-series-part-2-git-backend

【5】https://dzone.com/articles/spring-cloud-config-part-3-zookeeper-backend

【6】https://github.com/santteegt/spring-cloud-zookeeper-service-discovery-demo

【7】https://blog.csdn.net/peterwanghao/article/details/79722247

【8】https://www.cnblogs.com/EasonJim/p/7613734.html

【9】https://blog.csdn.net/eson_15/article/details/85561179

【10】https://www.nginx.com/blog/service-discovery-in-a-microservices-architecture/

站长资讯

Azure上搭建ActiveMQ集群-基于ZooKeeper配置ActiveMQ高可用性集群

2020-11-9 3:46:15

站长资讯

教你10分钟搭建酷炫的个人博客

2020-11-9 3:46:17

0 条回复 A文章作者 M管理员
欢迎您,新朋友,感谢参与互动!
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
私信列表
搜索