当前位置: 首页 > news >正文

springboot单体项目的发布生产优化

需求背景:

公司有个springboot单体项目,日志框架采用log4j2,是maven聚合工程,运行在Windowsserver服务器(不是Linux,哈哈..    log4j2框架2.13.3 左右的版本有严重漏洞,这个要升级哦,这里就不多说了)

yml文件的配置就是:

1、主配置文件application.yml

2、子配置文件:application-dev.yml  (用于开发环境)

3、子配置文件:application-prod.yml  (用于生产环境)

然而,这个项目卖给多个客户时,配置文件就会变得非常多,比如:

需求1:

那么,部署到某个客户服务器时,其他客户的配置文件是不是得删掉?如果不删就暴露了其他客户的配置信息,这是不妥的!所以在maven打包的时候,就要去掉其他客户的配置文件!

需求2:

A客户的springboot项目部署到 X服务器后,为节约成本,B客户的springboot项目也部署到X服务器。那么就会有个问题,log4j2.xml的保存目录要改,比如

A客户的日志文件改成:

<property name="LOG_FILE_PATH" value="/A客户/mylog" />

B客户的日志文件改成:

<property name="LOG_FILE_PATH" value="/B客户/mylog" />

这样每次手动改,就很烦,所以要做成变量形式!

其他:

项目是springboot 2.3.4.RELEASE 版本的,网上的一些解决方案在此版本是无效的,虽然可以通过升级springboot版本解决一些问题,但是公司jdk是1.8的,如果升级springboot版本,那也得升级jdk,那么多个客户,能用就行,谁吃饱撑着浪费时间去升级,除非公司要求

需求1解决方案

打包前,修改主配置文件application.yml为

spring.profiles.active: qx

( 以上的修改也可以通过maven配置来实现,比如定义<profiles> )

在子pom.xml文件中增加插件:gmavenplus-plugin,并排除所有以 `application-` 开头的 YAML 文件。原理就是,执行resource的时候,不要把任何yml文件打包进来,等执行maven-resources-plugin插件的时候再打包相应的yml文件。那么最终只有application.yml和application-qx.yml会打包到jar包中

<?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"><parent><artifactId>YMRoot</artifactId><groupId>com.yema</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>ym-terminal-boot</artifactId><dependencies><dependency><groupId>com.yema</groupId><artifactId>ym-common</artifactId></dependency></dependencies><build><finalName>ym-terminal-boot</finalName><plugins><plugin><!--该插件主要用途:可执行的 JAR / WAR  --><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin><plugin><groupId>org.codehaus.gmavenplus</groupId><artifactId>gmavenplus-plugin</artifactId><version>2.1.0</version><dependencies><dependency><groupId>org.codehaus.groovy</groupId><artifactId>groovy</artifactId><version>3.0.20</version></dependency></dependencies><executions><execution><id>read-application-yml</id><!--Maven 生命周期阶段 validate → initialize → generate-sources → process-resources → compile → ...--><phase>initialize</phase><goals><goal>execute</goal></goals><configuration><scripts><script><![CDATA[// 安全处理Windows路径def baseDir = project.basedir.toString().replace('\\', '/')def ymlPath = baseDir + "/src/main/resources/application.yml"// 调试输出println "Attempting to load YML from: " + ymlPath// 读取文件def ymlFile = new File(ymlPath)if (!ymlFile.exists()) {throw new RuntimeException("YML配置文件未找到: " + ymlPath)}// 解析YMLdef config = new org.yaml.snakeyaml.Yaml().load(ymlFile.newInputStream())// 多层级尝试获取profiledef activeProfile = config?.get('spring')?.get('profiles')?.get('active') ?:config?.get('spring.profiles.active') ?: 'dev'// 设置Maven属性project.properties.springActive = activeProfileprintln "成功设置激活的profile: " + activeProfile]]></script></scripts></configuration></execution></executions></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-resources-plugin</artifactId><version>3.3.0</version><executions><execution><id>filter-resources</id><!--Maven 生命周期阶段,要比gmavenplus插件阶段迟才能拿到${springActive}的值。validate → initialize → generate-sources → process-resources → compile → ...--><phase>process-resources</phase><goals><goal>resources</goal></goals><configuration><!-- 覆盖之前的资源配置 --><resources><resource><directory>src/main/resources</directory><filtering>true</filtering><includes><include>application.yml</include><include>application-${springActive}.yml</include></includes></resource></resources></configuration></execution></executions></plugin></plugins><resources><resource><!-- 资源文件所在目录 --><directory>src/main/resources</directory><!-- 禁用占位符替换 --><filtering>false</filtering><excludes><!-- 排除所有以 `application-` 开头的 YAML 文件 --><exclude>application-*.yml</exclude></excludes></resource></resources></build>
</project>

