Quarkus框架学习的第一部分,请访问:
IDEA中Quarkus框架(3.13版本)开发、调试、部署、打包等
五、docker-compose容器编排
1、创建编排文件
cd quarkus-helloworld
vi docker-compose.yml
docker-compose.yml内容如下:
# yaml 配置实例
version: '3'
services:
quarkus-hello-world:
image: quarkus/code-with-quarkus:latest # docker中已存在的镜像名+版本名
ports:
- "8080:8080" # 主机Port: 容器暴露Port
environment:
TZ: Asia/Shanghai
deploy:
replicas: 2
update_config:
parallelism: 2
delay: 1s
restart_policy:
condition: on-failure
volumes:
logvolume01: {}
2 docker-compose启动(单实例):
cd quarkus-helloworld
docker-compose up -d
如果启动有问题(端口占用等)则可停止(会杀死容器实例)
docker-compose down

3、 docker-compose启动(多实例,弹性伸缩、负载均衡):
docker-compose.yml 文件内容调整为:
# yaml 配置实例
version: '3'
services:
quarkus-hello-world:
image: registry.cn-hangzhou.aliyuncs.com/lizhou828/code-with-quarkus:1.0.0-SNAPSHOT # docker中已存在的镜像名+版本名
ports:
- "8080" # 将容器内部的多个端口映射到宿主机的端口上
restart: always
environment:
TZ: Asia/Shanghai
nginx:
image: nginx:latest
volumes:
- type: bind
source: /run/desktop/mnt/host/d/xxxx/quarkus-helloworld/nginx.conf # /run/desktop/mnt/host/ 前缀来表示 WSL 中访问 Windows 主机文件系统的路径映射 /d/xxxx/quarkus-helloworld/nginx.conf 是windows文件路径在linux系统的表示方式
target: /etc/nginx/nginx.conf # 容器中的路径
depends_on:
- quarkus-hello-world
ports:
- "81:81"
volumes:
logvolume01: {}
新增 nginx.conf文件
cd quarkus-helloworld
vi nginx.conf
nginx.conf 文件内容如下:
asciidoc
user nginx;
events {
worker_connections 1000;
}
http {
server {
listen 81;
location / {
proxy_pass http://quarkus-hello-world:8080;
}
}
}
执行启动命令(2个实例):
docker-compose up -d --scale quarkus-hello-world=2
六、压测与调优
1、启动参数未加 任何 参数,且docker-compose.yaml未限制CPU与内存资源 情况下:
在不停的访问 http://localhost:9000/hello/testData 接口时,内存占用情况从启动时的21M一直升到50M(单实例):

2、启动增加参数 -Xmx32m 参数,且 docker-compose.yaml限制了CPU与内存资源 情况下(两个实例,1个并发,测试接口访问10000次):
使用工具压测 http://localhost:81/hello/testData 接口时,测试访问10000+次,内存占用情况非常平稳:

