2.5【实例】Spring Cloud操作Consul的K/V存储
2.5.1 实例背景
K/V存储是Consul的特点之一。与Redis缓存不同的是,Consul K/V存储的功能是辅助分布式环境下每个微服务的配置等。
一旦微服务过多,每个微服务都需要大量的配置信息,这样很容易造成维护上的困难。在分布式刚兴起的阶段,Spring Cloud给出了Spring Cloud Config组件作为解决方案,其原理是把配置文件集中在Git或SVN等相关版本控制软件中,方便进行统一维护管理,然后在应用程序中使用Spring Cloud Config组件读取和调用配置。因为Consul自带K/V存储,所以已经基本替代了Spring Cloud Config,不但减少了学习成本,更减少了Git和SVN的管理控制成本。如果用SVN管理配置文件,就需要给SVN做高可用和容灾处理,以免无法读取配置文件从而不能正常运行项目。
本实例继续更改cloud-01工程,首先通过Consul的Web UI存储一部分参数,然后通过cloud-01工程对Consul的K/V存储上的数据进行读取操作。
2.5.2 添加依赖
pom.xml文件所需增加代码如下。若用<parent>继承Spring Boot的方式,则不需要再写<version>版本号,Spring Boot会统一管理。
注意,操作Consul的K/V存储的主要API都在spring-cloud-starter-consul-config包中。
2.5.3 利用Consul的UI界面添加K/V存储
Consul的UI界面进行数据存储有3种格式,分别为json、yml和hcl。
如图2-14所示,在Key or folder文本框中输入相关的Key,在Value文本框中输入相应内容,右下角选择定义Value的数据格式,右上角Code表示是否对Value进行数据格式校验。整个界面可看成一个小型的IDE程序。
图2-14
图2-14中的Key值为config/cloud01,dev/data。
config:spring cloud consul config的默认存放地址(文件夹),也可通过bootstrap配置文件更改spring.cloud.consul.config.prefix参数。
cloud01:分布式该微服务的名称,即${spring.application.name}值,该名称不同于project工程名,不同于URL中追加的工程映射的路径名,而是自定义独立存在的微服务名。
dev:cloud01应用程序的profile环境规定,即${spring.profiles.active}值,与微服务名连起来写为cloud01,dev。
data:spring cloud consul config默认存放的Key名,也可通过bootstrap配置文件更改spring.cloud.consul.config.data-key参数并进行自定义。
Value内部需要书写对应config/cloud01,dev/data该Key值的Value。
Value书写YAML格式,其中jdbc是Value的头部标签,将按照该标签进行取值,使程序可以映射到资源配置中。jdbc.my.year展示了在次级目录下如何获取相关参数。
Value内部代码如下,其格式与YAML文件保持一致。
2.5.4 编写YAML资源配置文件对应K/V存储
YAML资源配置文件将与2.5.3节中存储的相关数据进行对应。application.yml资源配置文件代码如下。
spring.cloud.consul.config.fail-fast:true代表开启快速失败。快速失败是指在Java用迭代器遍历一个集合对象时,如果遍历过程中对集合对象的内容进行了修改(如增加、删除、修改),就会抛出Concurrent Modification Exception异常。开启快速失败代表应用程序要求接收到的数据是最新的,如果可能出现脏读等情况,宁愿触发失败也不接收数据。快速失败的原理是迭代器遍历集合中的内容,并且遍历前定义一个变量,若在遍历期间内容发生变化,则会改变变量的值。每当迭代器.hasNext()/next()遍历下一个元素前,都会检测定义的变量是否正常,否则抛出相关异常。
spring.cloud.consul.config.format:YAML是Consul存储中定义的格式。
spring.cloud.consul.config.prefix:config是基础文件夹名称,对应Key键下config/cloud01,dev/data的config文件夹名称。
spring.cloud.consul.config.defaultContext:application是所有应用程序默认文件夹,若有Key值,则直接进行读取。
spring.cloud.consul.profileSeparator:','用于设置分隔符的值,使用配置文件在属性源中分隔配置文件名称。
spring.cloud.consul.data-key:data是Key值名称对应Key键下config/cloud01,dev/data的data值。
Key值config/cloud01,dev/data中的cloud01对应spring.application.name,dev对应spring.profiles.active。
2.5.5 编写MyConfig.java文件对应相关K/V存储
编写MyConfig.java实体类,保证Java程序与2.5.3节中Consul K/V存储的数据相互对应,其代码如下。
@ConfigurationProperties注解对应Consul中config/cloud01,dev/data的Key值下的YAML头部,即获得JDBC头部以下的参数。如果没有头部YAML文件,@Configuration Properties注解内部设置为空即可。注意,此时已经截掉了JDBC,JDBC剩下的username和password必须与Consul K/V中的名字对应,否则Spring Cloud无法进行匹配和映射。
@RefreshScope注解的作用是监控Consul的K/V存储刷新,一旦Consul上的数据被更改,获取的参数也会相应被更改。
2.5.6 调用MyConfig.java中的参数
为方便测试,创建ConfigController.java接口类,在接口中返回Consul K/V存储内的相关参数,代码如下。将MyConfig类通过@Autowired注解注入ConfigController类后,可直接获得Consul K/V存储内的相关参数。
2.5.7 在启动类引用相关配置
本实例中启动类代码如下。由于MyConfig.java对象中使用了@ConfigurationProperties注解获得了JDBC头部参数的数据(prefix="jdbc"),启动类中的@EnableConfiguration-Properties注解则允许微服务工程使用@ConfigurationProperties注解。
@EnableConfigurationProperties注解:启动对@Configurationproperties注解的支持。该注解需要输入已使用@ConfigurationProperties注解的类反射。若用逗号分隔,则可输入多个。
@Configurationproperties注解:外部化配置的注解。若需要绑定和接收外部的一些资源配置文件,则需要使用该注解。
2.5.8 当前项目结构
如图2-15所示,本实例新增了ConfigController.java类管理相关接口,使用My.java与MyConfig.java类进行对相关Value值的映射与返回。如果不使用Java Bean形式,使用@Value、@ConfigurationProperties等注解就可获得相关Value值。
图2-15
2.5.9 运行结果
1.第一次获取相关参数
如图2-16所示,调用获得年份接口得到值2019。
图2-16
如图2-17所示,调用获得用户名接口得到值myusername。
图2-17
2.修改相关参数
如图2-18所示,在第一次调用接口获得参数后修改相关参数,并且使应用程序保持持续运行,可观察应用程序是否在未重启的情况下重新获得了新的参数。
图2-18
3.刷新Controller后第二次获取相关参数
如图2-19所示,重新调用获得年份接口得到新的值20192。
图2-19
如图2-20所示,重新调用获得用户名接口得到新的值myusername2。
图2-20
2.5.10 实例易错点
本实例的易错点较多,包括抛出如空指针、无法获得某对象、某对象为空无法进行引用等相关错误。主要原因是本实例没有较多Debug的能力,所以可能导致项目报错过多,注意不要将一些重要的对应信息遗忘或出现配置错误,主要包括以下几点。
(1)YAML资源配置文件中的每个参数是否与Consul K/V存储的Key地址相互对应。
(2)MyConfig中的参数名称是否与Consul K/V存储的Value名称一一对应,数据格式是否一一对应。
(3)ApplicationMain启动类是否开启了对引入配置注解的支持。