在我们开发项目时,需要将很多的配置写到配置文件中。但是,如果项目已经启动运行了,而数据库服务器的ip地址发生了改变,我们应该怎么办?如果是这样,我们需要重新修改配置文件,然后重新启动应用。但是如果应用数量庞大的话,维护的成本非常高。
Spring Cloud Config提供了这样的功能,可以让我们统一管理配置文件,以及实时同步更新,并不需要重新启动应用。
Spring Cloud Config简介
Spring Cloud Config为分布式系统外部化配置提供了服务器端和客户端的支持,它包括Config Server和Config Client两部分。由于Config Server和Config Client都实现了对Spring Environment和PropertySource抽象的映射,因此,Spring Cloud Config非常适合Spring应用程序,当然也可与其他任何语言编写的应用程序配合使用。
Config Server时一个可横向扩展、集中式的配置服务器,它用于集中管理应用程序各个环境下的配置,默认使用Git存储配置文件内容,也可以使用SVN存储或者本地存储。
Config Client时Config Server的客户端,用户操作存储在Config Server中的配置内容。微服务在启动时会请求Config Server获取配置文件的内容,请求到后再启动容器。
使用Spring Cloud Config的架构如下:
快速入门 – 搭建Config Server
准备3个配置文件,推送到Git服务器
准备3个文件:
microservice-dev.properties
microservice-production.properties
microservice-test.properties
该文件的命名规则是:{application}-{profile}.properties
其内容是(另外2个文件内容稍有不同即可):
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/taotao?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true
jdbc.username=root
jdbc.password=root
推送文件到git服务器,这里使用的是我们内网的git服务器(Gogs),当然也可以使用github或者使用svn。
创建工程itcast-microservice-config-server
pom依赖:
4.0.0
cn.itcast.microservice.config
itcast-microservice-config-server
1.0.0-SNAPSHOT
org.springframework.boot
spring-boot-starter-parent
1.5.4.RELEASE
org.springframework.cloud
spring-cloud-dependencies
Dalston.SR3
pom
import
org.springframework.cloud
spring-cloud-config-server
编写入口ConfigApplication
package cn.itcast.microservice;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;
@EnableConfigServer //开启配置服务
@SpringBootApplication
public class ConfigApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigApplication.class, args);
}
}
编写application.yml
server:
port: 6688 #服务端口
spring:
application:
name: itcasst-microservice-config-server #指定服务名
cloud:
config:
server:
git: #配置git仓库地址
uri: http://172.16.55.138:10080/zhangzhijun/itcast-config-server.git
#username: zhangzhijun
#password: 123456
启动测试
测试已经看到了配置文件的内容。
请求配置文件的规则如下:
/{application}/{profile}/[label]
/{application}-{profile}.yml
/{label}/{application}-{profile}.yml
/{application}-{profile}.properties
/{label}/{application}-{profile}.properties
其中{label}是指分支,默认是master。
快速入门 – 搭建Config Client
我们在itcast-microservice-item项目中添加Config Client的支持。来读取JDBC的配置文件的内容。
导入依赖
org.springframework.cloud
spring-cloud-starter-config
创建配置文件bootstrap.yml
spring:
cloud:
config:
name: microservice #对应的配置服务中的应用名称
uri: http://127.0.0.1:6869/
profile: dev #对应配置服务中的{profile}
label: master #对应的分支
编写JdbcConfigBean
编写对象通过@Value注解读取Config Server中的值。
package cn.itcast.microservice.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component//加入到Spring容器
public class JdbcConfigBean {
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Value("${jdbc.driverClassName}")
private String driverClassName;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getDriverClassName() {
return driverClassName;
}
public void setDriverClassName(String driverClassName) {
this.driverClassName = driverClassName;
}
@Override
public String toString() {
return "JdbcConfigBean [url=" + url + ", username=" + username
+ ", password=" + password + ", driverClassName="
+ driverClassName + "]";
}
}
编写测试方法进行测试
在ItemController中编写test方法:
package cn.itcast.microservice.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import cn.itcast.microservice.config.JdbcConfigBean;
import cn.itcast.microservice.pojo.Item;
import cn.itcast.microservice.service.ItemService;
@RestController
public class ItemController {
@Autowired
private ItemService itemService;
@Autowired
private JdbcConfigBean jdbcConfigBean;
/**
* 对外提供接口服务,查询商品信息
*
* @param id
* @return
*/
@GetMapping(value = "item/{id}")
public Item queryItemById(@PathVariable("id") Long id) {
return this.itemService.queryItemById(id);
}
@GetMapping(value = "test")
public String test(){
return this.jdbcConfigBean.toString();
}
}
启动测试
测试结果显示,已经从Config Server中获取到配置文件的内容。
手动更新运行中的配置文件
如果git服务器中的配置文件更新了怎么办?正在运行的应用中的配置内容如何更新?
测试
现在我们更改git服务器中的配置文件内容:
修改成4444:
然后刷新Config Server地址观察:
可以看到这里查询到的是新的数据。
在Config Client中测试:
看到依然是旧的数据。
如何才能在重启应用的情况下,获取到最新的配置文件内容呢? -- 为Config Client添加refresh支持。
加入依赖
org.springframework.boot
spring-boot-starter-actuator
为JdbcConfigBean添加@RefreshScope注解
需要为动态更新配置内容的bean添加@RefreshScope注解。
修改application.yml文件
server:
port: 8181 #服务端口
spring:
application:
name: itcast-microservice-item #指定服务名
logging:
level:
org.springframework: INFO
eureka:
client:
registerWithEureka: true #是否将自己注册到Eureka服务中,默认为true
fetchRegistry: true #是否从Eureka中获取注册信息,默认为true
serviceUrl: #Eureka客户端与Eureka服务端进行交互的地址
defaultZone: http://itcast:[email protected]:6868/eureka/
instance:
prefer-ip-address: true #将自己的ip地址注册到Eureka服务中
ipAddress: 127.0.0.1
management:
security:
enabled: false #是否开启actuator安全认证
重启做测试
刷新Config Client的地址:
为了测试,需要再次修改配置文件的内容,将原来的端口4444改为5555。
可以看到Config Client中依然是4444:
然后,post请求/refresh来更新配置内容:
响应:
可以看到有配置文件内容更新了。
可以看到应该更新了最新的配置文件内容,我们也就实现了在未重启项目的情况下实现了动态修改配置文件内容。
但是,这并不实用,原因是项目已经发布上线了,不可能人为的守在服务器前,发现有更新了就手动请求/refresh.
是否自动完成呢?
借助与git的webhook(web钩子)实现自动更新
gogs、github等git服务器提供了web hook功能,意思是,在仓库中的资源发生更新时会通知给谁,这里的谁是一个url地址。
查看本机ip地址:
点击“添加Web钩子”。
添加完成。
接下来进行测试,更新配置文件的内容。
测试的结果会发现,配置文件内容会动态更到Bean中。
总结下流程:
Config Client配置的优化
分析
在itcast-microservice-item中作为Config Client,在配置文件中了配置了Config Server的地址:
这样的硬编码是不好的,如果配置中心的ip地址发生了改变,那么久需要重新修改并且重启应用了。
想想,有什么好的办法解决呢? 如果将Config Server作为一个微服务,并且将其注册的Eureka中,是不是就可以不用硬编码了?
在itcast-microservice-config-server中添加Eureka的依赖
org.springframework.cloud
spring-cloud-starter-eureka-server
com.fasterxml.jackson.dataformat
jackson-dataformat-xml
修改application.yml文件
server:
port: 6869 #服务端口
spring:
application:
name: itcasst-microservice-config-server #指定服务名
cloud:
config:
server:
git: #配置git仓库地址
uri: http://172.16.55.138:10080/zhangzhijun/itcast-config-server.git
#username: zhangzhijun
#password: 123456
eureka:
client:
registerWithEureka: true #是否将自己注册到Eureka服务中,默认为true
fetchRegistry: true #是否从Eureka中获取注册信息,默认为true
serviceUrl: #Eureka客户端与Eureka服务端进行交互的地址
defaultZone: http://itcast:[email protected]:6868/eureka/
instance:
prefer-ip-address: true #将自己的ip地址注册到Eureka服务中
ipAddress: 127.0.0.1
ConfigApplication添加@EnableDiscoveryClient注解
重新启动
可以看到在Eureka中已经有配置中心的服务。
在itcast-microservice-item中修改bootstrap.yml配置
eureka:
client:
serviceUrl: #Eureka客户端与Eureka服务端进行交互的地址
defaultZone: http://itcast:[email protected]:6868/eureka/
spring:
cloud:
config:
name: microservice #对应的配置服务中的应用名称
#uri: http://127.0.0.1:6869/
profile: dev #对应配置服务中的{profile}
label: master #对应的分支
discovery:
enabled: true #启用发现服务功能
service-id: itcasst-microservice-config-server #指定服务名称
疑问:在application.yml中以及配置Eureka的信息,为什么在bootstrap.yml还需要配置?
因为在Spring Boot中bootstrap.yml在application.yml之前加载,所以即使在application.yml中以及配置Eureka的信息,是使用不了的,所以需要在bootstrap.yml中配置Eureka的信息。
测试
测试结果,一切正常。这就完美解决了硬编码的问题。