简介
微服务集群中少不了的一个重要组件便是配置中心,之前用过百度开源的disconf做配置中心,springcloud生态中提供了springcloudconfig组件来担当配置中心的职责。springcloud项目是基于springboot项目的,天生拥有springboot自动化配置功能,能识别properties和yaml文件作为其配置文件,springcloud默认读取bootstrap.yaml、application.yaml(或者对应的.properties)文件获取项目的配置信息。通过springcloudconfig组件,我们可以把我们的配置信息集中管理,而且支持动态更新配置信息,springcloudconfig借助git、svn或文件系统来提供配置信息的持久化和版本管理,当然也可以自己实现特定的持久化和版本管理,比如采用DB的方式,本教程将采用git+gogs管理我们的配置信息。
原文:传送门
注:本文基于springcloud2.1.3 Greenwich.RELEASE 版本
多个项目可以集中管理配置信息
项目间配置信息复用,可以提取公共配置信息,供多个项目同时使用
可以动态切换各个环境的配置信息
配置信息变更时,可以即时更新项目内容
配置信息可以缓存在配置中心,无需完全依赖配置信息持久化服务(git、svn等等)
与项目代码完全解耦,无侵入性,也就是说,无论使用配置中心,从配置中心拿配置文件,还是读取本地配置文件,业务是同样处理,完全无感的 ,这样方便我们开发初期直接将配置信息写入本地配置文件,后续再移动至配置中心做集中管理,整个迁移过程,对业务无感,无缝迁移
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>com.danyuan.spring.cloud.demogroupId>
<artifactId>config-serverartifactId>
<version>0.0.1-SNAPSHOTversion>
<name>config-servername>
<url>http://www.danyuanblog..comurl>
<properties>
<java.version>1.8java.version>
<spring-cloud.version>Greenwich.RELEASEspring-cloud.version>
properties>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.1.3.RELEASEversion>
<relativePath />
parent>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-config-serverartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-bus-amqpartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>${spring-cloud.version}version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
<repositories>
<repository>
<id>spring-milestonesid>
<name>Spring Milestonesname>
<url>https://repo.spring.io/milestoneurl>
repository>
repositories>
project>
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.config.server.EnableConfigServer;
@EnableConfigServer
@EnableDiscoveryClient
@SpringBootApplication
public class SpringCloudConfigApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudConfigApplication.class, args);
}
}
spring:
application:
#配置信息服务名配置
name: config-server
#rabbitmq信息配置
rabbitmq:
host: 127.0.0.1
port: 5672
username: admin
password: 123456
cloud:
config:
server:
git:
#配置信息所在git仓库地址
uri: http://host:port/config_repo
force-pull: true
#git用户名、密码配置,springcloudconfig也是一个git客户端服务,需要提供账号密码,从git服务器拉取配置信息下来
username: test
password: 123456
#配置信息缓存目录父路径,特别注意,默认位置是/tmp目录,有的操作系统会定时清理该目录,所以建议指定缓存父目录,用来缓存配置信息
basedir: /home/gitTempConfDir
server:
#配置服务端口
port: 1201
#配置注册中心信息,详情请参考本系列springcloud注册中心搭建相关教程
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8763/eureka/
instance:
prefer-ip-address: true
leaseRenewalIntervalInSeconds: 10
health-check-url-path: /actuator/health
management:
endpoint:
#启用MQ消息总线方式刷新配置信息
bus-refresh:
enabled: true
health:
show-details: ALWAYS
#监控端点暴露
endpoints:
web:
exposure:
include: "*" #暴露所有监控端点
需要先启动Eureka注册中心
然后运行SpringCloudConfigApplication.java类
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-configartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.springframework.retrygroupId>
<artifactId>spring-retryartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-bus-amqpartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
@EnableDiscoveryClient
@SpringBootApplication
@EnableWebMvc
public class StartEurekaAndConfigClientApplication {
public static void main(String[] args) {
SpringApplication.run(StartEurekaClientProviderApplication.class, args);
}
}
spring:
application:
name: client
cloud:
config:
#配置需要消费的配置文件,通过服务名来订阅配置文件信息。这里除了读取应用自身的配置文件和application.yml外,还需要读取common.yml文件中的配置信息
name: ${spring.application.name},common
discovery:
enabled: true
#配置中心服务名,可以直接通过服务名获取到注册中心地址,这样可以直接借助Eureka实现ConfigServer的集群
service-id: config-server
#git分支的配置
label: master
fail-fast: true
retry:
initial-interval: 2000 #首次重试间隔时间,默认1000毫秒
multiplier: 1.1D #下一次重试间隔时间的乘数,比如开始1000,下一次就是1000*1.1=1100
max-interval: 2000 #最大重试时间,默认2000
max-attempts: 20 #最大重试次数,默认6次
#配置注册中心信息,详情请参考本系列springcloud注册中心搭建相关教程
eureka:
client:
serviceUrl:
defaultZone: ${registUrl:http://localhost:8763/eureka/}
instance:
prefer-ip-address: true
leaseRenewalIntervalInSeconds: 10
health-check-url-path: /actuator/health
server:
port: 6001
management:
endpoint:
bus-refresh:
enabled: true
health:
show-details: ALWAYS
endpoints:
web:
exposure:
include: "*"
新建client.yml和common.yml文件
编辑内容
client.yml
app: username: 小明 sex: 男
common.yml
common: disableInterface: false #是否禁用接口
上传这两个文件到到git仓库"http://host:port/config_repo" 即可
编写测试接口
@RestController public class HelloController { @Value("${app.username}") private String username; @Value("${app.sex}") private String sex; @Value("${common.disableInterface}") private boolean disableInterface; @RequestMapping(value = "/getUserInfo", method = RequestMethod.GET) public String getUserInfo() { if(!disableInterface){ return username+":"+sex; }else{ return "接口已经被禁用了,无法查看用户信息!"; } } }
启动StartEurekaAndConfigClientApplication.java
调用测试接口"http://localhost:6001/getUserInfo",即可看到该应用能获取到配置信息的内容
配置git钩子,可以设置其在配置文件提交成功后主动调用配置中心的刷新配置接口,进行配置信息的刷新
但是很多时候这个git钩子调用刷新配置信息接口时,传递的参数,使配置中心解析失败,导致配置信息无法更新,这个应该是springcloudconfig的一个bug
但是我们可以通过改造配置中心代码,添加一个接口拦截器处理这些参数,使其能正常处理这些请求
创建类:WebConfigurer.javaimport org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class WebConfigurer { @SuppressWarnings("rawtypes") @Bean public FilterRegistrationBean indexFilterRegistration() { FilterRegistrationBean<SimoWebHookFilter> registration = new FilterRegistrationBean<SimoWebHookFilter>(new SimoWebHookFilter()); registration.addUrlPatterns("/*"); registration.setOrder(9); return registration; } }
创建类:SimoWebHookFilter.java
import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; @Slf4j @WebFilter(urlPatterns = "/*", filterName = "simoWebHookFilter") public class SimoWebHookFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { log.info("init IndexFilter"); } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest httpServletRequest = (HttpServletRequest)servletRequest; String url = new String(httpServletRequest.getRequestURI()); //只过滤/actuator/bus-refresh请求 if (!url.endsWith("/bus-refresh")) { filterChain.doFilter(servletRequest, servletResponse); return; } //获取原始的body String body = CustometRequestWrapper.getBody(httpServletRequest); log.info("original body:{}", body); //使用HttpServletRequest包装原始请求达到修改post请求中body内容的目的 CustometRequestWrapper requestWrapper = new CustometRequestWrapper(httpServletRequest); filterChain.doFilter(requestWrapper, servletResponse); } @Override public void destroy() { } }
创建类:CustometRequestWrapper.java
import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.IOException; import javax.servlet.ReadListener; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; public class CustometRequestWrapper extends HttpServletRequestWrapper { public CustometRequestWrapper(HttpServletRequest request) { super(request); } // 字符串读取 public static String getBody(HttpServletRequest request) { BufferedReader br = null; StringBuilder sb = new StringBuilder(""); try { br = request.getReader(); String str; while ((str = br.readLine()) != null) { sb.append(str); } br.close(); } catch (IOException e) { e.printStackTrace(); } finally { if (null != br) { try { br.close(); } catch (IOException e) { e.printStackTrace(); } } } return sb.toString(); } @Override public ServletInputStream getInputStream() throws IOException { byte[] bytes = new byte[0]; ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes); return new ServletInputStream() { @Override public boolean isFinished() { return byteArrayInputStream.read() == -1 ? true : false; } @Override public boolean isReady() { return false; } @Override public void setReadListener(ReadListener readListener) { } @Override public int read() throws IOException { return byteArrayInputStream.read(); } }; } }
通过注册多个配置中心服务到Eureka注册中心,多个配置中心实例的应用名需要一致,便自动组成一个配置中心集群,并能同步配置信息
业务服务也注册到Eureka注册中心,并通过配置中心服务名进行配置文件的读取,便能自动对配置中心进行故障转移、负载均衡了
实质上是业务服务调用配置中心的接口来获取应用的配置信息,客户端对配置中心的接口实现了故障转移和负载均衡等策略
可以使用gogs或者github等作为配置中心的管理界面和用户权限控制,而且可以配置刷新配置钩子
当然也可以自己通过DB实现,开发一个管理后台来实现这些功能
不过利用gogs或者GitHub之类的git代码管理平台,是完全足够我们使用的。