![基于 Docker 搭建开发环境(三):链路追踪](/images/logos/docker-compose.jpg)
基于 Docker 搭建开发环境系列:
基于 Docker 搭建开发环境(一):数据库+监控
基于 Docker 搭建开发环境(二):EFK 日志套件
基于 Docker 搭建开发环境(三):链路追踪
在上一篇文章 基于 Docker 搭建开发环境(一):数据库+监控 和 基于 Docker 搭建开发环境(二):EFK 日志套件 两篇文章中,分别介绍了“数据库+监控”和“EFK 日志套件”。这篇文章给大家分享一下如何在本地搭建起一套简单的分布式链路追踪。
在 AI 的帮助下,如同砍瓜切菜一样,非常迅速地就完成了 基于 Docker 搭建开发环境(二):EFK 日志套件 的搭建。原以为搞这个也会分分钟的问题,结果应用的追踪数据一致无法正常发送到 Jaeger 中,各种改端口号都不行。后来,无意间看了 OpenTelemetry 的配置文档,增加了一个协议配置,全部流程竟然通了,非常神奇!
站在更高的视角去看,链路追踪其实是可观测性的一部分,包括上篇文章的日志,也是可观测性的一部分。日志、追踪、度量,三者是相辅相成的。
图 1. 可观测性 在 OpenTelemetry 出现之前,日志、追踪、度量是分离的,三者各各自为战。而 OpenTelemetry 的出现,则是试图将三者统一。目前 OpenTelemetry 是云原生架构中,最炙手可热的分布式链路追踪解决方案,它提供了一套相关标准,各个厂商可以在这套标准之上进行各种各样的组件开发,大家可以根据自己的需要,选择不同的组件,进行可插拔式的安装。
图 2. OpenTelemetry 的野心 在这篇文章中,链路追踪的解决方案选择的是 OpenTelemetry + OpenTelemetry Collector + Jaeger。
OpenTelemetry OpenTelemetry 并不需要在 Docker 中启动或者配置什么。在目前的架构中,Jaeger 是作为 OpenTelemetry 的一个实现来出现的。 OpenTelemetry 需要做的就是下载一个 Java Agent,执行 docker/config/opentelemetry/download-opentelemetry-agent.sh 脚本即可下载最新版的 Java Agent。在业务应用启动时,增加如下 JVM 参数:
![基于 Docker 搭建开发环境(二):EFK 日志套件](/images/logos/docker-compose.jpg)
基于 Docker 搭建开发环境系列:
基于 Docker 搭建开发环境(一):数据库+监控
基于 Docker 搭建开发环境(二):EFK 日志套件
基于 Docker 搭建开发环境(三):链路追踪
在上一篇文章 基于 Docker 搭建开发环境(一):数据库+监控 中,介绍了一下如何使用 Docker 搭建起 MySQL + NACOS + Prometheus + Grafana 集成数据库、注册中心+配置管理、监控的开发环境。这篇文章来介绍一下如何在原来的基础上接入 Elasticsearch + Fluentd + Kibana 套件,并且将 NACOS 的日志接入到 Elasticsearch 里。
Elasticsearch 由于 Elasticsearch 8+ 的版本修改了安全策略,不允许 Kibana 使用超级管理员 elastic 连接 Elasticsearch,这里选用 7.x 版本做演示。
还有一点需要提醒,在设置 Elasticsearch 的超级管理员 elastic 的账户密码时,如果密码是全部的阿拉伯数字,那么需要用双引号或者单引号括起来。
在测试中,还遇到一个磁盘过载导致的只读问题。解决方式如下:
curl -X GET "localhost:9200/_cat/allocation?v&pretty" 查看磁盘使用情况
解除只读状态
$ curl -X PUT "localhost:9200/test/_settings" -H 'Content-Type: application/json' -d' { "index.blocks.read_only_allow_delete": null } '
![基于 Docker 搭建开发环境(一):数据库+监控](/images/logos/docker-compose.jpg)
基于 Docker 搭建开发环境系列:
基于 Docker 搭建开发环境(一):数据库+监控
基于 Docker 搭建开发环境(二):EFK 日志套件
基于 Docker 搭建开发环境(三):链路追踪
去年,很多同事要换 Mac 本,所以,写了 新 Mac 安装软件脚本,方便大家一键换机。最近想玩一下 Spring Cloud 以及相关周边的部署、监控等开源解决方案。由于组件众多及为了便于迁移和共享,计划基于 Docker 及 Docker Compose 搭建一套相关的开发环境。记录一下,方便有相同需求的朋友借鉴。
最新版的 Docker 在下载镜像时,会先访问一下 Docker 的官方站点。由于国内众所周知的网络情况,访问 Docker 官方站点总失败。所以,即使配置了国内 Docker 镜像站点也会失败。只需要将 Docker 软件回滚到 4.30.0 即可。(Mac 下验证有效,其他操作系统待进一步验证。) MySQL 开发中,最常用的应该就是数据库了。所以,先来搞 MySQL 数据库。
创建如下目录结构,并添加相关相关文件:
$ tree . ├── README.adoc ├── clean.sh ├── data │ └── mysql │ └── .gitkeep ├── docker │ ├── config │ │ └── mysql │ │ └── init.sql │ ├── env │ │ └── mysql.env │ └── images │ └── mysql.dockerfile └── docker-compose.yml
![深入研究 BeanFactoryPostProcessor](/images/logo/spring.svg)
D瓜哥在 Spring 扩展点概览及实践 中概要性地介绍了一下 Spring 的核心扩展点。里面也提到了 BeanFactoryPostProcessor 和 BeanDefinitionRegistryPostProcessor,但仅仅提了一句,没有深入研究。在 Spring 扩展点实践:整合 MyBATIS 中,由于 MapperScannerConfigurer 实现了 BeanDefinitionRegistryPostProcessor 接口,也只是简单介绍了一些作用,又一次没有深入研究。
最近,在开发一个插件时,遇到了一个问题:利用 BeanFactoryPostProcessor 对一些特定 BeanDefinition 设置属性,但生成的 Bean 却没有相关的属性值。由此,对 BeanFactoryPostProcessor 做了一些研究。记录一下,以备不时之需。
Spring 启动流程简介 在 Spring 启动流程概述 中,D瓜哥对 Spring 的启动流程做了比较详细的介绍。同时画了一张启动流程图,如下:
图 1. AbstractApplicationContext.refresh — 重塑容器 从该图中可以明显看到,如果需要对 Spring 的 BeanDefinition 做些修改,那么,就需要通过实现 BeanFactoryPostProcessor 接口,来对 Spring 做些扩展。坦白讲,为了上述流程图只展示了一个非常概要性的流程。如果深入一下 invokeBeanFactoryPostProcessors 方法的细节,会发现这又是一番天地。
BeanFactoryPostProcessor 调用详解 D瓜哥把 invokeBeanFactoryPostProcessors 方法的流程图也画了出来,细节如下:
图 2. BeanDefinitionRegistryPostProcessor & BeanFactoryPostProcessor 调用过程 从这张流程图上可以看出 BeanFactoryPostProcessor 的调用过程,比在 Spring 启动流程概述 中介绍的要复杂很多:
首先,执行 BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry 方法,顺序如下:
关于 BeanDefinitionRegistryPostProcessor 的处理流程,D瓜哥在 Spring 扩展点概览及实践:BeanDefinitionRegistryPostProcessor 中有更详细的描述,不了解的朋友请参考那篇文章的介绍。
![生产环境中 Java 21 启动参数](/images/java/jvm.jpg)
在 OpenJDK 21 升级指南 中,给大家分享了一下升级到 OpenJDK 21 中遇到的一些问题。文末留了一个小问题:生产环境的 Java 21 启动参数怎么配置?这篇文章将给出 D瓜哥的答案。
先说明一下生产环境的机器配置:4C8G,四个内核,8G 内存。
启动参数 鉴于 JVM GC 性能测试(二):递增流量 和 JVM GC 性能测试(三):真实流量 中,G1 GC 的惊艳表现,这里分别提供 Gen ZGC 和 G1 GC 两个配置。
两个配置差距级小,为了方便复制粘贴,还是分两个来展示。 Gen ZGC 配置 追求极致低延迟,就上 GenZGC,它通过牺牲大约 10% 的吞吐量,换来无与伦比的低延时。
注意:使用时,请修改日志目录! ## 变量配置 ####################################################################### # java -XshowSettings:all --展示所有配置项(测试发现也不全) -Dfile.encoding=UTF-8 # https://zhuanlan.zhihu.com/p/455313866 # https://zhuanlan.zhihu.com/p/455746995 # https://blog.csdn.net/u014149685/article/details/83002405 # 随机数来源 -Djava.security.egd=file:/dev/./urandom -Djava.security=file:/dev/./urandom # https://docs.oracle.com/javase/8/docs/api/java/net/doc-files/net-properties.html # https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/net/doc-files/net-properties.html # DNS 过期时间 -Dnetworkaddress.cache.ttl=10 # -Dsun.net.client.defaultConnectTimeout=60000 -Dsun.net.client.defaultReadTimeout=60000 #-Dsun.net.inetaddr.ttl=300 # https://mdnice.com/writing/47e729bbf8e44431a396a481ed173dae -Djava.awt.headless=true # https://blog.csdn.net/maverick0/article/details/8282472 -Djmagick.systemclassloader=no # From Cassandra # On Java >= 9 Netty requires the io.netty.tryReflectionSetAccessible system property # to be set to true to enable creation of direct buffers using Unsafe. Without it, # this falls back to ByteBuffer.allocateDirect which has inferior performance and # risks exceeding MaxDirectMemory # https://blog.csdn.net/jdcdev_/article/details/132843927 -Dio.netty.tryReflectionSetAccessible=true # 内部中间件 # 注意:一些中间件会内嵌 Netty,这里建议同步修改其相关参数配置。 -Dump.profiler.shade.io.netty.tryReflectionSetAccessible=true -Dtitan.profiler.shade.io.netty.tryReflectionSetAccessible=true # Revert changes in defaults introduced in https://netty.io/news/2022/03/10/4-1-75-Final.html -Dio.netty.allocator.useCacheForAllThreads=true -Dio.netty.allocator.maxOrder=11 # 内部中间件 # 理由上面已讲 -Dump.profiler.shade.io.netty.allocator.useCacheForAllThreads=true -Dump.profiler.shade.io.netty.allocator.maxOrder=11 # Byte Buddy 支持21 -Dnet.bytebuddy.experimental=true -Dpfinder.shade.net.bytebuddy.experimental=true ## 参数配置 ##################################################################### # https://jacoline.dev/inspect -- JVM 参数诊断 # https://chriswhocodes.com/corretto_jdk21_options.html # https://docs.oracle.com/en/java/javase/21/docs/specs/man/java.html # https://blog.csdn.net/wxb880114/article/details/119888587 # https://www.cnblogs.com/three-fighter/p/14644152.html #- https://www.skjava.com/article/2134434173 # 解锁诊断参数 -XX:+UnlockDiagnosticVMOptions # 解锁试验参数 -XX:+UnlockExperimentalVMOptions # 启用 ZGC -XX:+UseZGC # 启用分代ZGC -XX:+ZGenerational # https://tech.meituan.com/2020/08/06/new-zgc-practice-in-meituan.html # 加快 GC 的时间和能力 -XX:ZAllocationSpikeTolerance=5 -XX:ConcGCThreads=2 -XX:ParallelGCThreads=4 # G1 GC #-XX:+UseG1GC #-XX:MaxGCPauseMillis=50 # 初始堆大小,等价于 -XX:InitialHeapSize -Xms4608m # 弱最大堆,尽量保持,但是可以突破 #-XX:SoftMaxHeapSize=3g # 最大堆大小,等价于 -XX:MaxHeapSize -Xmx4608m # 归还未使用的内存 #-XX:+ZUncommit # 设置每个线程的堆栈大小,等价于 -XX:ThreadStackSize=512k -Xss512k # https://cloud.tencent.com/developer/article/1408384 # 本地内存大小 -XX:MaxDirectMemorySize=512m # https://cloud.tencent.com/developer/article/2277327 # https://cloud.tencent.com/developer/article/2277328 # https://cloud.tencent.com/developer/article/2277329 # 元空间 # 设置为 256m 时,发生过一次频繁 GC 导致应用无法相应的问题 -XX:MetaspaceSize=512m # 最大元空间 -XX:MaxMetaspaceSize=512m # https://cloud.tencent.com/developer/article/1408773 # https://blog.csdn.net/lidf1992/article/details/75050219 # 编译代码缓存空间 -XX:ReservedCodeCacheSize=256m # https://cloud.tencent.com/developer/article/1408827 # https://malloc.se/blog/zgc-jdk15 # https://tinyzzh.github.io/java/jvm/2022/04/24/JVM_CompressedOops.html # https://www.cnblogs.com/star95/p/17512212.html -- 由于从 JDK15 开始, # -XX:+UseCompressedClassPointers 与 -XX:-UseCompressedOops 之间的强 # 关联被打破,文章里关于上述这种搭配是不正确的。 TODO 可以从新测试验证一线。 # TODO 如果开启 -XX:+UseCompressedClassPointers,不确定 32M 是否够用? # https://www.zhihu.com/question/268392125 -XX:+UseCompressedClassPointers -XX:CompressedClassSpaceSize=48M # 关闭热度衰减 -XX:-UseCounterDecay # 内存占座 -XX:+AlwaysPreTouch # 禁止代码中显示调用GC -XX:+DisableExplicitGC # 关闭安全点间隔 -XX:GuaranteedSafepointInterval=0 # 避免循环无法进入安全点的问题 -XX:+UseCountedLoopSafepoints # https://blog.csdn.net/m0_46596655/article/details/123606813 -XX:LoopStripMiningIter=1000 # 打印命令行参数 -XX:+PrintCommandLineFlags # 显式地并发处理 GC 调用 -XX:+ExplicitGCInvokesConcurrent # https://panlw.github.io/15320998566522.html -XX:AutoBoxCacheMax=20000 # https://blog.csdn.net/zshake/article/details/88796414 # 省略异常栈信息从而快速抛出 -XX:-OmitStackTraceInFastThrow # https://www.jianshu.com/p/c9259953ca38 # 致命错误日志文件 -XX:ErrorFile=/path/to/log/jvm/hs_err_%p.log # https://blog.csdn.net/lusa1314/article/details/84134458 # https://juejin.cn/post/7127557371932442632 # 当JVM发生OOM时,自动生成DUMP文件。 -XX:+HeapDumpOnOutOfMemoryError # 设置上述DUMP文件路径 -XX:HeapDumpPath=/path/to/log/jvm/ # https://juejin.cn/post/6959405798556434440 # 设置 JFR 相关参数 # TODO 感觉这里不全乎,似乎需要 -XX:+FlightRecorder 来启用 # TODO 似乎可以设置文件,例如: -XX:StartFlightRecording=duration=200s,filename=flight.jfr # 不确定文件名是否可以这样配置,测试一下_%p-%t # Amazon Corretto JDK OK;Eclipse Temurin 不识别,并且监控报错 #-XX:StartFlightRecording=delay=5s,disk=true,dumponexit=true,duration=24h,maxage=5d,maxsize=2g,filename=/path/to/log/jvm/jfr_%p-%t.jfr.log #-XX:FlightRecorderOptions=maxchunksize=128m #-XX:StringDeduplicationAgeThreshold=threshold? TODO 测试之后才可以定 # https://zhuanlan.zhihu.com/p/111886882 # https://github.com/apache/cassandra/tree/trunk/conf # https://github.com/elastic/elasticsearch/blob/main/distribution/src/config/jvm.options # java -Xlog:help # 日志配置 -Xlog:gc*=debug,stringdedup*=debug,heap*=trace,age*=debug,promotion*=trace,jit*=info,safepoint*=debug:file=/path/to/log/jvm/gc_%p-%t.log:time,pid,tid,level,tags:filecount=10,filesize=500M # 分开设置可用,使用分开的配置 #-Xlog:gc*=debug,stringdedup*=debug,heap*=trace,age*=debug,promotion*=trace:file=/path/to/log/jvm/gc-%t.log:utctime,level,tags:filecount=10,filesize=200M #-Xlog:jit*=info:file=/path/to/log/jvm/jit_compile-%t.log:utctime,level,tags:filecount=10,filesize=50M #-Xlog:safepoint*=debug:file=/path/to/log/jvm/safepoint-%t.log:utctime,level,tags:filecount=10,filesize=50M # https://stackoverflow.com/a/44059335 # https://openjdk.org/jeps/261 # https://www.diguage.com/post/upgrade-to-openjdk21/ -- 内有详细介绍 # 开启模块权限:下面是D瓜哥需要的模块,请根据自己实际需求来调整。 --add-exports java.base/sun.security.action=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.math=ALL-UNNAMED --add-opens java.base/java.net=ALL-UNNAMED --add-opens java.base/java.security=ALL-UNNAMED --add-opens java.base/java.time=ALL-UNNAMED --add-opens java.base/java.util.concurrent.locks=ALL-UNNAMED --add-opens java.base/java.util.concurrent=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/jdk.internal.loader=ALL-UNNAMED # Netty 内部需要 https://stackoverflow.com/a/57892679 # https://github.com/netty/netty/issues/7769 # https://blog.csdn.net/thewindkee/article/details/123618476 --add-opens java.base/jdk.internal.misc=ALL-UNNAMED --add-opens java.base/sun.net.util=ALL-UNNAMED # 设置 -Dio.netty.tryReflectionSetAccessible=true 后,不设置该值也会报错 --add-opens java.base/java.nio=ALL-UNNAMED --add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/sun.reflect.generics.reflectiveObjects=ALL-UNNAMED --add-opens java.base/sun.util.calendar=ALL-UNNAMED --add-opens java.management/com.sun.jmx.mbeanserver=ALL-UNNAMED --add-opens java.management/java.lang.management=ALL-UNNAMED --add-opens java.management/sun.management=ALL-UNNAMED --add-opens jdk.management/com.sun.management.internal=ALL-UNNAMED
![使用 OpenRewrite 优化代码](/images/logos/openrewrite.png)
在 OpenJDK 21 升级指南 中提到, OpenRewrite 可以帮忙解决一些升级 OpenJDK 中发现的问题。随着不断的探索,D瓜哥发现,OpenRewrite 的功能远远不止这些。下面就挑选一些重要的功能来给大家做一些讲解。
为了方便查看改动点,建议将代码交给版本管理工具,比如 Git,来管理。 快速入门 OpenRewrite 是一套对源码做重构的大型生态系统,可以帮助开发人员减少技术债。所以,它提供了一套的相关工具。对于大多数开发人员来说,最方便的也许就是基于 Maven 插件的相关工具。这里以对 Java 的 import 语句排序来为示例展示一下 OpenRewrite 的使用方法。
在项目的 pom.xml 中增加如下配置:
<!-- @author: D瓜哥 · https://www.diguage.com --> <plugin> <groupId>org.openrewrite.maven</groupId> <artifactId>rewrite-maven-plugin</artifactId> <version>5.30.0</version> <configuration> <activeRecipes> <!-- import 排序 --> <!-- https://docs.openrewrite.org/recipes/java/orderimports --> <recipe>org.openrewrite.java.OrderImports</recipe> </activeRecipes> </configuration> </plugin> 然后执行如下命令:
mvn rewrite:run 执行会输出一大堆东西,这里就不再展示,执行完成后,使用 Git 查看一下改动点。如下图:
图 1. 使用 OpenRewrite 排序 import 的改动点 将这些修改点提交,就完成了一次优化, OpenRewrite 的基本使用,你学废了吗?
这里再多说一句: 由于 OpenRewrite 精巧的设计,可以通过使用不同的处方,进行各种各样的优化。所以,最重要的一点就是了解 OpenRewrite 各种不同的处方及使用办法。下面就介绍一下常用的处方及使用办法。
常用处方 升级到 Java 21 在 OpenJDK 21 升级指南 中提到,可以使用“科技与狠活”来解决很多升级中遇到的问题。这里就来实操一把。
![OpenJDK 21 升级指南](/images/java/java-21.jpeg)
OpenJDK 21 已经发布半年有余,在这个版本中, Generational ZGC 也一起发布了。在 ZGC | What’s new in JDK 16 中, Per Lidén 宣称,将 ZGC 的最大停顿时间从 10ms 降低到了 1ms。再加上 JVM GC 性能测试(二):递增流量 和 JVM GC 性能测试(三):真实流量 文中,GenZGC 的惊艳表现,这些种种先进技术,着实充满诱惑,忍不住想吃口螃蟹 🦀。这篇文章,D瓜哥就来分享一下,自己在升级 OpenJDK 21 中的一些经验。
本文仅介绍升级 OpenJDK 的相关内容,ZGC 原理等会专门撰文介绍。 升级依赖 依赖升级不是 KPI,也不涉及需求交付。所以,大多数项目的依赖自从项目创建后,就很少升级。如果想比较顺利地将项目升级到 OpenJDK 21,那么,先将项目所用依赖做一个整体升级是一个事半功倍的操作。可以直接使用 Maven 命令来检查依赖可以升级的情况:
mvn versions:display-dependency-updates 执行该命令后,会有如下类似输出:
# 检查依赖升级情况 $ mvn versions:display-dependency-updates # 此处省略一万个字 # @author: D瓜哥 · https://www.diguage.com [INFO] org.springframework:spring-aop ......... 5.3.33 -> 6.1.6 [INFO] org.springframework:spring-aspects ..... 5.3.33 -> 6.1.6 [INFO] org.springframework:spring-beans ....... 5.3.33 -> 6.1.6 [INFO] org.springframework:spring-context ..... 5.3.33 -> 6.1.6 [INFO] org.springframework:spring-core ........ 5.3.33 -> 6.1.6 [INFO] org.springframework:spring-jdbc ........ 5.3.33 -> 6.1.6 [INFO] org.springframework:spring-web ......... 5.3.33 -> 6.1.6 [INFO] org.mybatis:mybatis-2-spring ............ 1.1.0 -> 1.2.0 [INFO] org.mybatis:mybatis-spring .............. 2.1.1 -> 2.1.2 [INFO] org.junit.jupiter:junit-jupiter ........ 5.9.3 -> 5.10.2 [INFO] org.junit.jupiter:junit-jupiter-api .... 5.9.3 -> 5.10.2
![JVM GC 性能测试(三):真实流量](/images/java/jvm.jpg)
JVM GC 性能测试系列:
JVM GC 性能对比方法
JVM GC 性能测试(一):相同流量
JVM GC 性能测试(二):递增流量
JVM GC 性能测试(三):真实流量
书接上文,在 JVM GC 性能测试(二):递增流量 的最后,D瓜哥提到了一个问题,对于在 JVM GC 性能测试(一):相同流量 和 JVM GC 性能测试(二):递增流量 中存在的巨大 QPS 差异疑惑不解。所以,D瓜哥决定将测试机器接入到线上环境,在真实访问中,观察各个 GC 的表现。
一言以蔽之 J21-Gen-ZGC 和 J21-G1 无论在稳定性,吞吐量以及响应时效性上都非常优秀。
再极端峰值情况,J21-G1 是更好的选择,更加稳定,不容易出凸点。
日常使用,J21-Gen-ZGC 响应性更好,接口耗时更低。
鉴于 OpenJDK 21 G1 GC 一如既往的惊艳表现,D瓜哥准备整理一下 G1 GC 的主要优化,敬请关注: Java G1 垃圾收集器主要优化。
1. 服务调用监控数据 监控服务调用的相关数据,这是对于用户来说,感知最强烈的相关数据,也是直接关系到服务质量的数据。
1.1. 服务调用次数 从调用次数上来看,五个分组没有大的变化,可以说根本没有达到系统的极限峰值。当然,这才是正常现象,如果日常运行都爆峰值,那说明系统早该扩容了。
图 1. 服务调用次数(秒级) 图 2. 服务调用次数(分钟级) 1.2. 服务调用耗时 整体上讲,J21-Gen-ZGC 的耗时更短,从数据上来看,TP999 能比 J21-G1 的少 10~20ms;TP99 更加夸张,J21-Gen-ZGC 的耗时只有 J21-G1 的一半。
J21-Gen-ZGC 和 J21-G1 还是一如既往的稳。
这次测试中,J17-ZGC 也很稳,有些出乎意料。但是,结合下面 JVM CPU 使用率 和 系统 CPU 使用率 来看,J17-ZGC 和 J21-ZGC 的 CPU 使用率早早就达到 90%+,再结合上面两个测试,从稳定性来看,J17-ZGC 和 J21-ZGC 只能被排除掉。
![JVM GC 性能测试(二):递增流量](/images/java/jvm.jpg)
JVM GC 性能测试系列:
JVM GC 性能对比方法
JVM GC 性能测试(一):相同流量
JVM GC 性能测试(二):递增流量
JVM GC 性能测试(三):真实流量
在上一篇文章 JVM GC 性能测试(一):相同流量 中,D瓜哥使用一个总量请求对所有分组的所有机器进行性能测试。但是,经过测试发现了一个问题,同时产生了另外一个问题,有两个问题没有得到很好的解答:
由于服务响应时长直接关系到服务调用次数,当某一台机器出现问题时,整体调用次数就会急剧下降,调用次数加不上去。一个机器出问题,所有机器的访问量就上不去了。这是测试中发现的一个问题。当然,这属于测试工具的问题,别不是 GC 的问题。但是,也影响到我们的压测,也需要解决。
上次测试,这是针对某一个指定服务调用量进行性能测试,那么,无法确定每个 GC 能支撑的极限调用峰值。另外,在极限峰值和超极限峰值的情况下,各个 GC 的表现如何?这个也有待验证。
针对上述两个问题,设计了本次测试。测试方法如下:
各个分组使用一套相同的流量策略:
各个分组几乎同时开始执行测试任务;
调用量从低到高,以此同时使用相关的调用量进行测试;
除最开始预热阶段的调用量外,后续每个调用量都持续进行十分钟的测试。
针对每个 GC 分组单独设定一套调用发量程序,这个保证各个 GC 分组直接不相互影响。
最后,再分析调用量相同时段的各个 GC 表现,就可以看到各个 GC 的极限峰值。
为了保留更多细节,本文所有截图都是在 34 吋带鱼屏下,使用全屏模式展示并截图的。如果看不清楚,可以右击在新页面打开图片来查看。 具体流量及时间段:
750, 23:14:30 ~ 23:19:30
800, 23:19:30 ~ 23:29:30
850, 23:29:30 ~ 23:39:30
900, 23:39:30 ~ 23:49:30
950, 23:49:30 ~ 23:59:30
1000,23:59:30 ~ 00:09:30
1050,00:09:30 ~ 00:19:30
1100,00:19:30 ~ 00:29:30
![JVM GC 性能测试(一):相同流量](/images/java/jvm.jpg)
JVM GC 性能测试系列:
JVM GC 性能对比方法
JVM GC 性能测试(一):相同流量
JVM GC 性能测试(二):递增流量
JVM GC 性能测试(三):真实流量
在上一篇文章 JVM GC 性能对比方法 介绍了性能对比的方法,这篇文章就根据该方法对上述提到的5种 JVM GC 进行性能测试。
在正式测试之前,D瓜哥进行了多次小流量试探性测试,来探索一个合适的量。找到一个比较平稳的量后,乘以机器数量,获得一个每秒总计请求量,最后使用该总量数据去做压测。
根据多次测试的数据来看,最后选择的是每台每秒 500 QPS,5 个分组,每个分组 5 台机器,所以,每秒的请求总量是: 500 * 5 * 5 = 12500 QPS;每个分组每分钟的总量是:500 * 5 * 60 = 150000 QPS。使用每台机器以此使用 100 QPS,200 QPS,300 QPS,400 QPS 各运行一分钟来对系统进行预热。最后以每台每秒 500 QPS 的访问量来对测试机器进行持续十分钟的性能测试,最后分析这十分钟的相关数据。
一言以蔽之 服务稳定性:J21-Gen-ZGC、J21-G1、J8-G1 稳定性最好;J17-ZGC 有轻微波动;J21-ZGC 有剧烈波动;
服务耗时 TP999:J21-Gen-ZGC < J17-ZGC < J21-G1 < J8-G1 < J21-ZGC;
CPU 消耗:J21-G1 < J8-G1 < J17-ZGC < J21-Gen-ZGC < J21-ZGC;