spring cloud git config 配置中心多环境方案

1,400 阅读3分钟

这是我参与8月更文挑战的第12天,活动详情查看:8月更文挑战

遇到的问题

首先在spring cloud微服务化的开发大环境下,配置的动态化必不可少,spring cloud 自身集成的config 配置中心就可以实现但是项目遇到了问题很不舒服: 我们公司spring cloud 集成config 方案就是一个bootstrap.yml文件作为加载启动文件,所有的配置从配置中心加载

server:
  port: 8688


#测试环境
eureka:
  client:
    serviceUrl:
      defaultZone: http://127.0.0.1:8601/eureka/
  instance:
    prefer-ip-address: true
spring:
  application:
    name: cccc
  cloud:
    config:
      discovery:
        enabled: true
        service-id: config
      profile: dev,redis
      label: master

大致就是上面那样,那么如果我要部署本地环境和线上环境呢,就是修改注册中心地址,根据分支进行控制 这样的话线上部署就用prod分支,本地部署就用local分支,测试部署就用test,但是这样很容易出现问题,就是改完代码之后分支的合并得小心,不能合并配置文件,否则会出现配置出错的问题

预想结果

传统的 spring boot项目 其实可以根据配置文件的名字进行环境的切换 在这里插入图片描述 这样需要什么环境根据jvm参数或者maven打包参数进行构建就行

解决思路

这个问题找了好久其实可以有这几种方案

  1. 指定booststrap.yml的文件位置,通过 --spring.cloud.bootstrap.location= 启动参数设置
  2. 放置一个空的bootstrap.yml,然后在项目启动时设置默认的参数 springApplication.setDefaultProperties(properties)
  3. 想个办法动态的构建 bootstrap.yml 文件就是说项目启动时在 classpath下生成一个 bootstrap.yml 文件

方案实现

方案一

在这里插入图片描述 大致这样 启动参数:

java jar 0.0.1-SNAPSHOT.jar --spring.cloud.bootstrap.location=classpath:/local/bootstrap.yml

这样是可以的,不同的环境不同的启动参数

优势

改动小

劣势

大家也看到了,这样话需要配置idea,让其识别,否则开发时代码没提示啊,不爽

方案二

利用spring boot的加载顺序进行改变

在这里插入图片描述 这是我从网上找的spring boot 加载顺序

那么我们可以手动设置 还是一样classpath下放一个空的 bootstrap.yml 文件

将配置放在 ${env}.properties文件中 resource - bootstrap.yml - local.properties - test.properties - prod.properties

在项目启动类如下

public class Application {

    public static void main(String[] args) throws Exception {
    	SpringApplication springApplication = new SpringApplication(Application.class)
		// 虚拟机参数指定部署环境
		String profile = System.getProperty("spring.profiles.active");
		if (StringUtils.isEmpty(profile)){
		    throw new RuntimeException("未指定构建环境");
		}
		log.info("构建环境 ==> {}", profile);
		Properties properties1 = new Properties();
		properties1.load(EcoIpadApplication.class.getClassLoader().getResourceAsStream(profile + ".properties"));
		springApplication.setDefaultProperties(properties1);
		springApplication.run(args);
	}
}

启动参数

java -Dspring.profiles.active=local  jar 0.0.1-SNAPSHOT.jar

优势

代码也有提示(实在想不出来编的)

劣势

硬编码 项目原来的yml或者开发者喜欢用yml的这种格式的会感觉不舒服

方案三

这种方案实现起来很多解决思路

  1. 项目启动时复制指定环境的配置文件重命名为 bootstrap.yml
  2. maven打包时利用插件将文件重定向

maven方案(推荐)

文件目录 在这里插入图片描述 pom 文件

<profiles>
        <profile>
            <!-- 本地开发环境 -->
            <id>local</id>
            <properties>
                <env>local</env>
            </properties>
            <activation>
                <!-- 设置默认激活这个配置 -->
                <activeByDefault>true</activeByDefault>
            </activation>
        </profile>
        <profile>
            <!-- 发布环境 -->
            <id>prod</id>
            <properties>
                <env>prod</env>
            </properties>
        </profile>
        <profile>
            <!-- 测试环境 -->
            <id>test</id>
            <properties>
                <env>test</env>
            </properties>
        </profile>
    </profiles>

<build>
        <plugins>
           

            <!--将指定环境的yml文件重命名为bootstrap.yml-->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-antrun-plugin</artifactId>
                <version>1.8</version>
                <executions>
                    <execution>
                        <phase>compile</phase>
                        <goals>
                            <goal>run</goal>
                        </goals>
                        <configuration>
                            <tasks>
                                <move file="${project.build.directory}/classes/application-${env}.yml"
                                      tofile="${project.build.directory}/classes/bootstrap.yml" />
                            </tasks>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

这样maven打包时

mvn clean install -Dmaven.test.skip=true -Ptest

打包完target/classes如下 在这里插入图片描述 这也是我现在用的方案

优势

既没有硬编码又使用了yml格式

劣势

维护起来不方便,要是接手项目的人不懂又没写好注释把这个插件删了,那就凉了

硬编码方案

public class Application {

    public static void main(String[] args) throws Exception {
 		String profile = System.getProperty("spring.profiles.active");
		 if (StringUtils.isEmpty(profile)){
		      throw new RuntimeException("未指定构建环境");
		  }
		  log.info("构建环境 ==> {}", profile);
		// 获取项目构建路径
		// 这里想不到方案直接把文件放到 classpath 下,linux环境下不行,所以退而求其次,放到外部的文件夹
		  String property = System.getProperty("user.dir");
		  Resource resource = new DefaultResourceLoader().getResource("classpath:" + String.format("application-%s.yml", profile));
		  File target = new File(property + File.separator + "bootstrap.yml");
		
		  if (target.exists()){
		      target.delete();
		  }
		
		  Files.copy(resource.getInputStream(), target.toPath());
		
		  if (target.exists()){
		      List<String> collect = Stream.of(args).collect(Collectors.toList());
			// 指定配置文件地址
		      collect.add("--spring.cloud.bootstrap.location=" + target.getPath());
		      args = collect.toArray(new String[0]);
		  }   	
	}
}

其实还有一种方案,基于分支构建,但是改动小 利用yml多环境配置

server:
  port: 8688

spring:
  profiles:
    active: test
---
#测试环境

eureka:
  client:
    serviceUrl:
      defaultZone: http://127.0.0.1:8601/eureka/
  instance:
    prefer-ip-address: true
spring:
  application:
    name: cccc
  cloud:
    config:
      discovery:
        enabled: true
        service-id: config
      profile: dev,redis
      label: master
  profiles: test
  
---
# 正式环境

eureka:
  client:
    serviceUrl:
      defaultZone: http://127.0.0.1:8601/eureka/
  instance:
    prefer-ip-address: true
spring:
  application:
    name: cccc
  cloud:
    config:
      discovery:
        enabled: true
        service-id: config
      profile: dev,redis
      label: master
  profiles: prod

合并代码时只要小心

spring:
  profiles:
    active: test

总结

其实一开始的问题应该还有更优雅的解决方案,只是我没找到,还有其他方案的朋友可以留个言让我学习下,谢谢