spring cloud config 配置中心使用与避坑指南

简介

微服务集群中少不了的一个重要组件便是配置中心,之前用过百度开源的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 版本

1、配置中心的功能有哪些

多个项目可以集中管理配置信息
项目间配置信息复用,可以提取公共配置信息,供多个项目同时使用
可以动态切换各个环境的配置信息
配置信息变更时,可以即时更新项目内容
配置信息可以缓存在配置中心,无需完全依赖配置信息持久化服务(git、svn等等)
与项目代码完全解耦,无侵入性,也就是说,无论使用配置中心,从配置中心拿配置文件,还是读取本地配置文件,业务是同样处理,完全无感的 ,这样方便我们开发初期直接将配置信息写入本地配置文件,后续再移动至配置中心做集中管理,整个迁移过程,对业务无感,无缝迁移

2、如何搭建配置中心和配置中心的使用
2.1 搭建springcloudconfig 服务器
2.1.1 编辑pom.xml
<?xml version="1.0" encoding="UTF-8"?>

<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.0</modelVersion>

	<groupId>com.danyuan.spring.cloud.demo</groupId>
	<artifactId>config-server</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<name>config-server</name>
	<url>http://www.danyuanblog..com</url>

	<properties>
		<java.version>1.8</java.version>
		<spring-cloud.version>Greenwich.RELEASE</spring-cloud.version>
	</properties>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.3.RELEASE</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>

	<dependencies>
	    <!-- 配置中心 -->
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-config-server</artifactId>
		</dependency>
		<!-- 服务健康检查与监控端点暴露 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-actuator</artifactId>
		</dependency>
		<!-- eureka注册中心客户端包,用于配置中心服务自动注册与发现 -->
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
		</dependency>
		<!-- 通过rabbitmq通知集群内服务动态更新配置信息 -->
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-bus-amqp</artifactId>
		</dependency>		
				
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>${spring-cloud.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

	<repositories>
		<repository>
			<id>spring-milestones</id>
			<name>Spring Milestones</name>
			<url>https://repo.spring.io/milestone</url>
		</repository>
	</repositories>
</project>
2.1.2 创建项目启动类SpringCloudConfigApplication.java
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);
	}
	
}
2.1.3 配置信息bootstrap.yml
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: "*" #暴露所有监控端点
2.1.4 服务启动

需要先启动Eureka注册中心
然后运行SpringCloudConfigApplication.java类

2.2 业务服务如何使用配置中心
2.2.1 引入maven依赖
		<!-- 配置中心客户端依赖 -->
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-config</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-actuator</artifactId>
		</dependency>
		<!-- 连接配置中心重试的依赖 -->
		<dependency>
			<groupId>org.springframework.retry</groupId>
			<artifactId>spring-retry</artifactId>
		</dependency>
		<!-- 通过rabbitmq通知集群内服务动态更新配置信息 -->
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-bus-amqp</artifactId>
		</dependency>
		<!-- eureka注册中心客户端包,用于配置中心服务自动注册与发现 -->
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
2.2.2 启动类配置 StartEurekaAndConfigClientApplication.java
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);
	}
}
2.2.3 配置信息bootstrap.yml
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: "*"
2.2.4 上传配置文件到git仓库

新建client.yml和common.yml文件

编辑内容

client.yml

app:
  username: 小明
  sex:

common.yml

common:
  disableInterface: false #是否禁用接口

上传这两个文件到到git仓库"http://host:port/config_repo" 即可

2.2.5 服务启动

编写测试接口

@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 "接口已经被禁用了,无法查看用户信息!";
        }
    }
}
2.2.6 服务启动

启动StartEurekaAndConfigClientApplication.java

调用测试接口"http://localhost:6001/getUserInfo",即可看到该应用能获取到配置信息的内容

3、配置中心如何动态刷新配置,git钩子的使用与避坑指南

配置git钩子,可以设置其在配置文件提交成功后主动调用配置中心的刷新配置接口,进行配置信息的刷新
但是很多时候这个git钩子调用刷新配置信息接口时,传递的参数,使配置中心解析失败,导致配置信息无法更新,这个应该是springcloudconfig的一个bug
但是我们可以通过改造配置中心代码,添加一个接口拦截器处理这些参数,使其能正常处理这些请求
创建类:WebConfigurer.java

import 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();
			}
		};
	}
}
4、如何搭建配置中心集群

通过注册多个配置中心服务到Eureka注册中心,多个配置中心实例的应用名需要一致,便自动组成一个配置中心集群,并能同步配置信息
业务服务也注册到Eureka注册中心,并通过配置中心服务名进行配置文件的读取,便能自动对配置中心进行故障转移、负载均衡了
实质上是业务服务调用配置中心的接口来获取应用的配置信息,客户端对配置中心的接口实现了故障转移和负载均衡等策略

5、对配置中心做管理界面和权限控制

可以使用gogs或者github等作为配置中心的管理界面和用户权限控制,而且可以配置刷新配置钩子
当然也可以自己通过DB实现,开发一个管理后台来实现这些功能
不过利用gogs或者GitHub之类的git代码管理平台,是完全足够我们使用的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值