尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

SpringBoot应用迁移到宝兰德BES 9.5.5实战:从jar到war的全流程踩坑记录

SpringBoot应用迁移到宝兰德BES 9.5.5实战:从jar到war的全流程踩坑记录 1. 迁移前要想清楚的事为什么非得改造先说结论这次把一个跑了三年多的SpringBoot单体应用迁到宝兰德BES 9.5.5上前前后后折腾了小两周中间踩的坑比预想的多但回过头看大部分坑其实都源于最初对SpringBoot接管一切这个习惯的惯性依赖。如果你正准备做类似的信创改造我建议你先搞清楚核心问题你的项目是真的需要迁移到外部中间件还是只需要换一个部署包的形式信创改造这个词听着大落到我们这种业务系统上通常就是三件事操作系统从商用发行版换成国产环境数据库从Oracle/MySQL换成达梦、人大金仓这类国产库应用服务器从WebLogic、Tomcat这类换成东方通TongWeb、宝兰德BES或者金蝶天燕。技术栈里每一层都是可替换的但替换顺序很讲究。我们这次是数据库和中间件同时动看起来省事实际排查问题时变量太多后来复盘如果让两边错开节奏至少能省三分之一的时间。这篇内容适合谁看只要你的项目是SpringBoot写法的Java应用并且接下来要部署到宝兰德BES 9.5.5或者同类国产中间件上这篇文章就能用得上。你会看到我认为最关键的改造判断标准、一整套可以直接抄走的依赖配置以及我实际遇到过并处理掉的五个典型故障。这些内容不是从文档里抄来的是调试现场一行行日志查出来的。在动代码之前我先把最终要回答的几个问题列在这里后面逐一展开SpringBoot的jar包部署和war包部署在信创场景下到底选哪个BES 9.5.5对SpringBoot的Servlet规范支持到什么程度依赖该怎么配换掉Tomcat之后内嵌容器和外部容器之间有哪些隐藏差异日志、数据源、类加载这些看不见的手在迁移时怎么一次理顺2. 你真正需要改的jar包转war包的整体设计方案2.1 为什么不直接用jar包部署很多团队第一反应是SpringBoot自带Tomcat直接把jar包扔上去跑不就行了。话是没错但信创改造的场景下你绕不开一个事实监管和验收要求的往往是应用必须运行在国产中间件上而审计逻辑里中间件是否真正承载了应用判断标准之一就是应用是否以标准方式部署在中间件内部。你用jar包自己拉起一个内嵌Tomcat从技术上说得通但验收时可能被判定为尚未完成中间件替换。更现实的一点是运维侧。信创环境下的中间件通常由专门的运维团队统一管理应用以war包形式部署才能纳入BES的管理控制台做统一启停、告警监控、会话管理等。jar包那种进程自己拉起来的方式对运维体系来说是个孤岛。所以这次我们最终选择了标准war包部署让SpringBoot应用跑在BES的Web容器里。2.2 最常见的迁移路线图我们实际走的路线是四步顺序非常关键先把SpringBoot版本锁定到2.7.x系列后面细说为什么。改打包方式为war排除spring-boot-starter-web里内嵌的Tomcat同时引入provided范围的Tomcat依赖。增加SpringBootServletInitializer入口类让war包能被外部容器识别并启动。在BES上创建域、部署war包、逐个验证数据源和日志链路是否正常。这里要强调一个容易被忽略的点改动虽然集中在pom.xml和一个入口类但能不能启动只是开始启动之后的类加载顺序、日志输出路径、数据库驱动识别才算真正决定迁移成败。2.3 版本选型别让SpringBoot 3.x给你挖坑我们在预研阶段先试着用SpringBoot 3.2做过一次冒烟测试结果在BES 9.5.5上直接就卡住了。原因不复杂SpringBoot 3.x全面切换到Jakarta EE命名空间原来的javax.servlet一把梭变成了jakarta.servlet而BES 9.5.5的Servlet容器实现仍基于javax规范体系。你可以在依赖层面做各种适配但架不住底层的类库不认。这不是说BES不支持Jakarta而是在9.5.5这个版本上对SpringBoot 3.x这类基于Jakarta EE 9的应用支持成熟度明显不如javax时代。所以我们最终把项目锁在SpringBoot 2.7.18——这是2.x系列的最后一个版本bug修复最完整且完全基于javax规范和BES 9.5.5属于一个时代的产物。如果你手里的项目已经是SpringBoot 3.x也先别急着崩我的建议是先量化项目的整改成本看是不是值得为了中间件兼容性把代码里的javax逐批替换成jakarta再决定要不要强行落地。但如果时间紧我强烈建议用2.7.x做基线先把业务跑通后续再考虑升级。3. 宝兰德BES 9.5.5的环境准备与实例域配置3.1 安装包选择与license前置检查宝兰德BES的安装包分两种一种是图形化安装向导一种是免安装的tar.gz解压包。命令行习惯好的人直接选解压包在国产操作系统上操作起来最省心。我们的目标环境是一台ARM架构的服务器操作系统是麒麟系JDK用的是厂商配套的国产化OpenJDK 8。先强调一个前置动作BES这种商业中间件需要license授权文件不然实例能启动但到一定时间就会被强制退出。我们一开始没留意这件事部署完隔了一天再看域已经自动停了控制台连不上排查好久才发现是license没生效。安装路径我建议不要带空格和中文统一放在比如/opt/bes/ 下面因为后续的域信息、日志路径都会基于这个根路径拼接路经里有特殊字符容易引发各种莫名其妙的问题。解压完包里基本就是bin、modules、domain这几个核心目录bin下面放着启动脚本domain目录则是将来创建域的位置。3.2 创建BES域一个域就是一个独立应用服务器实例BES的域管理和WebLogic的domain是同一个概念理解成独立的BES运行实例就行。每个域有自己独立的端口、配置文件和日志目录。一个BES安装目录可以创建多个域但实际生产中建议一个域只跑一个应用隔离性最好。创建域推荐用命令行工具例如在bin目录下执行创建命令你要目前主要关注这么几个参数域名称建议和应用英文名一致比如demo-app-domain。HTTP服务端口应用对外访问的端口我们这次用了8088。管理控制台端口管理员网页控制台用的端口我们规划成8880。管理员账号和密码初次创建会要求设置后面登录控制台要用。创建完成后在域目录下会有bin/startDomain.sh这类脚本。启动之前先确认bin目录下的setEnv.sh或者域内的启动脚本里JAVA_HOME指向正确的JDK路径。我们环境里同时装了多个JDK一开始JAVA_HOME指到了商用JDK上虽然也能启动但基于信创验收要求最后统一切到了国产化JDK。这里有个坑如果切换了JDK记得把域里生成的一些缓存文件和临时目录清理掉不然会出现类版本报错。清完之后重启世界就安静了。3.3 控制台登录与端口占用排查域启动后浏览器访问管理控制台地址用前面设置的管理员账号登录。第一次登录建议立刻做两件事第一确认域处于运行状态第二到部署菜单里看看有没有默认自带的示例应用如果有先删掉避免和后面正规部署搞混。端口占用是高频问题特别是服务器上原来装过别的中间件。如果域启动时日志报告端口被占用最简单的办法是查看占用进程并处理掉旧进程然后把域停掉再重新启动。我见过有人改端口改了半天结果旧的域实例根本没停干净端口一直被占着新配置怎么都不生效。这类问题一定要养成先停旧域再改配置、再启新域的习惯。4. SpringBoot项目的依赖配置改造一份可以直接参考的pom.xml4.1 核心改动打包方式、排除Tomcat、引入外部容器这是整个迁移里最机械也最核心的一步。SpringBoot默认打成可执行jar应用内嵌Tomcat改成war部署后这个职责就交给了BES。所以pom里需要做三处调整。第一处 从jar改成war。第二处在spring-boot-starter-web里排除内嵌Tomcat否则war包里带着Tomcat类库部署到BES上会和容器自带的Servlet实现打架轻则警告重则直接启动失败。第三处单独引入一个Tomcat的依赖scope设置为provided意思是打包时带进war包但运行时不重复打包。这个provided依赖的作用不是让你继续用Tomcat跑而是让编译期间Servlet API的引用不报错。这里我把一份可用于SpringBoot 2.7.x迁移到BES 9.5.5的基础pom配置贴出来大家直接参照改即可。注意版本号要结合你们自己的依赖管理这里的依赖已经是最小可用集合。groupIdcom.example/groupId artifactIddemo-app/artifactId version1.0.0/version packagingwar/packaging parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.7.18/version relativePath/ /parent properties java.version1.8/java.version /properties dependencies !-- Web核心依赖排除内嵌Tomcat -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId exclusions exclusion groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-tomcat/artifactId /exclusion /exclusions /dependency !-- 编译期需要Servlet API运行期由BES提供 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-tomcat/artifactId scopeprovided/scope /dependency !-- 数据源相关根据实际项目保留 -- dependency groupIdcom.dameng/groupId artifactIdDmJdbcDriver18/artifactId version8.1.2.192/version /dependency !-- 其他业务依赖按原样保留 -- /dependencies build finalNamedemo-app/finalName plugins plugin groupIdorg.springframework.boot/groupId artifactIdspring-boot-maven-plugin/artifactId /plugin /plugins /build看到这里你可能注意到我连spring-boot-maven-plugin都没做特殊定制。因为你的目标是war包这个插件默认行为在打成war时会正常配合不需要额外写repackage配置。如果你自己原来的pom里对maven-war-plugin做过特殊过滤记得检查一下web.xml相关配置SpringBoot场景下不需要web.xml。4.2 入口类必须继承SpringBootServletInitializer改完pom程序入口类也要动一刀。原来的Application类只有SpringBootApplication注解war包部署到外部容器后外部容器不认识这个类的启动方式。正确做法是让它继承SpringBootServletInitializer并重写configure方法。下面的代码是从我们项目里抽出来的标准写法SpringBootApplication public class DemoApplication extends SpringBootServletInitializer { Override protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) { // 这里必须指定当前的启动类否则war包启动后会找不到Spring的ApplicationContext return builder.sources(DemoApplication.class); } public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }这个类继承很关键它其实就是SpringBoot对外部容器的接入适配器。没有这一段war包直接部署到BES日志里只会看到应用被BES加载了但始终不会执行SpringBoot的自动装配访问所有接口都是404。main方法可以保留保留的作用是以后还可以用java -jar方式本地调试。但生产部署以BES为准。需要注意如果你在configure里漏了sources指定或者指定错了类启动时大概率报错找不到主类这类问题排查起来非常耗时所以写的时候一次写对。4.3 数据源从Oracle/MySQL切到达梦的配置对照信创改造里数据库往往同步替换。我们原来用的是MySQL这次切到的是达梦DM8。SpringBoot层面改起来其实就两处pom里的驱动依赖和应用配置文件里的连接参数。以达梦为例maven驱动坐标就是上节pom里写明的DmJdbcDriver18版本要和数据库服务端版本保持一个量级。如果你们拿到达梦的驱动jar也可以直接安装到本地maven仓库再按坐标引用但更推荐走公司私服方便统一管理。连接参数变化不大差异在driver-class-name和url。这是我们从MySQL迁到DM8之后的配置片段spring.datasource.driver-class-namedm.jdbc.driver.DmDriver spring.datasource.urljdbc:dm://127.0.0.1:5236/DAMENG?compatibleModemysql spring.datasource.usernameyour_user spring.datasource.passwordyour_password这里特别注意url里我加了compatibleModemysql参数。达梦的兼容模式可以帮你减少很多SQL改写工作尤其是项目里用了MySQL特有的分页写法、函数时这个参数能让达梦尽量向下兼容MySQL语法。但别想着完全无感切换limit这类写法建议还是动一遍SQL。这些语法问题在测试阶段一定会暴露越早扫SQL越省心。如果你的数据库是人大金仓KingbaseES连接配置类似driver换成com.kingbase8.Driverurl换成jdbc:kingbase8://ip:port/dbname。思路完全一样都是驱动替换URL替换方言适配。4.4 日志和外部配置的额外处理SpringBoot应用跑在BES里日志输出是不可忽略的环节。本地用java -jar启动时logback把日志打到控制台是分分钟的事但放到外部容器后控制台日志被BES接管你可能在BES的日志里看到SpringBoot的输出也可能看不到取决于日志框架之间的关系。我们的做法很直观给logback配置里指定一个不依赖系统控制台的日志文件路径用绝对路径输出例如/data/logs/demo-app/app.log。并在logback.xml里把日志级别调整到INFO以上。如果你不想改代码也可以在BES的启动参数里通过-Dlogging.file.name指定日志文件位置但我更推荐前者写在配置里更直观换中间件也不受影响。另外原来jar包里习惯用application.yml的绝对外部路径覆盖比如--spring.config.location/opt/config/application.yml。在BES里这个参数要通过域启动脚本的JAVA_OPTS传进去或者写到BES控制台的服务器启动参数里。这一步特别容易忘我们上线前联调时发现配置没生效排查半天就是因为只改了命令行参数但BES域的启动进程没拿到这个参数。5. 部署实战与高频踩坑实录5.1 部署war包到BES域里的标准步骤部署过程并不复杂之前没操作过的人可以先在测试环境走一遍流程步骤很简单在BES管理控制台里找到部署菜单选择安装或部署应用程序。选择你刚打好的demo-app.war应用名称会自动识别建议明确写成demo-app方便后续维护。部署目标选择前面创建好的server实例。确认启动策略通常选随服务器启动。点确认后直接启动应用。启动完成后通过BES的HTTP端口访问应用上下文例如http://ip:8088/demo-app/。注意这个上下文路径就是war包名称默认通过/demo-app访问如果想让用户直接通过根路径/访问可以在部署时设置上下文根为/或者把war包改名为ROOT.war再部署。5.2 高频问题启动后访问404、类找不到、数据源连接失败我把迁移过程中实际遇到且修复过的高频故障整理成了一个表每个问题都是我或同事在项目上真碰过的不是从手册里摘的。现象根本原因解决方案war包能启动但访问接口全部404入口类没有继承SpringBootServletInitializer按4.2的代码补全入口类重新打包部署启动时提示NoSuchMethodError或ClassNotFoundExceptionBES自带的类库和应用war包lib里同名类冲突在BES控制台调整应用类加载策略改为优先应用内类数据源连接失败报驱动类找不到war包内没有包含数据库驱动或驱动版本不对把对应驱动的jar通过Maven引入并确保打进war包同时核对driver-class-name日志没有任何输出logback配置与控制台日志冲突或没指定文件输出在logback.xml里配置独立的文件appender路径用绝对路径BES域启动后一段时间自动停止license未生效或已过期检查BES安装目录下的license文件联系厂商获取正确授权中文乱码服务器系统编码不是UTF-8在BES启动参数里加-Dfile.encodingUTF-8并设置file.encoding5.3 类加载冲突的系统性解法类加载冲突是外部容器部署里最顽固的一类问题表现形式千奇百怪但根子基本上只有一个同一个类在BES模块里有一份在你的war包里又有一份JVM加载了错误的那份。处理这类问题我的排查经验分三步。第一步看启动日志里是否有ClassLoader相关的警告信息BES启动时通常会提示存在类加载器冲突。第二步去war包解压目录下的WEB-INF/lib里搜一下报错类所在的jar是不是真的在里面如果确实有再对比BES模块里有没有同名的包。第三步在BES控制台的应用部署配置里把类加载器顺序设为子类优先或应用优先。多数情况下调整类加载顺序就能解决。如果还不行才考虑把war包lib里冲突的jar移除或改名。但这种做法是下策每次升级都要再处理一次不建议作为长期解决方案。5.4 关于静态资源和Session的兼容性SpringBoot项目的静态资源JS、CSS、图片在jar包部署时通常由Spring Boot自己的资源映射处理。迁移到BES后只要war包结构标准这部分不会有大问题但要注意一下路径。如果应用里有/devtools相关的依赖务必在打包时排除掉devtools在外部容器环境下会带来一些莫名其妙的热加载报错我们就被坑过一次。Session这块如果原来用的是SpringSession Redis迁移后基本不受影响因为会话数据已经抽到Redis里中间件只负责请求转发。如果是单机Session迁移后要确认BES的Session过期机制配置默认30分钟可以按需调整。但从高可用角度还是建议尽早接分布式Session。6. 上线前验证清单和性能参数调整建议6.1 功能回归与数据库兼容性验证信创环境下的上线验证工作要做得比普通发版细得多。我按模块列一个验证清单方便你们直接拿去用。登录认证流程是否完整密码加密方式在切换JDK后是否还一致。所有查询、分页、排序的SQL是否正常重点检查日期函数、字符串函数、子查询和分页语法。事务回滚是否正常可以在测试环境故意制造一条失败数据观察是否回滚。文件上传下载功能是否正常BES默认可能有请求体大小限制需要调大max-post-size参数。消息队列、定时任务等组件是否注册成功定时任务不能重复执行。外部系统接口调用是否正常如果用了HTTPClient确认连接超时和读超时参数没变。数据库这一块尤其建议用慢SQL日志或达梦的监控视图跟踪一下前期运行情况看看有没有之前MySQL上不明显的性能问题在达梦上暴露出来。6.2 BES的JVM参数调整BES默认的JVM内存参数偏向保守生产环境要按应用规模调整。我们的做法是在域启动脚本的JAVA_OPTS里显式指定-Xms2048m -Xmx2048m -XX:MaxMetaspaceSize512m -Xss512k和普通Java应用一样-Xms和-Xmx建议设为相同值避免运行期堆内存动态伸缩带来的性能抖动。如果部署机内存足够应用并发量又高可以把堆设到4G甚至更大但先通过压测确定基线再调整。还要注意一个参数BES对应用部署超时有时比较严格如果应用启动比较慢可能出现部署成功但应用还没起来的情况。可以在BES的部署配置里调大启动超时时间或者等应用日志出现SpringBoot启动完成的标志后再把应用标记为可用。6.3 迁移后一个月的观察重点上线后头一个月的观察期我建议核心盯三样东西BES的运行日志里有没有持续增长的异常堆栈数据库连接池有没有泄漏导致连接数飙升以及应用日志输出到文件这块是否持续稳定。尤其日志很多问题是上线后一周才暴露的——比如某个定时任务每天跑一次跑到第五天才发现它把日志打到了BES的系统目录把中间件所在磁盘硬生生写满了。这个问题我们反复遇到最佳策略是从第一天就给日志文件外加logrotate滚动策略并按天归档。还有一个小细节BES的管理控制台账号密码建议上线后立刻改成强密码并且不要和业务代码里任何配置共用同一套凭证。中间件属于基础安全设施控制台一旦被拿到应用层做得再好都没用。从个人经验讲每次做这类信创改造我都会在迁移完成后的至少两周内保留一条快速回退路径保留旧环境的完整备份、部署脚本、数据库导出文件。不是对国产软件不信任而是回退方案本身就是一个成熟系统必须具备的能力。即使一切顺利这条回退路径也得在并且要是实操过一遍能真正跑起来的而不是停留在文档里。
返回列表