SpringBoot的部署上,个人习惯于代码部分打入镜像,配置项以外部挂载的方式进行关联,从而进行代码和配置的解耦合。这也符合程序设计上把易变部分和不变部分进行分离的思想。在部署不同环境的时候(例如,test、dev、staging、product)只需要一个镜像即可,只需创建对应的配置对象即可。这里的配置对象在docker上可以是文件夹,每个文件夹对应一个环境,docker run的时候通过 -v 命令进行关联,在kubernetes上可以是一个configMap对象。

需要外提的配置项一般有application.porperties一族(application-mysql.porperties、application-redis.porperties、application-kafka.porperties、application-otherMiddleware.porperties等),和日志配置一族(logback.xml、log4j.xml等)。下面针对这几种配置文件进行分析。

application.porperties

文件分析

一般用于代码中的属性注入,例如:

@Component
public class MyBean {

    @Value("${name}")
    private String name;

    // ...

}

通过@Value注解,可以把properties中name的值注入到代码中,实现代码和配置文件的解耦合。它的实现依赖于设置java线程中的环境变量,因此它也可以通过命令行覆盖,例如:

java -jar xx.jar --name="nick"

执行上面命令,就会用命令行中的name代码properties中的name覆盖。

application.properties文件外提:

常用的有两种方式:

第一种,在主类上加一个注解@PorpertySource(value={"classpath:a/b/c/application.properties"})这种方式用于项目内文件移动。@PorpertySource(value={"file:/a/b/c/application.properties"})这种方式用于外提,需要使用绝对路径,尽量不要用相对路径。在jar外部创建对应的目录 /a/b/c/(这个目录不需要和代码中一致,在挂载的时候指向正确文件路径即可),并放入application.properties,如果是docker,使用-v /a/b/c/:/a/b/c/ 把宿主机的application.properties文件挂入容器即可。

第二种,在命令行中修改,通过-Dspring.config.location指定。

java -jar myproject.jar --spring.config.location=classpath:/default.properties,classpath:/override.properties

上面例子改为绝对路径的宿主机地址即可实现外部挂载配置。而且路径也支持通配符的方式进行挂载。

除此之外,还有--spring.config.additional-location支持自定义配置载入。

application.properties文件切换:

如果配置文件打在镜像内部,且有多个版本的配置(例如,application-test.porperties、application-dev.porperties、application-staging.porperties、application-product.porperties),在启动的时候根据环境选择对应的配置文件,执行命令:

-Dspring.profiles.active=dev

传入配置后部分名称进行选择,匹配对应的properties文件,默认是-default.properties。

也可以在application.properties中添加

spring.profiles.active=dev

区别上一个在命令行里指定的方式。但是它们的核心思想没变:把properties文件中的内容写到环境变量里,在application.properties中定义,则默认加载了application.properties所有内容,并把spring.profiles.active指定的properties文件加载到环境变量。此处需要注意,如果两个配置文件中如果有key重复,会发生覆盖的问题。 

application.properties使用占位符${}:

如果配置文件只有一份,且打在镜像内,可以考虑使用此方式。通过占位符的方式进行配置。

spring.datasource.url = ${OPENSHIFT_MYSQL_DB_HOST}:${OPENSHIFT_MYSQL_DB_PORT}/"nameofDB"
spring.datasource.username = ${OPENSHIFT_MYSQL_DB_USERNAME}
spring.datasource.password = ${OPENSHIFT_MYSQL_DB_PASSWORD}

这种方式需要在启动java进程的时候创建以上环境变量,在docker中可以通过 -e 的命令创建:

docker run --name my-tomcat -p 127.0.0.1:8080:8080 -e APP_DB_DB=mydb -e APP_DB_USER=dbuser -e APP_DB_PASS=dbpass --link mongo-myapp:mongo -v /path-to/tomcat/webapps:/usr/local/tomcat/webapps -d tomcat:8-jre8-alpine

在kubernetes中,yaml中的containers的下一层加入:

env:
          - name: db_host
            value: "127.0.0.1"
          - name: db_port
            value: 27017
          ...

logbak.xml

以logback.xml为例,外提分几种方式:

通过application.properties修改:

在application.properties中添加配置

logging.config=/home/dev-01/Documents/logback.xml

通过命令行修改:

java -jar xx.jar -Dlogging.config=/home/dev-01/Documents/logback.xml

通过include的方式修改日志内容:

这种方式比较特殊,也很非主流,在上述两种方式都不生效的情况下采取这种方式实现。经过查资料,说springboot加载logback配置只会在classpath下,如何外提都不生效(具体问题讨论),因此classpath下保留原始配置,但是把关键配置(appender)提取到另一个xml中,通过include的方式引入。

在classpath中的logback.xml中加入:

<include file="/a/b/c/logback-ext.xml">

logback-ext.xml中需要以<included>开始,包含主xml的关键信息即可。例如:

<included>
<appender>
  ...
</appender>
</included>

需要注意缩进,如果错误可能无法include成功。

configuration
  |
  |———— property(全局属性定义,用作占位符替换 ${xxx})
  |———— appender(日志输出方式)
  |       |
  |       |—— ConsoleAppender
  |       |__ RollingFileAppender
  |       |__ FileAppender...
  |———— logger(和代码中LoggerFactory.getLogger(xxx)返回的logger对象对应)
  |———— root (可以有多个,会分别执行)
  |———— include (这种特殊情况下会使用)

// 传入一个class,对应配置项中logger的name,clazz最后会用class.getName()寻找到logger,因此是以包名的全限定名决定。
// 如果 clazz是a.b.c.d,logger有name是a.b.c.d的就会匹配上,否则按a.b.c, a.b, a依次匹配
// 而且依次有继承关系,logger的继承是按包名的,且都会继承root
Logger LOGGER = LoggerFactory.getLogger(Class clazz);

// str 和 class.getName()一致
Logger LOGGER = LoggerFactory.getLogger(String str);

springboot 容器实例化_springboot 容器实例化