3、启动增加参数 -Xmx32m 参数,且 docker-compose.yaml限制了CPU与内存资源 情况下(10个实例、1000个并发):
使用工具压测 http://localhost:81/hello/testData 接口时,启动10个实例、1000个并发,内存占用情况也非常平稳(在预热期间,CPU在短时间内有个峰值):
第一轮测试情况:
第二轮测试情况:
七、参考文档
7.1、官方文档
Quarkus实战:专为Kubernetes而优化的Java解决方案.pdf
7.2、开发、部署、调试
IDEA中Quarkus框架(3.13版本)开发、调试、部署、打包等
7.3、Web层
Quarkus的JAX-RS采用Resteasy的实现,可以实现各类Web操作:上传、下载、导入、导出等
Resteasy官方文档
quarkus数据库篇之三:单应用同时操作多个数据库(有链接要改)
7.4、持久层ORM
hibernate-orm-panache 单表操作 的 官方文档
JPA SQL 查询、结果集映射(@NamedNativeQuery、@ColumnResult注解说明)
JPA EntityManager采用原生SQL语句执行后,返回LIst自定义对象的方法
hibernate使用createNativeQuery做原始的字段映射处理
EntityManager使用原生查询createNativeQuery并把值映射到自定义实体
完美的 jpa 多表 原生sql 分页查询
JPA使用原生SQL实现分页查询、排序
7.5、原生应用
Graalvm构建原生镜像时,如何嵌入配置文件(静态文件、反射、动态代理、序列化等)
在云原生镜像中如何自动识别反射、动态代理、序列化等、静态资源、JNI、URL协议等所用到的类和方法的说明
quarkus原生应用,一直刷接口,内存一直在升高?
quarkus原生应用,一直刷接口,内存从21M上升到50M,内存没有回收?
解决方式一:启动命令增加参数: -Xmx64m
解决方式二:
构建原生镜像时,添加参数: -Dquarkus.native.native-image-xmx=64M
7.6、容器化
docker compose 命令自定义参数 docker-compose使用
谁说docker-compose不能水平扩展容器、服务多实例?
Docker-desktop启用k8s挂载hostpath类型的pv时挂载不到windows磁盘的问题
7.7、单元测试
Test a Quarkus application with Junit5, Mockito and H2 Database
7.8、RBAC 基于角色的权限访问控制
7.9、quarkus 微服务( rest-api)
How quarkus/helidon/micronaut support Apache Dubbo Component ?
7.10、quarkus的扩展(二方包、三方包)
quarkus-building-my-first-extension
quarkus官方依赖的源码库 Quarkiverse Hub
7.11、国内博主专栏
CSDN博客专家程序员欣宸的github,这里有六百多篇原创文章的详细分类和汇总,以及对应的源码,内容涉及Java、Docker、Kubernetes、DevOPS等方面
八、编码注意事项
明确调用自己类的实例方法(this),还是调用父类的方法( super)
子类通过super调用了父类的方法, 父类里面的该方法内部不能再有调用链,否则报错
quarkus框架中,尽量避免使用 this 调用其他方法,而是通过 静态方法 或 工具类 来实现逻辑。也尽可能少使用:动态代理、反射等特性
hibernate-orm 执行原生SQL时,映射结果集的类MnItemVo 会被反射调用构造器,如果需要打原生包运行本应用的话,MnItemVo类必须要大打上@RegisterForReflection 注解
hibernate-orm 执行原生SQL时,要映射的结果集的类,每个字段的类型必须准确,否则报错 : argument type mismatch ,或者使用project() 投影 VO类
九 、spring框架的对比
1. 代理类生成机制的不同
Spring 的代理机制
默认使用 JDK 动态代理或 CGLIB:Spring 使用 JDK 动态代理(接口代理)或 CGLIB(基于子类的字节码生成)。
AOP 和代理行为透明:Spring 会尽可能保留类的原始逻辑,super 调用通常不会被代理拦截,因为 Spring 的代理层对父类方法并没有特殊处理。
Quarkus 的代理机制
基于字节码增强:Quarkus 不使用传统的动态代理(如 JDK 或 CGLIB)。取而代之,Quarkus 在构建时通过字节码增强(Bytecode Enhancement)生成代理类。
代理类替换原始类:Quarkus 的代理机制更偏向运行时性能优化,使用的是增强后的类作为代理。增强后的类可能不会直接调用原始父类的逻辑,而是将方法调用委托给特定的拦截器。
导致问题 :
在 Quarkus 中,super 的调用通常会绕过代理类的增强逻辑。如果代理类对父类方法有特殊的增强逻辑(例如注解解析、事务管理等),直接调用 super 会导致增强逻辑无法生效。这与 Spring 的行为不同,因为 Spring 的代理机制通常不会破坏方法调用链。
2. CDI 和 AOP 实现的差异
Spring 的 AOP
Spring 的 AOP 基于 AspectJ 或代理机制,支持拦截方法调用,包括子类方法调用。
如果子类调用父类的 super 方法,Spring 不会主动拦截,因为代理通常只针对接口或类的公开方法。
Quarkus 的 CDI
Quarkus 使用的是 Jakarta CDI(Context and Dependency Injection)的实现,它更加严格地依赖注解元数据和代理类增强。
父类方法在 Quarkus 的代理中可能不会自动包含注解元数据(如 @Path、@Transactional 等)。因此,当子类调用 super 方法时,代理无法处理注解解析,导致 NullPointerException
Quarkus 的 CDI 机制支持懒加载,这意味着某些方法调用可能会被代理类拦截,从而导致方法调用链被破坏。
Quarkus 在构建时会对字节码进行增强,以优化性能和减少内存占用。这种增强可能会影响类的继承结构,导致父类方法调用出现问题。
3. 生命周期和上下文的处理差异
Spring 的 Bean 生命周期
Spring 的 Bean 生命周期在运行时动态管理,Spring 的代理类是在运行时加载和初始化时生成的。
因为代理的目标是增强现有的 Bean,父类方法调用通常不会受到代理逻辑的影响。
Quarkus 的 Bean 生命周期
Quarkus 的 Bean 生命周期通过构建时分析生成字节码进行优化,代理类和增强逻辑在编译时就已经被固定。
如果子类调用父类的 super 方法时,增强逻辑或注解处理需要运行时动态解析,Quarkus 可能无法支持这种行为,因为代理类与父类的注解处理是分离的。
十、常见的第三方依赖的扩展包
已有的扩展:
redis、kafka、mongoDB、ES、microsoft-server-sql、quarkus-scheduler、itext、zookeeper、flyway、hibernate、JDBC
没有的扩展:
nacos、aliyun-sdk-oss、xxl-job