第八章:Spring Cloud Zookeeper
这个项目在spring boot基础上,通过自动化配置,提供对Zookeeper 的整合;并绑定到spring运行环境及各模块中。可以很方便的使用注解,去启用配置强大的组件,实现分布式通用模式,并构建基于Zookeeper组件的大型分布式系统。这些模式包括服务发现和配置,终端器模式(Hystrix),快捷路由模式(Zuul),客户端负载均衡模式(Ribbon)等。
安装Zookeeper
请按照安装文档中的操作步骤安装Zookeeper。
通过Zookeeper发现服务
服务发现模式是微服务化架构的关键原则。需要调用各服务的客户端的配置,或者让各模块都遵从一定形式的约定,很困难,也很繁琐。Curator(Zookeeper使用的一个java库)通过服务发现扩展实现的服务发现。Spring Cloud Zookeeper提升了服务注册和服务发现的特性。
如何激活
添加org.springframework.cloud:spring-cloud-starter-zookeeper-discovery
的依赖,可以启用自动化配置,设置Spring Cloud Zookeeper服务发现。
通过Zookeeper注册服务
当一个客户端通过Zookeeper注册的时候,会提供本身的元数据信息,包括主机名,端口号,id和名称。Zookeeper客户端示例:
@SpringBootApplication
@EnableDiscoveryClient
@RestController
public class Application {
@RequestMapping("/")
public String home() {
return "Hello world";
}
public static void main(String[] args) {
new SpringApplicationBuilder(Application.class).web(true).run(args);
}
}
(一般情况下,Spring Boot应用)。 如果Zookeeper不在localhost:2181
路径上。需要配置定位到服务的地址。示例:
application.yml
spring:
cloud:
zookeeper:
connect-string: localhost:2181
注意:如果你用 Spring Cloud Zookeeper配置,以上的值需要替换到
bootstrap.yml
文件中,而不是application.yml
文件。
默认运行环境中获取应用名称 (service ID),虚拟主机和非安全端口,分别是${spring.application.name}
, ${spring.application.name}
和 ${server.port}
。
@EnableDiscoveryClient
使应用注册了一个服务本身实例(注册了服务本身),也声明了它是Zookeeper的客户端(可以查询Zookeeper上的其他服务)。
通过Zookeeper发现客户端
Spring Cloud用逻辑服务名代替url地址对Feign(一个rest的客户端构建库)和Spring RestTemplate支持。
你可以通过org.springframework.cloud.client.discovery.DiscoveryClient
提供简单的服务发现的api客户端,不仅仅是netflix。例如:
@Autowired
private DiscoveryClient discoveryClient;
public String serviceUrl() {
List<ServiceInstance> list = discoveryClient.getInstances("STORES");
if (list != null && list.size() > 0 ) {
return list.get(0).getUri().toString();
}
return null;
}
Zookeeper依赖
使用Zookeeper依赖
Spring Cloud Zookeeper提供对你的应用的依赖作为属性成为可能。作为依赖,你可以理解你想要通过Feign(一个rest的客户端构建库)和Spring RestTemplate调用在Zookeeper上注册的其他应用。
你可以使用Zookeeper依赖监控功能让你能监控,管理你的依赖的状态,并决定如何面对。
如何激活Zookeeper的依赖
添加
org.springframework.cloud:spring-cloud-starter-zookeeper-discovery
的依赖,可以启用自动化配置,设置Spring Cloud Zookeeper服务发现。如果你必须加载
spring.cloud.zookeeper.dependencies
这部分依赖完整的配置好,需要检查后续部分的更多细节,确认这一特性被激活了。即便你在属性文件里添加了依赖文件,还是可以关闭这些依赖的加入。只需要设置
spring.cloud.zookeeper.dependency.enabled
属性为false。
设置Zookeeper依赖
从依赖实例为代表,让我们更近一层看看:
application.yml
spring.application.name: yourServiceName
spring.cloud.zookeeper:
dependencies:
newsletter:
path: /path/where/newsletter/has/registered/in/zookeeper
loadBalancerType: ROUND_ROBIN
contentTypeTemplate: application/vnd.newsletter.$version+json
version: v1
headers:
header1:
- value1
header2:
- value2
required: false
stubs: org.springframework:foo:stubs
mailing:
path: /path/where/mailing/has/registered/in/zookeeper
loadBalancerType: ROUND_ROBIN
contentTypeTemplate: application/vnd.mailing.$version+json
version: v1
required: true
现在让我们一个一个地浏览这些依赖。根属性的名称是spring.cloud.zookeeper.dependencies
。
别名
根据Ribbon的约束条件(URL路径里面的应用id必须被替换掉,因此不能取/foo/bar/name类似复杂的路径),根属性以下的每个以来必须要有一个别名。别名可以被用来代替DiscoveryClient, Feign或者RestTemplate对服务id的使用。
后面提到的例子中的别名是newsletter和mailing。使用Feign,newsletter为别名的例子,可以这样:
@FeignClient("newsletter")
public interface NewsletterService {
@RequestMapping(method = RequestMethod.GET, value = "/newsletter")
String getNewsletters();
}
路径
yaml路径path属性代表。
路径是指,在Zookeeper中依赖注册在什么路径之下。像之前Ribbon对URL的操作一样,这样的路径和需求是不一致的。这是Zookeeper把别名映射到相应的路径的原因。
负载均衡类型
yaml负载均衡类型loadBalancerType属性代表。
如果你知道,调用某个以来的时候,需要采用什么策略的话,你可以在yaml文件里面提供,它并会被自动采用。你可以采用以下策略中的一种:
STICKY - 某个实例一旦被选择,将会一直采用
RANDOM - 随机获取一个实例
ROUND_ROBIN - 对实例一个一个的轮换
内容类型模板和版本
由yaml内容类型模板contentTypeTemplate和版本version属性代表。
如果你的版本api通过content-type头信息去获取,你又不想每个请求的头信息都包含版本消息。你也不想通过多次请求获取到版本信息后,再去调用最新的api。那么你可以提供contentTypeTemplate,包含$version替换表达式。替换表达式被以yaml中的version属性填写。 让我们来看一个示例:
有以下contentTypeTemplate:
application/vnd.newsletter.$version+json
和以下版本:
v1
会对每个请求的头信息设置
application/vnd.newsletter.v1+json
默认头消息
由yaml中的headers映射map表示。
有时候对某个依赖的调用需要请求设置同样的头消息。为了不在代码中做这样的设置的话,我们可以使用yaml文件配置。例如以下头消息设置:
headers:
Accept:
- text/html
- application/xhtml+xml
Cache-Control:
- no-cache
会对你的http请求Accept和Cache-Control头消息,添加对应的值列表。
强制依赖
由yaml必要的required属性代表。
如果在你的应用启动的时候,有一部分以来需要运行起来,可以用required: true属性在yaml文件中设置。
如果在你的应用启动之前,设置了required的依赖找不到,系统会抛出异常,Spring上下文会启动失败。换句话说,你的应用不会启动,如果必要的依赖没能在Zookeeper中注册。
可以在后续部分阅读更毒关于Spring Cloud Zookeeper对服务在线的检查。
Stubs
你可以复制一个单独的路径,包含备用的依赖jar包,例如:
stubs: org.springframework:foo:stubs
表示有这样一个特殊的依赖:
groupId: org.springframework
artifactId: foo
classifier: stubs - 这是默认值
这时间上等同于:
stubs: org.springframework:foo
因为stubs是类别的初始值。
配置Spring Cloud Zookeeper依赖
有一大堆的属性可以用来设置启用/禁用Spring Cloud Zookeeper依赖的功能。
spring.cloud.zookeeper.dependencies - 如果不设置这个属性,就无法使用到Spring Cloud Zookeeper的依赖。
spring.cloud.zookeeper.dependency.ribbon.enabled (默认是启用的) - Ribbon需要严格的全局配置或者一个依赖一个配置。通过开启这个属性,运行过程中对负载均衡策略有效,也可以受益于Zookeeper依赖部分的负载均衡类型。配置这一功能需要实现LoadBalancerClient用以代表ILoadBalancer,在下一点中将会说到。
spring.cloud.zookeeper.dependency.ribbon.loadbalancer (默认是启用的) - 幸亏有这个属性,自定义的ILoadBalancer能知道传达Ribbon的url,可能实际上是一个别名,需要处理成Zookeeper中的相应路径。没有这一属性,你就不能在内置的路径下注册应用。
spring.cloud.zookeeper.dependency.headers.enabled (默认是启用的) - 这个属性注册一个RibbonClient,会自动在依赖配置中添加相应的头消息和包含版本信息的内容类型消息。没有这些设置,这两个参数就无效了。
spring.cloud.zookeeper.dependency.resttemplate.enabled (默认是启用的) - 启用后,会修改 @LoadBalanced注解的RestTemplate方法的请求的头消息,因而,会传递相应的头消息和包含版本信息的内容类型消息。没有这些设置,这两个参数就无效了。
Spring Cloud Zookeeper依赖监控器
依赖监控机制允许你对依赖添加监听器。这一功能时间上是观察者模式的实现。当依赖的状态改变时(上线或者下线),某些自定义的逻辑会实现。
如何激活
Spring Cloud Zookeeper依赖启用需要在依赖监控机制下工作。
注册一个监听器
为了注册一个监听器,你需要实现org.springframework.cloud.zookeeper.discovery.watcher.DependencyWatcherListener并注册为一个对象。这个接口有一个方法要实现:
void stateChanged(String dependencyName, DependencyState newState);
如果你对某个依赖添加一个监听器的话,依赖名称dependencyName是你的实现的限制条件。newState提供你的依赖变成CONNECTED或者DISCONNECTED的状态。
存在性检查
和依赖监控机制绑定在一起的功能是存在性检查。这提供你在启动应用是根据依赖的状态响应式执行自定义的行为。
对抽象类org.springframework.cloud.zookeeper.discovery.watcher.presence.DependencyPresenceOnStartupVerifier 的默认实现是org.springframework.cloud.zookeeper.discovery.watcher.presence。启动时候的验证默认依赖在性通过以下方式工作:
如果依赖标记为必须的,并且它不再Zookeeper上,那么启动应用时会抛出异常并且关闭。
如果依赖未被标记为必须的,org.springframework.cloud.zookeeper.discovery.watcher.presence.LogMissingDependencyChecker会以WARN的级别记录这个应用缺失的日志。
这一特性可以被重写,因为DefaultDependencyPresenceOnStartupVerifier只有在没有DependencyPresenceOnStartupVerifier类型的对象时才会注册。
用Zookeeper做分布式配置
Zookeeper提供了纵向的命名空间的方式允许客户端存储动态类型的数据,例如配置数据。Spring Cloud Zookeeper Config 也是Config服务端和客户端的替代方案。在特别的引导阶段,配置被加载到Spring环境中。配置默认存在/config命名空间下。多个PropertySource实例基于应用名以及有效的对象,按照Spring Cloud配置对属性映射的顺序被创建。例如你一个以testApp为名的应用,和dev为名的对象,会有以下属性源文件被创建:
config/testApp,dev/
config/testApp/
config/application,dev/
config/application/
越精细的属性源文件越在上面,最底下是最笼统的属性配置源文件。config/application文件下的所有属性是各个应用使用consul作为配置的文件。config/testApp只是testApp应用的属性配置文件。
配置文件目前在应用启动的时候开始读取。对/refresh路径发送一个Http post请求,会让配置被重新载入。监控键值存储(Zookeeper 支持的)是否被修改,在目前项目中还不支持,未来项目中会添加这一特性。
如何激活
添加org.springframework.cloud:spring-cloud-starter-zookeeper-config依赖,会启用自动配置,设置Spring Cloud Zookeeper的配置。
自定义
Zookeeper的配置可以通过以下属性自定义:
bootstrap.yml
spring:
cloud:
zookeeper:
config:
enabled: true
root: configuration
defaultContext: apps
profileSeparator: '::'
enabled设置为false会禁用Zookeeper配置
prefix设置配置的基础目录
defaultContext设置所有应用的公用配置的目录名
profileSeparator设置属性源文件分割对象的分隔符