需求2解决方案

log4j2-spring.xml (必须叫这个名字,如果是log4j2.xml启动会有报错信息,虽然不关紧要,但看着报错就难受) ,log4j2.xml的配置如下,基本上不用动什么,有日期能压缩,满足生产需求了。要变动的就是增加了变量${sys:ACTIVE_PROFILE} ,这个ACTIVE_PROFILE变量实际上是spring.profiles.active的值,要在启动springboot的main方法中去设置下。原理就是:在启动springboot前,解析application.yml主配置文件获取active的值(因为看active 就知道是哪个客户了),设置为系统属性,然后日志文件中用${sys:} 来获取(网上说的${spring:} 这些都不行)

<?xml version="1.0" encoding="UTF-8"?>
<!--
configuration 节点的属性:
status:用来指定log4j本身的打印日志的级别.
monitorinterval:用于指定log4j自动重新配置的监测间隔时间,单位是s,最小是5s.
-->
<!--
日志级别以及优先级排序(由高到低): OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL
--><configuration status="DEBUG" monitorinterval="60"><!--声明属性--><Properties><!--这里的log.path是定义日志存放的地方,此处是存放于项目根路径下的/sf.xlogs文件夹中--><!--        <Property name="log.path">/sf.xlogs</Property>--><!-- 格式化输出:%date表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度 %msg:日志消息,%n是换行符--><!-- %logger{36} 表示 Logger 名字最长36个字符    --><property name="LOG_PATTERN" value="%date{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36}(%F:%L) - %msg%n" /><!-- 定义日志存储的路径 --><property name="LOG_FILE_PATH" value="/XLogs/terminal-${sys:ACTIVE_PROFILE}" /><property name="FILE_NAME" value="terminalFile" /></Properties><!--声明日志输出位置--><Appenders><!--Console节点用来定义输出到控制台的Appendername : 指定Appender的名字,用于Logger节点引用target : SYSTEM_OUT 或 SYSTEM_ERR,一般设置为:SYSTEM_OUTPatternLayout : 指定日志输出格式,默认为%m%n--><Console name="Console" target="SYSTEM_OUT"><PatternLayout pattern="${LOG_PATTERN}"/>  <!--[%d{HH:mm:ss:SSS}] [%p] [%t] - %l - %m%n--></Console><!--File节点用来定义输出到指定位置的文件name:指定Appender的名字.fileName:指定输出日志的目的文件带全路径的文件名.PatternLayout:输出格式,不设置默认为:%m%n.--><File name="XLogs" fileName="${LOG_FILE_PATH}/xlogs.log"  append="false"><PatternLayout pattern="${LOG_PATTERN}"/>   <!--[%d{HH:mm:ss:SSS}] [%p] [%t] - %l - %m%n--><!-- 控制台只输出info其以上级别的信息(onMatch),其他的直接拒绝(onMismatch)--><ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/></File><!--RollingFile 节点用来定义超过指定大小自动删除旧的创建新的的Appendername:指定Appender的名字.fileName:指定输出日志的目的文件带全路径的文件名.PatternLayout:输出格式,不设置默认为:%m%n.filePattern:指定新建日志文件的名称格式.Policies:指定滚动日志的策略,就是什么时候进行新建日志文件输出日志.ThresholdFilter : 日志过滤器(如果存在多个日志过滤器,则使用<Filters></Filters>包含住)TimeBasedTriggeringPolicy:Policies子节点,基于时间的滚动策略,interval属性用来指定多久滚动一次,默认是1 hour。modulate=true用来调整时间:比如现在是早上3am,interval是4,那么第一次滚动是在4am,接着是8am,12am...而不是7am.SizeBasedTriggeringPolicy:Policies子节点,基于指定文件大小的滚动策略,size属性用来定义每个日志文件的大小.DefaultRolloverStrategy:用来指定同一个文件夹下最多有几个日志文件时开始删除最旧的,创建新的(通过max属性)。只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch) DENY(拒绝),NEUTRAL(中立),ACCEPT(允许)该RollingFile的Filters 代表的意思是,只输出info级别的日志,【先拒绝日志,后接受日志】顺序不能颠倒<RollingFile name="RollingFile" fileName="${log.path}/spring-trade-rolling.log"filePattern="${log.path}/logs/${date:yyyy-MM}/info-%d{yyyy-MM-dd}.log.zip"><Filters><ThresholdFilter level="warn" onMatch="DENY" onMismatch="NEUTRAL"/><ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/></Filters><PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] [%t] - %l - %m%n"/><Policies><TimeBasedTriggeringPolicy modulate="true" interval="1"/></Policies></RollingFile>--><!-- 这个会打印出所有的info及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档--><RollingFile name="RollingFileInfo" fileName="${LOG_FILE_PATH}/info.log" filePattern="${LOG_FILE_PATH}/logs/${FILE_NAME}-INFO-%d{yyyy-MM-dd}_%i.log.zip"><!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)--><ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/><PatternLayout pattern="${LOG_PATTERN}"/><Policies><!--interval属性用来指定多久滚动一次,默认是1 hour--><TimeBasedTriggeringPolicy modulate="true" interval="1"/><SizeBasedTriggeringPolicy size="10MB"/></Policies><!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖--><DefaultRolloverStrategy max="15"/></RollingFile><!-- 这个会打印出所有的warn及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档--><RollingFile name="RollingFileWarn" fileName="${LOG_FILE_PATH}/warn.log" filePattern="${LOG_FILE_PATH}/logs/${FILE_NAME}-WARN-%d{yyyy-MM-dd}_%i.log.zip"><!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)--><ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/><PatternLayout pattern="${LOG_PATTERN}"/><Policies><!--interval属性用来指定多久滚动一次,默认是1 hour--><TimeBasedTriggeringPolicy modulate="true" interval="1"/><SizeBasedTriggeringPolicy size="10MB"/></Policies><!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖--><DefaultRolloverStrategy max="15"/></RollingFile><!-- 这个会打印出所有的error及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档--><RollingFile name="RollingFileError" fileName="${LOG_FILE_PATH}/error.log" filePattern="${LOG_FILE_PATH}/logs/${FILE_NAME}-ERROR-%d{yyyy-MM-dd}_%i.log.zip"><!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)--><ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/><PatternLayout pattern="${LOG_PATTERN}"/><Policies><!--interval属性用来指定多久滚动一次,默认是1 hour--><TimeBasedTriggeringPolicy modulate="true" interval="1"/><SizeBasedTriggeringPolicy size="10MB"/></Policies><!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖--><DefaultRolloverStrategy max="15"/></RollingFile></Appenders><!--Root节点用来指定项目的根日志,如果没有单独指定Logger,那么就会默认使用该Root日志输出level:日志输出级别,共有8个级别,按照从低到高为:All < Trace < Debug < Info < Warn < Error < Fatal < OFF.AppenderRef:Root的子节点,用来指定该日志输出到哪个Appender.Logger节点用来单独指定日志的形式,比如要为指定包下的class指定不同的日志级别等。level:日志输出级别,共有8个级别,按照从低到高为:All < Trace < Debug < Info < Warn < Error < Fatal < OFF.name:用来指定该Logger所适用的类或者类所在的包全路径,继承自Root节点.AppenderRef:Logger的子节点,用来指定该日志输出到哪个Appender,如果没有指定,就会默认继承自Root.如果指定了,那么会在指定的这个Appender和Root的Appender中都会输出,此时我们可以设置Logger的additivity="false"只在自定义的Appender中进行输出。--><!--<Loggers>&lt;!&ndash;过滤掉spring和mybatis的一些无用的All,Trace,Debug级别的日志信息&ndash;&gt;<logger name="org.springframework" level="INFO"/><logger name="com.spring" level="INFO"/>&lt;!&ndash;additivity设置成false:org.mybatis包下的日志,只会打印到LogFile的appender中,不会再打到Root中定义的appender中additivity设置成true或不设置:日志除了打到LogFile的appender中,还会打印到Root中定义的appender的中&ndash;&gt;<logger name="org.mybatis" level="INFO" additivity="false"><appender-ref ref="LogFile"/></logger>&lt;!&ndash;level代表只输出info级别以上的日志&ndash;&gt;<root level="info"><appender-ref ref="Console"/><appender-ref ref="RollingFile"/></root></Loggers>--><!--Logger节点用来单独指定日志的形式,比如要为指定包下的class指定不同的日志级别等。--><!--然后定义loggers,只有定义了logger并引入的appender,appender才会生效--><loggers><!--过滤掉spring和mybatis的一些无用的DEBUG信息监控系统信息   若是additivity设为false,则 子Logger 只会在自己的appender里输出,而不会在 父Logger 的appender里输出。--><Logger name="org.springframework" level="info" additivity="false"><AppenderRef ref="Console"/></Logger><logger name="com.spring" level="INFO"/><logger name="org.mybatis" level="info" additivity="false"><AppenderRef ref="Console"/></logger><root level="info"><appender-ref ref="Console"/><appender-ref ref="XLogs"/><appender-ref ref="RollingFileInfo"/><appender-ref ref="RollingFileWarn"/><appender-ref ref="RollingFileError"/></root></loggers></configuration>

