首页 > 其他分享 >一文解读业务平台升级JDK11的适配之路

一文解读业务平台升级JDK11的适配之路

时间:2023-03-03 13:22:25浏览次数:56  
标签:CATALINA 一文 适配 JDK11 升级 XX JDK8 OPTS

本文基于两个出发点,描述了业务平台于21年12月启动了对JDK版本升级的适配之路,并回顾了整个升级过程,对升级过程中的问题做了记录。
业务平台升级JDK11,基于两个出发点:一、jdk8于2019年1月停止维护,springboot2.1之后的版本已经兼容JDK11,springboot3.0完全放弃对JDK8的支持,未来属于更高版本的JDK;二、在试点国产化芯片的过程中,由于JDK8对Arm架构的优化不足,导致国产化芯片无法发挥自身的性能优势,为了更好的适配国产化,务必要求对JDK版本进行升级。基于上述两个出发点,业务平台于21年12月启动了对JDK版本升级的适配之路。这里回顾整个升级过程,对升级过程中的问题做一下记录。

一、升级版本的选择

当时有两个JDK版本可供选择,JDK11、JDK17,从长远来看,JDK17更代表未来,但由于JDK11 相比JDK17的变化并不大,相比之下从JDK11升级到JDK17的挑战要相对小。由于升级的基本都是电商核心系统,基于风险的控制,从JDK8升级到JDK11,随后再从JDK11升级到JDK17是一个更合理的选择。

二、GC选择及功能适配

业务平台核心系统使用了非常多的定制化功能,比如GCIH、CMS GC、异步日志、Wisp、Jwarmup、UncommonNullCast、多租户,为了减少对业务的影响,同时保障JDK11相比JDK8不会产生性能的退化,相关的特性均需要在JDK11上进行适配。同时为了避免GC算法的调整对业务运维及稳定性的影响,升级初期决定依旧使用CMS GC,仅灰度少量的G1。
特性
特性说明
GCIH 
营销利用GCIH缓存营销数据,大促强依赖的特性
CMS GC
核心业务使用的GC算法,为了避免对业务使用上的影响,需要保留Cms GC的支持
异步GC日志
GC日志异步化,降低业务的影响
Wisp
协程支持,buy2、carts2上均在使用,对业务的性能3-5%的性能优化
JWarmup
启动预热,核心系统利用该功能降低启动过程中C1、C2带来的CPU飙升,业务抖动问题
UncommonNullCast
-XX:CompileCommand=
退优化相关的优化参数,避免部分场景下退优化带来的性能问题
-Dcom.alibaba.jdk.verifyECDSATrailing
跳过ECDSA尾部零校验,兼容老的ECDSA数据,Tp3和支付宝交互中,需要依赖

 

三、升级JDK11相关组件的升级

