Spring Cloud 使用 Zookeeper 进行服务注册与发现

服务注册(ServiceRegistry)与发现(DiscoveryClient)在Spring Cloud中都进行了抽象。除了之前介绍的Eureka外,Spring Cloud也支持使用Zookeeper作为服务的注册中心。基于Zookeeper的服务注册由org.springframework.cloud.zookeeper.serviceregistry.ZookeeperServiceRegistry实现,服务发现由org.springframework.cloud.zookeeper.discovery.ZookeeperDiscoveryClient实现。需要使用Zookeeper进行服务注册与发现时需要加入spring-cloud-starter-zookeeper-discovery依赖。

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
</dependency>

spring-cloud-starter-zookeeper-discovery默认会使用的是基于Zookeeper3.5.X系列的jar包,如果你使用的是Zookeeper3.4.X系列,则需要自己手动的指定依赖Zookeeper3.4.X系列,同时在spring-cloud-starter-zookeeper-discovery中排除对Zookeeper的依赖。

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-zookeeper-all</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <version>3.4.12</version>
    <exclusions>
        <exclusion>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
        </exclusion>
    </exclusions>
</dependency>

通常作为服务提供方的应用都是Web应用,所以一般还需要加上spring-boot-starter-web依赖。Classpath中添加了spring-cloud-starter-zookeeper-discovery后Spring Cloud默认会把自己注册到地址为localhost:2181的zookeeper,服务名称默认取${spring.application.name},如果没有指定spring.application.name则默认取application。注册时会在/services/serviceId下写入实例信息,比如serviceId指定为service1,则默认会在/services/service1目录下创建一个实例对应的节点,写入实例信息。如果你需要连接的Zookeeper的地址不是localhost:2181,则可以通过spring.cloud.zookeeper.connectString进行指定。

spring:
  application:
    name: service1
  cloud:
    zookeeper:
      connect-string: 10.10.10.1:2181

无论是服务提供者还是客户端应用都需要添加spring-cloud-starter-zookeeper-discovery依赖。服务提供方应用按照上面的配置就已经可以了。客户端应用,如果你使用Feign,则需要添加spring-cloud-starter-openfeign依赖。

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

然后声明@FeignClient,定义对远程接口的抽象,比如下面这样。

@FeignClient("${feign.service1.name:service1}")
public interface HelloService {
  @GetMapping("hello")
  String hello();
}

然后在@Configuration类上声明@EnableFeignClients以启用对Feign的支持,并将扫描@FeignClient

@SpringBootApplication
@EnableFeignClients
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
  
}

FeignClient将通过@FeignClient上指定的serviceId到Zookeeper上寻找对应的实例,从而完成相应的调用。

对于客户端应用而言,如果它只作为客户端,而不需要对外提供服务,或者说它不需要作为一个服务注册到Zookeeper中,则我们可以指定spring.cloud.service-registry.auto-registration.enabled=false或者spring.cloud.zookeeper.discovery.register=false

spring:
  cloud:
    service-registry:
      auto-registration:
        enabled: false

如果我们的应用中拥有spring-cloud-starter-zookeeper-discovery,但是我们又不想使用基于Zookeeper的服务发现与注册功能,则可以指定spring.cloud.zookeeper.enabled=false,如果只是不想使用服务发现功能,则可以指定spring.cloud.zookeeper.discovery.enabled=false。Spring Cloud Zookeeper不允许我们只启用服务注册功能,而不启用服务发现功能。换言之,启用服务注册功能的前提是启用了服务发现功能。所以如果你的服务提供方不需要依赖其它服务,但是它需要作为一个服务注册到Zookeeper上,你不能指定spring.cloud.zookeeper.discovery.enabled=false。更多关于Spring Cloud Zookeeper可以指定的信息可以参考org.springframework.cloud.zookeeper.ZookeeperProperties的源码或API文档。

服务注册到Zookeeper中默认写入的主机地址是主机名,很多时候可能你更希望它写入的是IP地址。此时可以指定spring.cloud.zookeeper.discovery.preferIpAddress=true

spring:
  cloud:
    zookeeper:
      discovery:
        prefer-ip-address: true

服务注册到Zookeeper中的实例ID默认是当前ApplicationContext的ID,它通常可以做到唯一,但是可读性不强。可能你更希望它能够表现为当前的主机跟端口,则可以指定spring.cloud.zookeeper.discovery.instanceId为如下这样。

spring:
  cloud:
    zookeeper:
      discovery:
        instance-id: ${spring.cloud.client.hostname}:${server.port}

如果你希望instanceId是主机IP和端口的组合,则可以指定instanceId的值为${spring.cloud.client.ip-address}:${server.port}

Spring Cloud Zookeeper在进行服务注册与发现时默认的namespace,或者说根路径是/services,如果你使用的Zookeeper该路径已经有其它用途了,你可能想要更换这个根路径。可以通过spring.cloud.zookeeper.discovery.root属性来更换这个根路径,比如下面就指定了根路径是/springcloud。当更换了默认的根路径时,注意服务提供方和客户端都需要更换这个路径。

spring:
  cloud:
    zookeeper:
      discovery:
        root: /springcloud

Spring Cloud Zookeeper底层在与Zookeeper进行交互时使用的是Apache Curator,其会自动注册org.apache.curator.framework.CuratorFramework类型的bean,所以如果应用中需要自己直接与Zookeeper进行交互时可以直接注入CuratorFramework,然后通过它与Zookeeper进行交互。

参考文档