springboot启动入口

package com.yema;import org.apache.commons.lang3.StringUtils;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.yaml.snakeyaml.Yaml;import java.io.IOException;
import java.io.InputStream;
import java.util.Map;@EnableScheduling
@SpringBootApplication
public class TerminalApplication extends SpringBootServletInitializer {public static void main(String[] args) throws IOException {System.setProperty("ACTIVE_PROFILE", getActive());SpringApplication.run(TerminalApplication.class,args);}@Overrideprotected SpringApplicationBuilder configure(SpringApplicationBuilder application) {return application.sources(TerminalApplication.class);}/*** 获取启动环境配置值* @return* @throws IOException*/private static String getActive() throws IOException {//1、先从系统属性获取String property = System.getProperty("spring.profiles.active");if (!StringUtils.isBlank(property)){return property;}//2、系统属性没有就获取主配置文件的Yaml yaml = new Yaml();Map<String, Object> config;try (InputStream in = TerminalApplication.class.getClassLoader().getResourceAsStream("application.yml")) {config = yaml.load(in);}// 安全获取 active(默认返回 null 或默认值)if (config != null) {if (config.get("spring.profiles.active") != null){return config.get("spring.profiles.active").toString();}Map<String, Object> springMap = (Map<String, Object>) config.get("spring");if (springMap != null) {Map<String, Object> profilesMap = (Map<String, Object>) springMap.get("profiles");if (profilesMap != null) {Object active = profilesMap.get("active");if (active != null) {return active.toString();}}}}return "default";}
}

需求2的Java部分,其实还有其他方式,比如在执行命令的是主动设置参数,看个人喜好。

总之,单体项目打包好,扔服务器执行即可。对于Windowsserver来说,写个bat文件啥的就满足了。好维护,简单粗暴。

论技术,肯定喜欢微服务啥的全套上

实际上,很多企业并发量用户量没多少,买个服务器硬件性能都抠抠搜搜

http://www.dtcms.com/a/270102.html

相关文章:

  • DMA(直接内存访问)是什么?
  • 第2章,[标签 Win32] :匈牙利标记法
  • 13届蓝桥杯省赛程序设计试题
  • 字符串大小比较的方式|函数的多返回值
  • 作业03-SparkSQL开发
  • 数字化校园升级:传统网络架构与SD-WAN智能方案对比详解
  • 汽车功能安全-软件单元验证 (Software Unit Verification)【定义、目的、要求建议】6
  • 【数据分析】基于 HRS 数据的多变量相关性分析与可视化
  • uniapp b树
  • C++笔记之使用bitset对uint32_t类型变量对位状态判断
  • 2025年深圳杉川机器人性格测评和Verify测评SHL题库高分攻略
  • 论文略读:Parameter-efficient transfer learning for NLP
  • InstructBLIP:迈向具备指令微调能力的通用视觉语言模型
  • Go语言标识符命名规则详解:工程化实践
  • Spring的依赖注入(xml)
  • RISC-V:开源芯浪潮下的技术突围与职业新赛道 (一)为什么RISC-V是颠覆性创新?
  • 安装 asciidoctor-vscode 最新版
  • 针对 SSD 固态硬盘的安全擦除 Secure Erase
  • Kotlin协程中的Job详解
  • 如何用Python编程计算权重?
  • Anolis OS 23 架构支持家族新成员:Anolis OS 23.3 版本及 RISC-V 预览版发布
  • 数据库设计精要:完整性和范式理论
  • 去掉长按遥控器power键后提示关机、飞行模式的弹窗
  • 数据提取之lxml模块与xpath工具
  • 基于Java+SpringBoot 协同过滤算法私人诊所管理系统
  • 系统架构设计师论文分享-论系统安全设计
  • IoTDB:专为物联网场景设计的高性能时序数据库
  • 把word中表格转成excle文件
  • 基于GeoTools的根据Shp文件生成完全包围格网实战
  • Oracle 存储过程、函数与触发器