- 性能测试
- 测试
- 后端
- 开发者工具
【免费下载链接】FrameworkBenchmarks
Source for the TechEmpower Framework Benchmarks project
Voovan 是 TechEmpower Framework Benchmarks(TFB)中 Java 平台分类下的一款 Web 框架测试实现,其相关说明文档位于 frameworks/Java/voovan/README.md。本文以该文档为主线,结合仓库内的基准测试配置、Java 源码、构建脚本与工具集实现,完整讲解 Voovan 在 TFB 中的接入方式:包括/json与/plaintext两个测试接口的 URL 约定、服务端路由的源码实现、运行时 JVM 参数与框架配置文件的调优要点,以及 TFB 工具集如何校验与压测这些接口。读完本文,你将能够理解并复现一个 Java Web 框架接入 TFB 基准测试的完整链路,并掌握 Voovan 服务端在性能测试场景下的关键配置项。
Voovan 在 FrameworkBenchmarks 中的定位
TechEmpower Framework Benchmarks 是一个用于横向比较各类 Web 开发平台性能的开源项目,项目源码位于本仓库根目录。Voovan 以 Java 平台成员的身份参与其中,其测试入口定义在 benchmark_config.json 中:
{ "framework": "voovan", "tests": [{ "default": { "json_url": "/json", "plaintext_url": "/plaintext", "port": 8080, "approach": "Realistic", "classification": "Platform", "database": "None", "framework": "voovan", "language": "Java", "flavor": "None", "orm": "None", "platform": "Voovan", "webserver": "None", "os": "Linux", "database_os": "Linux", "display_name": "voovan-mvc", "notes": "voovan webserver with mvc" } }] }该配置说明了 Voovan 测试的三项关键事实:
- 测试类型:仅参与 JSON 编码与 Plaintext 编码两类无数据库测试,
database与orm均为None; - 网络端口:固定监听
8080端口,这也是整个仓库绝大多数框架测试的默认端口约定; - 定位分类:
approach为Realistic(真实场景)、classification为Platform(平台级)、webserver为None(Voovan 自带 WebServer,不依赖外部服务器),display_name标注为voovan-mvc,即"Voovan WebServer + MVC"的组合。
与此对应,config.toml 以 TOML 形式记录了同一份元信息,供工具集内部解析使用,两处文件的字段一一对应。
版本差异说明
原 README 记录的版本为Java OpenJDK 1.8与Voovan 4.2.x。不过以当前仓库实际内容为准:构建阶段基于maven:3-eclipse-temurin-24-alpine镜像,运行阶段基于amazoncorretto:25镜像,而 pom.xml 中声明的 Voovan 框架版本为4.3.8,maven.compiler.source/target均为17。这意味着该测试实现实际面向的是较新的 JDK 与 Voovan 版本,README 中的版本描述属于历史记录,阅读时应以仓库源码为准。
测试 URL 约定
Voovan 参与的两项测试 URL 定义在 README 中,原文记录如下:
JSON Encoding Test
http://localhost:8080/jsonPlaintext Encoding Test
http://localhost:8080/plaintext这两个 URL 与benchmark_config.json中的json_url、plaintext_url字段严格对应。工具集在验证阶段会以http://{server_host}:{port}{url}的形式拼接请求地址(见 framework_test.py),因此端口 8080 与路由路径必须与配置保持完全一致,否则验证将直接失败。
服务端源码实现解析
Voovan 测试服务的全部业务逻辑集中在单个入口类 VoovanTFB.java 中,主类结构清晰:先构建并调优WebServerConfig,再注册两条路由,最后启动服务。
WebServer 配置调优
TReflect.register(Message.class); WebServerConfig webServerConfig = WebContext.buildWebServerConfig(); webServerConfig.setGzip(false); webServerConfig.setAccessLog(false); webServerConfig.setKeepAliveTimeout(1000); webServerConfig.setHost("0.0.0.0"); webServerConfig.setPort(8080); webServerConfig.setHotSwapInterval(0); webServerConfig.setCache(true); webServerConfig.getModuleConfigs().clear(); webServerConfig.getRouterConfigs().clear(); webServerConfig.setEnablePathVariables(false); webServerConfig.setEnableWebSocket(false); WebServer webServer = WebServer.newInstance(webServerConfig);这些配置项对基准测试性能有直接影响,逐项说明:
setGzip(false):关闭 Gzip 压缩。基准测试响应体极小,压缩反而引入 CPU 开销与响应头变化;setAccessLog(false):关闭访问日志,避免高并发下的磁盘 I/O 与锁竞争;setKeepAliveTimeout(1000):Keep-Alive 超时设为 1000 毫秒,配合 wrk 压测脚本的 keep-alive 连接策略;setHost("0.0.0.0")/setPort(8080):监听所有网卡接口的 8080 端口,保证容器内服务可被外部压测客户端访问;setHotSwapInterval(0):关闭热部署扫描间隔,避免运行期的文件监听开销;setCache(true):开启框架缓存;getModuleConfigs().clear()/getRouterConfigs().clear():清空默认模块与默认路由,从源码结构看,这是为了剔除 Voovan 框架自带的路由分发与模块处理逻辑,让服务仅保留基准测试所需的最小处理链路;setEnablePathVariables(false)/setEnableWebSocket(false):关闭路径变量解析与 WebSocket 支持,进一步削减每请求的处理开销。
WebContext.buildWebServerConfig()会读取 config/framework.properties 作为基础配置,上述代码再在内存中覆盖关键项。
路由注册与响应实现
// 性能测试请求; webServer.get("/plaintext", new HttpRouter() { public void process(HttpRequest req, HttpResponse resp) throws Exception { resp.header().put(HttpStatic.CONTENT_TYPE_STRING, HttpStatic.TEXT_PLAIN_STRING); resp.write(HELLO_WORLD_BYTES); } }); // 性能测试请求 webServer.get("/json", new HttpRouter() { public void process(HttpRequest req, HttpResponse resp) throws Exception { resp.header().put(HttpStatic.CONTENT_TYPE_STRING, HttpStatic.APPLICATION_JSON_STRING); resp.write(TString.toAsciiBytes(JSON.toJSON(new Message(HELLO_WORLD_STR), false, false))); } });Plaintext 接口:设置Content-Type: text/plain响应头后,直接写出预编译的字节数组HELLO_WORLD_BYTES("Hello, World!".getBytes())。将字符串预编码为字节数组、每次请求复用同一份数组,避免重复的编码操作,这是 Web 框架 benchmark 中常见的优化手段。
JSON 接口:设置Content-Type: application/json后,构造一个Message对象并调用 Voovan 的 JSON 工具类序列化:
JSON.toJSON(new Message(HELLO_WORLD_STR), false, false)Message是一个包含message私有字段与getMessage()取值方法的 POJO:
public static class Message { private final String message; public Message(String message) { this.message = message; } public String getMessage() { return message; } public int hashCode(){ return 98821452; } }注意main方法开头调用了TReflect.register(Message.class),从源码结构看,这是向 Voovan 的反射工具注册该类型,使 JSON 序列化能够按反射元数据高效处理该 POJO,避免重复解析类结构。
序列化结果再经TString.toAsciiBytes(...)转为字节数组写出,最终响应体为{"message":"Hello, World!"}形式(JSON.toJSON的后两个布尔参数分别控制是否格式化与是否转义,此处均传false以输出最紧凑的 JSON)。
服务启动
Logger.setEnable(false); webServer.serve();Logger.setEnable(false)关闭框架日志输出(与配置中的setAccessLog(false)互为补充),随后webServer.serve()阻塞启动服务。整个 main 方法即构成一个可直接java -jar运行的独立服务。
框架核心配置文件详解
config/framework.properties 是 Voovan 框架的全局配置,由WebContext.buildWebServerConfig()加载。该文件内每项配置均带有原始注释说明,完整整理如下:
| 配置项 | 当前值 | 框架默认值 | 说明 |
|---|---|---|---|
NoHeapManualRelease | true | true | 为true时框架会立即手动释放无堆内存 |
NoHeapReleaseInterval | 600 | 30 | 无堆对象被回收后,每隔该间隔(秒)释放一次无堆内存 |
ThreadPoolMinSize | 1 | 2 | 每个 CPU 核心对应的线程池最小线程数 |
ThreadPoolMaxSize | 1 | 50 | 每个 CPU 核心对应的线程池最大线程数 |
ThreadPoolStatusInterval | 0 | 1000 | 线程池状态日志输出间隔(毫秒),0表示关闭 |
ByteBufferChannelMaxSize | 20971520 | 1024*1024*2 | ByteBufferChannel 最大容量(字节),当前约为默认值的 10 倍 |
SerializeType | JDK | JDK | 缓存使用的序列化方式,可选JDK或JSON |
JSONConvertEscapeChar | false | true | 为false时 JSON 中的\f \b \r \n \t等转义字符不做 unicode 转换 |
RemoteClassSource | (注释掉) | - | 远程类源码位置,形如127.0.0.1:3001,当前未启用 |
其中两个线程池参数取值为1,表示每个 CPU 核心仅保留 1 个线程。从注释"one cpu core's min/max thread size"可以推断,Voovan 的线程池按 CPU 核心数伸缩;JSONConvertEscapeChar=false则保证 JSON 响应体保持最小的转义处理、输出最紧凑的字节。该文件在运行时通过-cp ./config置于 classpath 下(见下节 Dockerfile 的 CMD),因此修改参数后无需重新编译。
构建与运行:Dockerfile 与 JVM 参数
voovan.dockerfile 采用多阶段构建:
FROM maven:3-eclipse-temurin-24-alpine as maven WORKDIR /voovan COPY pom.xml pom.xml COPY src src COPY config/framework.properties config/framework.properties RUN mvn package -q FROM amazoncorretto:25 WORKDIR /voovan COPY --from=maven /voovan/target/voovan-bench-0.1-jar-with-dependencies.jar app.jar COPY --from=maven /voovan/config/framework.properties config/framework.properties EXPOSE 8080第一阶段在maven:3-eclipse-temurin-24-alpine中执行mvn package -q。这里依赖 pom.xml 中配置的maven-assembly-plugin,其jar-with-dependencies描述符会将 Voovan 框架依赖一并打入voovan-bench-0.1-jar-with-dependencies.jar,产出可直接运行的 fat jar。第二阶段使用amazoncorretto:25作为运行镜像,仅拷贝构建产物与配置文件,并声明EXPOSE 8080。
容器启动命令(CMD)是性能调优的核心:
java -DCheckTimeout=false \ -DThreadBufferPoolSize=1024 \ -DByteBufferSize=4096 \ -DAsyncSend=false \ -DAsyncRecive=false \ -DByteBufferAnalysis=-1 \ -DServer=v \ -server -Xms2g -Xmx2g \ -XX:+DoEscapeAnalysis \ -XX:+AlwaysPreTouch \ -XX:-RestrictContended \ -XX:+UseParallelGC -XX:+UseNUMA \ --add-opens java.base/java.lang=ALL-UNNAMED \ --add-opens java.base/java.util=ALL-UNNAMED \ --add-opens java.base/java.io=ALL-UNNAMED \ --add-opens java.base/java.nio=ALL-UNNAMED \ --add-opens java.base/sun.nio.ch=ALL-UNNAMED \ --add-opens java.base/java.security=ALL-UNNAMED \ --add-opens java.base/java.util.concurrent=ALL-UNNAMED \ --add-opens java.base/java.net=ALL-UNNAMED \ -cp ./config:voovan.jar:app.jar org.voovan.VoovanTFB各参数的作用可从命名与上下文合理推断:
- Voovan 框架系统属性:
-DCheckTimeout=false关闭连接超时检查;-DThreadBufferPoolSize=1024设置线程缓冲池容量;-DByteBufferSize=4096设置 ByteBuffer 初始大小;-DAsyncSend=false与-DAsyncRecive=false关闭异步收发(原样拼写即AsyncRecive);-DByteBufferAnalysis=-1关闭缓冲区分析;-DServer=v指定服务器实现标识; - JVM 堆与内存:
-server -Xms2g -Xmx2g将堆最小/最大值固定为 2G(避免运行期堆扩容抖动);-XX:+AlwaysPreTouch在启动阶段即预提交全部堆内存页; - GC 与并发:
-XX:+UseParallelGC选用吞吐优先的并行回收器;-XX:+UseNUMA启用 NUMA 感知内存分配;-XX:+DoEscapeAnalysis开启逃逸分析(利于栈上分配);-XX:-RestrictContended放宽@Contended注解的默认限制; - 模块开放:一批
--add-opens java.base/...=ALL-UNNAMED用于在较新 JDK 上放开 Voovan 框架反射访问java.lang、java.util、java.nio、sun.nio.ch等内部包所需的权限(新 JDK 强封装限制下常见做法); - 启动入口:
-cp ./config:voovan.jar:app.jar org.voovan.VoovanTFB将配置目录与依赖 jar 加入 classpath,以org.voovan.VoovanTFB作为主类启动。
工具集如何校验这两个接口
TFB 工具集对 Voovan 的验证逻辑由 json.py 与 plaintext.py 两个测试类型驱动,它们从benchmark_config.json中读取json_url与plaintext_url参数(见 abstract_test_type.py)。
JSON 测试的校验规则
json.py的verify方法按顺序执行:
- 以
Accept: application/json,...头发起请求并读取响应; - 通过
basic_body_verification检查响应非空且可被json.loads解析; - 通过
verify_helloworld_object校验响应对象——要求包含message键、值不区分大小写地等于hello, world!,多余键值对与多余空白字节会给出warn(详见 verifications.py); - 通过
verify_headers检查必须存在Server、Date、Content-Type头以及Content-Length或Transfer-Encoding头,且Content-Type须匹配^application/json(; ?charset=(UTF|utf)-8)?$正则;同时会间隔 3 秒请求两次,校验Date头未被缓存(详见 verifications.py)。
这意味着 Voovan 的/json实现必须在Content-Type中使用application/json(Voovan 的HttpStatic.APPLICATION_JSON_STRING恰好满足),且响应体中不得包含多余字段。
Plaintext 测试的校验规则
plaintext.py的verify方法则要求:
- 响应非空;
- 响应体转小写后须包含
hello, world!子串,返回的额外字节数超过 0 会触发warn(提示可能影响性能); Content-Type匹配^text/plain(; ?charset=(UTF|utf)-8)?$;- 状态码为 200,头部要求与 JSON 测试相同。
这正是 Voovan 的/plaintext路由直接写出"Hello, World!"字节数组的原因——响应体被精确控制在最少字节数。
压测脚本
验证通过后,工具集调用 wrk 进行压测:JSON 测试使用 concurrency.sh(固定Accept头、先 primer 后 warmup、再按 concurrency level 阶梯压测),Plaintext 测试使用 pipeline.sh(额外加载pipeline.lua,以 16 级 HTTP 管线深度压测),两者均以Connection: keep-alive请求 Voovan 的 8080 端口。这也解释了源码中setKeepAliveTimeout(1000)与端口 8080 的配置来源。
本地手动验证
不依赖 TFB 工具集时,可以手动复现 Voovan 测试服务的验证过程。构建并启动容器后,分别执行:
# JSON 接口 curl -v http://localhost:8080/json # Plaintext 接口 curl -v http://localhost:8080/plaintextJSON 接口应返回Content-Type: application/json与{"message":"Hello, World!"}响应体;Plaintext 接口应返回Content-Type: text/plain与Hello, World!响应体。之后可用 wrk 发起与工具集一致的压测,例如:
wrk -H "Host: localhost" -H "Accept: text/plain,text/html;q=0.9,*/*;q=0.7" \ -H "Connection: keep-alive" --latency -d 15 -c 512 -t 16 \ http://localhost:8080/plaintext这也是理解 Voovan 在 TFB 中成绩构成的最直接方式——两个接口响应体极小、无数据库参与、无外部 WebServer 层,测得的吞吐与延迟基本反映 Voovan WebServer 与 JVM 本身的数据处理能力。
小结
Voovan 在 FrameworkBenchmarks 中的实现是一个"最小化、可复现"的基准测试范本:README 定义 URL 约定,benchmark_config.json声明测试元数据,VoovanTFB.java用约 80 行代码完成配置调优与路由注册,framework.properties与 Dockerfile 的 JVM 参数共同决定运行时行为,而工具集则以严格的头部/响应体校验保证所有参赛框架在同一规则下被测。理解这条链路,即可举一反三地将任意 Java Web 框架接入该基准测试体系。
- 性能测试
- 测试
- 后端
- 开发者工具
【免费下载链接】FrameworkBenchmarks
Source for the TechEmpower Framework Benchmarks project
相关推荐
Tad.x(tadx)基准测试框架解析:基于 Spring Boot + Vert.x + PostgreSQL 的 Java Web 性能测试实现
Tad.x(tadx)基准测试框架解析:基于 Spring Boot + Vert.x + PostgreSQL 的 Java Web 性能测试实现 导读 本文
性能测试测试后端开发者工具Apache Ignite性能测试指南:基于Yardstick框架的基准测试
Apache Ignite性能测试指南:基于Yardstick框架的基准测试 引言 在分布式系统开发中,性能测试是验证系统能力的关键环节。Apache Igni
数据库分布式数据库后端Kubernetes 调度器性能测试指南:深入 scheduler_perf 基准测试框架
Kubernetes 调度器性能测试指南:深入 scheduler_perf 基准测试框架 导读 test/integration/scheduler_perf
云原生容器编排集群管理微服务
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考