Java 11迁移成功案例


这篇文章总结了如何成功将微服务从Java 8迁移到Java 11。
对于每项服务,已经完成了以下步骤:

  • 使用Java 11编译代码
  • 在Java 8上运行Java 11兼容服务
  • 在Java 11上运行该服务

实际上,我们有一些额外的步骤,因为当我们开始迁移时,Java 11还没有发布,我们只能使用Java 10. 假设代码是在Java 10上编译的,那么迁移到的工作就不会那么多了。 Java 11和Jigsaw项目引入了Java 11作为模块化的最大变化。谢天谢地,就是这样!

1.使用Java 11编译代码
我们不得不提高我们正在使用的大多数框架和工具的版本。特别是,我们必须处理从Spring Boot 1到2以及Spring 4到5的迁移。由于这些是主要版本,我们必须修复几个重大变化。
对于Spring Boot 2,Spring Boot 2.0Spring Boot 2.1的官方迁移指南编写得很好并且详细。

  • 配置文件加载已经演变
  • 属性轻松绑定是有点不够放得开
  • 某些属性已重命名,其他属性不可用(例如security.basic.enabled property,必须替换为a WebSecurityConfigurerAdapter)
  • 一些端点被重命名(例如执行器健康检查)
  • 现在默认禁用bean重载,这是我们在集成测试中使用的东西,我们不得不使用新属性重新启用它spring.main.allow-bean-definition-overriding。

在Spring5迁移是从视图与一些小的改动代码点非常简单。困难的部分与项目遗留的事实有关,我们必须处理复杂的Spring XML配置和迁移到Dropwizard Metrics 4。

很少有框架仍然与Java 9+不兼容,除非它们没有得到社区的积极维护。在我们的案例中,我们必须找到Cassandra Unit的解决方法。我们不打算花时间更改测试框架,因此我们计划迁移到DynamoDB。

我们还必须处理Maven依赖地狱,因为一些必需的依赖项带来了与Java 9+不兼容的旧依赖项。在大多数情况下,在POM中添加一些排除项解决了这个问题。

我们在bash配置文件中添加了一组简单的别名,以便在Java版本之间切换。

alias setjava8=”export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/”
alias setjava10=”export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk-10.0.2.jdk/Contents/Home/”
alias setjava11=”export JAVA_HOME=/Library/Java/JavaVirtualMachines/openjdk-11.jdk/Contents/Home/”

在IntelliJ上,可以从项目设置(“Project SDK”下拉列表)更改Java版本。

CI环境:
对于持续集成方(我们使用Bamboo),我们更新了代理以使用Java 11. 我们注意到,由于计划配置是全局的,因此不可能为分支计划和主计划提供不同版本的代理。这意味着如果其他人正在将更改推送到master(例如,新功能或完全独立于Java 11迁移的错误修复),则将代理更新为Java 11将破坏master。
为了缓解此问题并避免红色构建,我们必须确保项目正在使用Java 11进行编译,并且在更新代理之前所有测试都在本地传递,以便快速合并Java迁移拉取请求。另一个选择是在Java 11上分支计划为绿色时暂时将代理程序设置为Java 8,而不会忘记在合并之前将其设置回Java 11。

2.在Java 8上运行Java 11兼容服务
一旦所有内容都修复,合并并且主构建为绿色,我们必须确保Java 11兼容版本在我们的测试环境中正常运行。基本上确保没有任何损坏...我们有单元,集成和端到端测试,所以我们的信心水平相当高。
为了安全起见,我们对API进行了一些额外的探索性和手动测试,并提供了一些异常和边缘案例请求,以确保其行为正确。我们还确保日志和Grafana仪表板都很好。
下一步是将新版本推向生产。即使代码与Java 11兼容,该服务仍然使用Java 8运行,我们不想同时引入太多更改,毕竟我们不喜欢有风险的版本。由于多次重构和版本提升,我们特别小心处理此版本。在查看Grafana仪表板几天后,比较迁移前后的指标,结果表明一切顺利。

3.在Java 11上运行服务
最终目标是在Java 11上运行服务。理论上,它将像更新Docker文件一样简单,比如使用Java 11映像并在生产中推送工件。然而,在实践中它并不那么简单......
首先,我们必须更新Java JVM参数(该-d64[url=https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8180286]参数已弃用[/url],将阻止服务启动,我们还必须更新GC日志参数)
然后,我们很快意识到服务日志已经从我们的本地生产环境中的Splunk中消失了,这些日志实际上是在AWS上正常工作时显示的。我们必须以更新的logback配置来解决这个“时间失真”;通过更新)日期模式从%d{ISO8601}到%d{yyyy-MM-dd’T’HH:mm:ss.SSSZ,UTC}。

在部署到生产期间出现了另一个奇怪的错误VerifyError: Bad type on operand stack,AppDynamics因为一些奇特的字节码操作而阻止某些实例启动。由于某些原因,它在prod-canary上很好,然后在几个实例上成功部署后开始失败!我们不得不禁用AppDynamics,这很好,因为我们在团队中没有使用此工具。

当我们转向Java 11时,我们还必须更新一些Grafana仪表板以使用新垃圾收集器的G1。

结论
今天,我们项目中使用Spring Boot 2提供用户通知的3个服务,以及使用Spring 5提供网站页眉和页脚的1个服务已经在Java 11上运行顺利有几个星期了。使用默认的G1垃圾收集器,我们没有遇到任何与内存占用或任何其他性能问题相关的奇怪行为。另一方面,我们的响应时间没有任何改善。
下一步是什么?Java 12将于2019年3月发布。目前,我们仍然不知道是否将使用此版本或等待下一个Java LTS。它可能取决于包含哪些功能。