在升级JDK11的过程中,业务代码的改造只是其中之一,还涉及到业务依赖的各种组件之间的升级适配,包括Pandora、Tomcat、星环容器、天启、Doom、天擎等测试工具的升级支持。
组件
升级改造点
Tomcat 
非Pandoraboot的应用,强依赖Tomcat,目前大部分业务使用的依旧是Tomcat7,但Tomcat7 本身对JDK11的支持存在问题(ECJ的版本过低,在存在动态编译代码的情况下,比如:jsp、模板预演、groovy等,由于不识别jdk11,将目标代码编译成低版本的代码,导致性能及功能受损)。由于tomcat7 和tomcat8对jar包加载的顺序有了调整,导致升级到tomcat8成本会相对较高,为了降低改造成本,对tomcat7进行改造。(https://bz.apache.org/bugzilla/show_bug.cgi?id=57129) 
最新的版本:taobao-tomcat-7.0.108.ecj
Pandora
Pandora各组件对JDK11的版本支持,在项目启动之初,Pandora已经在对JDK11进行了适配,但在实际升级使用中,依旧发生了一些不兼容的问题,期间主要有
  • 服务端使用JDK11版本运行,客户端使用JDK8的场景下,服务端抛的异常经过Hession序列化后,无法在JDK8上进行反序列化的情况。该问题前期评估,主要是客户端感知的错误发生变化,风险可控,实际在上线过程中,消费者侧的感知发生变化。
最新的版本:2022-07-stable
天启
天启作为业务平台最核心的测试用力回归工具,在整个适配开始之初,就作为高优先级适配,天启内部使用了很多序列化及记录Record的过程,本身为了做代码增强,使用了相对较多的hack的方式,因此这部分改造及适配成本最大,适配过程中,一方面需要调整doom的依赖,另外一方面需要保障测试用力能正常回归
天擎
天擎作为业务平台预发环境隔离的工具,预发回归测试均需要进行依赖。这部分的改造也需要提前进行适配。

 

四、灰度及上线流程的选择

业务平台核心应用很多都是伴随taobao业务成长起来的应用,具有历史代码多、依赖组件广的特点,核心系统中存在非常多的二三方包依赖,在升级之初考虑使用JDK提供的兼容性扫描工具进行分析,但在实操过程中,扫描到的不兼容代码过多,但实际上大部分并没有使用到的情况。如果按照扫描出来的结果推动相关二三方包的适配,整个工程量是完全无法评估的,整个升级节奏也无法把控。为了解决该问题,我们决定整体的升级节奏调整并不追求完全的代码兼容。只对实际影响业务运行的代码做兼容处理。因为整体的升级节奏也就变成了如下几步:
  1. 代码使用JDK8进行编译,但要求代码可以同时在JDK8及JDK11上运行

    1. 上线过程中,先将日常及预发环境的版本切换成JDK11和JDK8同时运行,回归测试过程中同时测试JDK11、JDK8版本下的运行情况

    2. 线上逐步灰度JDK11运行环境的功能,做好监控及线上排查定位。随着几次大促下来后,逐步将所有的容器切换到JDK11,下线JDK8环境的容器。 

    3. 利用运行态的JDK11特性,优化系统性能

    4. 将编译从JDK8切换到JDK11 ,同时充分JDK11本身的新的特性。

if [ -f /home/admin/logs/jdk11.enable ]; then     
    export JAVA_HOME=/opt/taobao/install/ajdk11_11.0.14.13/     
    export JDK11_ENABLE=true     
    echo "enable jdk11 , use new JAVA_HOME : ${JAVA_HOME}" 
fi 
    
if [ "$JDK11_ENABLE" == "true" ]; then     
    CATALINA_OPTS="${CATALINA_OPTS} -Xlog:gc*:${MIDDLEWARE_LOGS}/gc.log:time"     
    CATALINA_OPTS="${CATALINA_OPTS} --add-exports=java.base/jdk.internal.loader=ALL-UNNAMED --add-exports=java.base/jdk.internal.loader=jdk.unsupported --patch-module jdk.unsupported=/home/admin/buy2/bin/java9-migration-helper-0.1.jar"     
    CATALINA_OPTS="${CATALINA_OPTS} -Dio.netty.tryReflectionSetAccessible=true --add-opens=java.base/jdk.internal.misc=ALL-UNNAMED --add-opens=java.base/java.nio=ALL-UNNAMED"     CATALINA_OPTS="${CATALINA_OPTS} --add-exports=java.base/jdk.internal.util.jar=ALL-UNNAMED --add-exports=java.base/jdk.internal.util.jar=jdk.unsupported"     
    CATALINA_OPTS="${CATALINA_OPTS} --add-opens=java.base/com.alibaba.wisp.engine=ALL-UNNAMED"     CATALINA_OPTS="${CATALINA_OPTS} -XX:CompileCommand=stableif,*::*"     test -z "$JPDA_ADDRESS" && export JPDA_ADDRESS=*:8000     #gson兼容     CATALINA_OPTS="${CATALINA_OPTS} -Djava.locale.providers=COMPAT,SPI" else     
    CATALINA_OPTS="${CATALINA_OPTS} -Xloggc:${MIDDLEWARE_LOGS}/gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps"     
    CATALINA_OPTS="${CATALINA_OPTS} -XX:-UncommonNullCast -XX:CompileCommand=stableif,*::*"     test -z "$JPDA_ADDRESS" && export JPDA_ADDRESS=8000 fiexport G1_ENABLE=false if [ -f /home/admin/logs/g1.enable ]; then     export G1_ENABLE=true     echo "enable g1" fi if [ "$G1_ENABLE" == "true" ]; then   
    CATALINA_OPTS="${CATALINA_OPTS} -Xms9500m -Xmx9500m"   CATALINA_OPTS="${CATALINA_OPTS} -XX:+UseG1GC"   
    CATALINA_OPTS="${CATALINA_OPTS} -XX:G1HeapRegionSize=32m"   CATALINA_OPTS="${CATALINA_OPTS} -XX:+G1BarrierSkipDCQ"   
    CATALINA_OPTS="${CATALINA_OPTS} -XX:InitiatingHeapOccupancyPercent=40"   CATALINA_OPTS="${CATALINA_OPTS} -XX:-G1UseAdaptiveIHOP"   
    CATALINA_OPTS="${CATALINA_OPTS} -XX:G1HeapWastePercent=2" else   CATALINA_OPTS="${CATALINA_OPTS} -Xms10g -Xmx10g"   
    CATALINA_OPTS="${CATALINA_OPTS} -Xmn5632m"   CATALINA_OPTS="${CATALINA_OPTS} -XX:+CMSScavengeBeforeRemark"   
    CATALINA_OPTS="${CATALINA_OPTS} -XX:+UseConcMarkSweepGC -XX:CMSMaxAbortablePrecleanTime=5000"   
    CATALINA_OPTS="${CATALINA_OPTS} -XX:+CMSClassUnloadingEnabled -XX:CMSInitiatingOccupancyFraction=80 -XX:+UseCMSInitiatingOccupancyOnly" 
fi
基于上述的升级步骤,对业务代码的升级改造也做了调整,业务在使用过程中,同时安装JDK8和JDK11,业务利用脚本的方式控制线上的JDK11及JDK8的灰度比例。同时经过多轮大促:38大促灰度10台容器、日常灰度100台、618灰度一个单元、99大促灰度所有机房。

五、二三方包适配及改造

JDK11相比JDK8之间的关键的变化
参考:Oracle JDK Migration Guide
  • 版本号格式的变化

  • 本地化的优先级变化

  • module化

  • 废弃代码清理

  • JDK日志架构变化

由于业务代码中旧的代码量较多,为了减少整个升级的复杂度,整体的升级策略,以兼容旧的代码逻辑为主,尽可能的升级到新的版本中(并不是使用jdk11的最佳实践进行直接升级)。
在实际的适配过程中,发现主要的兼容问题集中在版本号格式的变化带来的不兼容。对于这部分代码逻辑主要要求相关业务进行改进即可,对于一些无法找到维护者的二方包来说,直接通过反编译的方式进行整体改造。
二方包
兼容版本
改造原因
citrus-webx-all-in-one
3.2.4.1-jdk11
JDK11版本号格式变化,导致无法启动,由于目前已经无法找到对应维护者,针对这部分二方包,反编译后提交仓库供业务使用。
toolkit-webx-all-in-one
2.0.3-jdk11
toolkit.common.lang
com.alibaba.toolkit:toolkit-common-lang:jar
1.0-jdk11
citrus-webx-all-in-one
3.2.4.1-jdk11
forest-store
8.1.11
使用了非公开的类,在升级到jdk11后,相关的类发生了变化,需要做响应的改造升级
ip-client
5.5.69
sic-client
1.5.33
com.alibaba.ultron:common-sdk-datasource:1.0.0-buy2.0-4
1.0.0-jdk11-beta.2
common-uic-common
3.8.8-jdk11 +
com.cainiao.cdc:cdc-client
com.cainiao.cdc:cdc-common
1.13.25
使用了unsafe的包,导致在升级到JDK11后,相关的类无法使用
gson
2.8.9
由于JDK11的本地化变化,导致gson在解析时间格式上存在不兼容的情况,升级gson版本,同时在启动时添加 -Djava.locale.providers=COMPAT,CLDR 参数。https://github.com/google/gson/pull/1211

 

六、JDK11参数改造

参数
变化原因
-XX:+CMSParallelFullGC
CMS并行gc相关的参数,jdk11版本移植成本较高,在新版本中不做支持
-Xlog:gc*:${MIDDLEWARE_LOGS}/gc.log:time
Xloggc:${MIDDLEWARE_LOGS}/gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps jdk11中调整了统一日志框架支持,需要新的框架支持
-Djava.locale.providers=COMPAT,SPI
在jdk11下增加-Djava.locale.providers=COMPAT,SPI参数,避免因为本地化的问题,带来一些不兼容的情况,目前在业务平台测试下来,主要是gson低版本中会出现无法解析日期格式的情况,为了保证在jdk11下的兼容性,需要将gson升级到2.8.9,同时在jdk11环境下添加-Djava.locale.providers=COMPAT,SPI 启动参数
--add-exports=java.base/jdk.internal.loader=ALL-UNNAMED --add-exports=java.base/jdk.internal.loader=jdk.unsupported --add-exports=java.base/jdk.internal.util.jar=ALL-UNNAMED --add-exports=java.base/jdk.internal.util.jar=jdk.unsupported --add-opens=java.base/com.alibaba.wisp.engine=ALL-UNNAMED --patch-module jdk.unsupported=/home/admin/ump2/bin/java9-migration-helper-0.1.jar
由于module化改造,jdk11下部分package并不会对业务module开放,为了兼容早期的业务代码,需要通过add-exports、add-opens做临时放开,确保在jdk11上能正常运行,同时部分原本jdk内部类的方法签名发生了变化,为了减少迁移的成本,JDK团队提供了migrationjar包,需要通过--patch-module主动引入进来。
JPDA_ADDRESS=*:8000
在jdk11中,JPDA默认只绑定127.0.0.1 IP,在日常及预发需要远程debug的场景下,需要调整启动脚本,使用JPDA_ADDRESS=*:8000, 指定JPDA监听所有的IP
-Dio.netty.tryReflectionSetAccessible=true --add-opens=java.base/jdk.internal.misc=ALL-UNNAMED --add-opens=java.base/java.nio=ALL-UNNAMED 
JDK11升级后,netty的内存回收机制会带来堆外内存增长

 

七、从JDK8升级到JDK11后的性能表现

Buy2性能对比
压测方式:中心固定容器,重启后压测近似时间后,通过tsar统计5分钟的平均数据
机器IP:buy2xxxxx.center.na610
机型:F6X, CPU:8269CY, CPU MHz:3200
性能结论:JDK11版本相比JDK8相同qps (45.56),CPU降低 5% (63.27/60-1); RT降低:10% (232.49/210.03 -1)
JDK版本
压测数据指标
JDK11
cpu util : 60% 
user: 54%
bytin: 6.6M
bytout: 13.3M
qps: 45.56
rt: 210.03
JDK8
cpu util : 63.27% 
user: 56.18%
bytin: 6.7M
bytout: 13.5M
qps: 45.67
rt: 232.49
购物车性能对比
压测方式:选取两台中心的容器,分别部署JDK11和JDK8的环节,比较两者在压测期间的各项指标,整体差异对比
机型:F6X, CPU:8269CY, CPU MHz:3200
性能结论:中心X86机型上,JDK11相比JDK8,相同qps下,CPU降低 5%(65.55/62.31-1); 
指标
JDK8
JDK11
cpu
65.55
62.31
内存占用率
19.77
19.82
GC次数
67
65
GC耗时
1110
710
非堆内存利用率
33.29
39.46
metaspace利用率
55.05
71.64
UMP2性能对比
压测机器型号:6核48G cpu型号:Intel(R) Xeon(R) Platinum 8269CY CPU @ 2.50GHz moc1.5
压测方式:不同环境相同的业务流量,观察系统指标表现
压测结论:相同流量下,JDK8相比JDK11 CPU开销优化并不明显,MS有上涨
 
JDK8
JDK11
CPU
38.5%(user31.5%)
38.3%(user31.3%)
YGC频率
30次/分钟
28次/分钟
线程数
1806(running:771)
1796(running:746)
网络
IN:19 OUT:17
IN:18.7 OUT:17.2
QPS总量
785
790
RT
11
10.4
Metaspace
74.31%
77.32%
整体从性能表现看,部分容器在升级到JDK11后,x86环境下性能有一定的提升,但并不明显,同时由于JDK11本身类结构上的描述信息增加,Codecache分代等特性的增加,会导致应用的Metaspace增加。

八、G1 GC测试验证

 

在大促期间,核心系统依旧使用了Cms GC,但核心应用均保留了几台容器用作G1测试验证,同时为了降低CMS gc的延迟对业务的影响,UMP2的导购分组在大促期间,全部切换到了G1 GC。在双十二期间,四大金刚从G1测试体验下来,GC效率均有一定的提升。期间在使用过程中,并不是直接升级到G1后,就能带来GC及性能上的收益,在压测期间,buy2、carts2均出现升级到G1后,性能出现衰减的情况。
图片
图片
联合JVM排查下来,主要是由于G1的自适应算法不足,导致GC频繁,进而导致了业务的性能衰减。随后调整GC参数后,问题解决。目前Buy2使用的G1的Gc参数如下:
export G1_ENABLE=false
if [ -f /home/admin/logs/g1.enable ]; then
    export G1_ENABLE=true
    echo "enable g1"
fi
if [ "$G1_ENABLE" == "true" ]; then
  CATALINA_OPTS="${CATALINA_OPTS} -Xms9500m -Xmx9500m"
  CATALINA_OPTS="${CATALINA_OPTS} -XX:+UseG1GC"
  CATALINA_OPTS="${CATALINA_OPTS} -XX:G1HeapRegionSize=32m"
  CATALINA_OPTS="${CATALINA_OPTS} -XX:+G1BarrierSkipDCQ"
  CATALINA_OPTS="${CATALINA_OPTS} -XX:InitiatingHeapOccupancyPercent=40"
  CATALINA_OPTS="${CATALINA_OPTS} -XX:-G1UseAdaptiveIHOP"
  CATALINA_OPTS="${CATALINA_OPTS} -XX:G1HeapWastePercent=2"
else
  CATALINA_OPTS="${CATALINA_OPTS} -Xms10g -Xmx10g"
  CATALINA_OPTS="${CATALINA_OPTS} -Xmn5632m"
  CATALINA_OPTS="${CATALINA_OPTS} -XX:+CMSScavengeBeforeRemark"
  CATALINA_OPTS="${CATALINA_OPTS} -XX:+UseConcMarkSweepGC -XX:CMSMaxAbortablePrecleanTime=5000"
  CATALINA_OPTS="${CATALINA_OPTS} -XX:+CMSClassUnloadingEnabled -XX:CMSInitiatingOccupancyFraction=80 -XX:+UseCMSInitiatingOccupancyOnly"
fi
性能数据参考:
图片

展望

 

在交易链路核心系统升级到JDK11后,后续希望能更充分的利用JDK11生态带来的便利性,探索JDK11对启动优化、对开发便捷性、新版Springboot带来的性能提升。
作者|郑波(岱泽)

标签:CATALINA,一文,适配,JDK11,升级,XX,JDK8,OPTS
From: https://www.cnblogs.com/88223100/p/Adaptation-path-of-business-platform-upgrading-JDK11.ht

相关文章

  • 一文吃透 Go 内置 RPC 原理
    hello大家好呀,我是小楼,这是系列文《Go底层原理剖析》的第三篇,依旧分析Http模块。我们今天来看Go内置的RPC。说起RPC大家想到的一般是框架,Go作为编程语言竟然还内置......
  • vue+vant项目中 rem适配配置
    vantrem适配,需要安装两个插件postcss-pxtorem 是一款postcss插件,用于将单位转化为rem  lib-flexible 用于设置rem基准值postcss-pxtorem:npminstallpostc......
  • 不为人知的网络编程(十五):深入操作系统,一文搞懂Socket到底是什么
    1、引言我相信大家刚开始学网络编程中socket的时候,都跟我一样对书上所讲的socket概念云里雾里的、似懂非懂,很是困扰。这篇文章我打算从初学者的角度,用通俗易懂的文字,跟大......
  • ViewPager+Fragment+Adapter+BottomNavigation+BadgeDrawable,适配器和碎片这一块啃的
    前几天用的原生的写法实现了一个基本的导航、基本的流程就是通过线性布局设置图片和文字、最后再加上一个碎片,碎片需要搭配适配器使用今天首先用了BottomNavigation控件,具......
  • (转)一文了解 Go 标准库 strings 常用函数和方法
    原文:https://blog.csdn.net/weixin_44604586/article/details/128104981?spm=1001.2101.3001.6650.2&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EYu......
  • 【转载】一文看懂HTTPS、证书机构(CA)、证书、数字签名、私钥、公钥
    说到https,我们就不得不说tls/ssl,那说到tls/ssl,我们就不得不说证书机构(CA)、证书、数字签名、私钥、公钥、对称加密、非对称加密。这些到底有什么用呢,正所谓存在即合理,这篇......
  • 一文深度解读音视频行业技术发展历程
    从1948年的香农定律,到音视频的今天。IMMENSE、36氪|作者北京时间2月28日凌晨,FIFA年度颁奖典礼在巴黎举行。梅西荣膺年度最佳球员,斯卡洛尼当选年度最佳男足主帅,马丁内斯荣......
  • 一文读懂App热更新技术方案
    早在2017年,AppStore审核团队便针对AppStore中“热更新”的App开发者发送邮件,要求移除所有相关的代码、框架或SDK,并重新提交审核,否则就会在AppStore中下架该软件。由于......
  • 大屏适配方案
    现有大屏实现方案:一、页面元素定位使用rem定义父级大小,之后进行组件的height、margin、padding等多种css属性采用rem作为单位,实现适配二、大屏内容居中展示做法类似于......
  • 一文深度剖析扩散模型究竟学到了什么?
    前言 本文介绍的这篇文章提供了关于扩散模型从其训练数据中复制内容的潜力的重要见解,这项研究可以帮助改进扩散模型的设计和训练,确保它们能够生成独特的原创艺术和图形,非......