「Quarkus基础系列」探索使用Grafana监控集成Mybatis-plus的Quarkus服务简单记录一下quar
概述
本篇为学习官方文档和运用一些知识点进行集成探索的文档,用于记录学习Agroal数据库连接池与mybatis-plus的整合有关的知识。Agroal 是 Quarkus 默认的数据库连接池,它在性能、资源管理和集成方面具有多个优势,作者能力有限,尽量根据自己的理解和AI辅助记录一下目前Agroal的整体情况。一些特性测试的demo在这里:(feature-agroal分支) MaidSG/quarkus-mybatis: a demo for study and dev quarkus with mybatis (github.com)
Agroal 在Quarkus中的基础配置
属性名 | 取值 | 简介 |
---|---|---|
quarkus.datasource.health.enabled | ture 或者 false | smallrye-health插件,用于帮助 Quarkus 应用提供有关其状态的信息,供外部查看者使用。SmallRye Health 是 MicroProfile Health 规范的一个实现。具体来说,MicroProfile Health 规范定义了一组 API,使开发者可以编写健康检查端点(通常是 HTTP 端点),这些端点返回服务的健康状态(如“UP”或“DOWN”)。健康检查通常包括检查数据库连接、依赖服务的可用性、磁盘空间的使用情况等,以确保服务的所有关键功能都正常运行。 |
quarkus.datasource.reactive.max-size | int | 响应式连接池最大连接数,需要根据应用的并发负载、数据库的处理能力和硬件资源来设置。通常建议值在 20 到 50 之间,根据具体情况调整 |
quarkus.datasource.jdbc.initial-size | int | 连接池初始化大小 |
quarkus.datasource.jdbc.min-size | int | 数据库连接最小连接数,数据库正常情况下会根据这个配置创建连接 |
quarkus.datasource.reactive.idle-timeout | int30S | 这个配置意味着,如果一个连接在连接池中空闲了30秒,且没有被使用,那么它将被关闭。主要适用于高并发的场景,或者在你希望优化资源使用时可以调整这个参数。例如,当应用程序在某些时间段内负载较低时,通过减少空闲连接来释放资源。 |
quarkus.datasource.jdbc.acquisition-timeout | int20S | 这个参数定义了从 JDBC 连接池中获取连接的最大等待时间。如果应用程序在指定的超时时间内无法获取到一个连接,获取连接的操作将会超时,并抛出一个异常。 |
官方文档中提到Quarkus支持用于配置响应式(Reactive)和传统的阻塞式(JDBC)数据库连接,区别是什么?
概念上的区别
Reactive (响应式):
• 采用非阻塞式 I/O 和响应式编程模型,旨在处理高并发、高性能的应用场景。
• 使用 quarkus.datasource.reactive. 开头的配置项。
• JDBC (传统阻塞式):
• 传统的数据库连接方式,使用阻塞式 I/O 操作。适合于大多数应用场景,尤其是那些没有高并发要求的应用。
• 使用 quarkus.datasource.jdbc. 开头的配置项。
JDBC: 配置项通常与连接池的大小和行为有关,例如最大最小连接数、超时设置等;而不同于传统的连接池管理,响应式的数据源会使用异步的方式来管理数据库连接,因此配置项可能会有所不同,主要关注连接的最大并发数量和空闲超时。
编程模型与兼容性
对于高并发、低延迟的场景中表现优异,例如微服务架构、实时数据处理、IoT 等使用,官方更推荐使用Reactive进行配置和管理服务的数据库连接池。 当在 Quarkus 配置文件中同时配置了传统 JDBC 和响应式的数据源配置时,服务与数据库进行交互的方式取决于在代码中使用的具体 API。 当你使用标准的 JDBC API(例如通过 javax.sql.DataSource 或 java.sql.Connection 等)来与数据库进行交互时,Quarkus 会使用传统的 JDBC 数据源。 • 在这种情况下,配置文件中的 quarkus.datasource.jdbc 前缀的配置参数将生效。
如果你在代码中使用的是 Vert.x Reactive API 或 Quarkus 提供的响应式数据源 API(例如通过 io.vertx.sqlclient.SqlClient 或 Mutiny 的 Uni/Multi 接口)进行数据库操作时,Quarkus 将使用响应式的数据源。
• 这时,配置文件中 quarkus.datasource.reactive 前缀的配置参数将生效。
实践与参数收集
下面开始记录整合的过程,集成了一些组件和监控,方便进行。就在使用的时候简单介绍一下并给出相关的配置,对集成感兴趣的读者欢迎私信或在评论区交流。
集成mybatis-plus
注意: 目前由于mybatis-plus中大部分实现依靠反射,使用mybatis-plus后打包原生镜像一定会报错,可以尝试使用mybatis,记得根据社区中的文档使用:quarkiverse/quarkus-mybatis: Quarkus MyBatis Extension (github.com)
添加pom依赖
依赖说明: quarkus-resteasy-jackson : 将 Java 对象自动序列化为 JSON 格式 quarkus-jdbc-mysql: 允许应用程序通过 JDBC 连接到 MySQL 数据库,并执行 SQL 查询、更新、事务处理等操作。 quarkus-mybatis-plus: MyBatis Plus 是 MyBatis 的增强版,提供了 CRUD 操作和其他高级特性。目前(20240903)更新到2.2.4 quarkus-spring-di: 这个依赖用于在 Quarkus 项目中提供 Spring 框架的依赖注入(DI)功能的兼容性支持。主要为了支持mybatis-plus service 层一些方法使用的spring-di的问题 quarkus-arc: 这个依赖是 Quarkus 中的默认依赖注入(DI)框架,基于 CDI(Contexts and Dependency Injection)规范。提供了依赖注入、生命周期管理和上下文管理等功能,是 Quarkus 中处理依赖注入的核心模块。 quarkus-resteasy: 用于处理 RESTful Web 服务;创建 RESTful API,处理 HTTP 请求,并生成 HTTP 响应。
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-jackson</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-mysql</artifactId>
</dependency>
<dependency>
<groupId>io.quarkiverse.mybatis</groupId>
<artifactId>quarkus-mybatis-plus</artifactId>
<version>2.2.4</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-spring-di</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-arc</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy</artifactId>
</dependency>
监控有关依赖 quarkus-smallrye-health: 为应用程序提供健康检查的功能,使得你可以定义和暴露应用程序的健康状态(如数据库连接、服务可用性等)。这些健康检查可以被监控系统(如 Kubernetes)调用,以确定应用程序是否运行正常或需要重启。 quarkus-smallrye-metrics: 应用程序提供度量指标的功能,允许你监控应用程序的性能和行为(如内存使用、请求延迟、吞吐量等)。这些度量指标可以通过 Prometheus 等工具收集,并用于分析和调优应用程序。 quarkus-smallrye-openapi: 这个依赖用于在 Quarkus 项目中集成 SmallRye OpenAPI,它是 MicroProfile OpenAPI 的一个实现。
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-health</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-metrics</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-openapi</artifactId>
</dependency>
补充配置、测试文件
我在实践项目中也尝试根据graalvm的反射配置尝试使用mybatis-plus的情况下构建原生镜像,但是问题很多,没有成功过;希望有成功或者了解的老哥给我的实际项目提成功的PR或者评论区指路,不能使用原生镜像确实挺遗憾的,不过这个是java的历史包袱,确实目前挺难做的。 配置文件 这里的配置文件包含了监控、vertx、数据库连接池的配置,我使用的环境是mcaOS m2 10核 16G的情况下验证。
quarkus.datasource.db-kind=mysql
quarkus.datasource.username=root
quarkus.datasource.password=root
#quarkus.datasource.jdbc.url=jdbc:mysql://host.docker.internal:3306/quarkus-mybatis
quarkus.datasource.jdbc.url=jdbc:mysql://localhost:3306/quarkus-mybatis
quarkus.mybatis.initial-sql=insert.sql
# DataSource config
quarkus.datasource.reactive.max-size=500
quarkus.datasource.reactive.min-size=200
quarkus.datasource.reactive.idle-timeout=30000
# health check
quarkus.datasource.health.enabled=true
# MicroProfile Metrics
quarkus.metrics.enabled=true
quarkus.datasource.metrics.enabled=true
quarkus.smallrye-metrics.export.enabled=true
quarkus.http.access-log.enabled=true
quarkus.datasource.jdbc.initial-size=250
quarkus.datasource.jdbc.max-size=500
quarkus.datasource.jdbc.min-size=210
quarkus.datasource.jdbc.acquisition-timeout=30S
quarkus.datasource.jdbc.leak-detection-interval=45S
quarkus.vertx.event-loops-pool-size=950
quarkus.thread-pool.core-threads=10
quarkus.thread-pool.max-threads=150
quarkus.thread-pool.queue-size=950
quarkus.thread-pool.keep-alive-time=60
quarkus.vertx.worker-pool-size=850
quarkus.thread-pool.shutdown-timeout=50S
quarkus.thread-pool.shutdown-check-interval=2S
初始化sql sql文件放 resoruces 目录中就行了,在配置文件中使用 quarkus.mybatis.initial-sql 进行处理
DROP TABLE IF EXISTS m_user;
CREATE TABLE m_user (
id integer not null primary key,
name varchar(80) not null
);
INSERT INTO m_user (id, name) values(1, 'Test User1');
INSERT INTO m_user (id, name) values(2, 'Test User2');
INSERT INTO m_user (id, name) values(3, 'Test User3');
DROP TABLE IF EXISTS m_todo;
CREATE TABLE `m_todo` (
`id` bigint NOT NULL AUTO_INCREMENT,
`title` varchar(255) NOT NULL DEFAULT '',
`completed` tinyint NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
mybatis Dao层代码:详细文件内容见(feature-agroal分支):[MaidSG/quarkus-mybatis: a demo for study and dev quarkus with mybatis (github.com)](github.com/MaidSG/quar…) entity、mapper、service
package qm.dao.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.activerecord.Model;
/*******************************************************************
* <pre></pre>
* @文件名称: Todo.java
* @包 路 径: qm.dao.entity
* @Copyright:wy (C) 2024 * * @Description: * @Version: V1.0 * @Author: wy * @Date: 2024/8/26 23:19 * @Modify: */@TableName("m_todo")
public class Todo extends Model<User> {
private Long id;
private String title;
private boolean completed;
public Todo() {
}
public Todo(Long id, String title, boolean completed) {
this.id = id;
this.title = title;
this.completed = completed;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public boolean isCompleted() {
return completed;
}
public void setCompleted(boolean completed) {
this.completed = completed;
}
}
package qm.dao.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import qm.dao.entity.Todo;
import qm.dao.entity.User;
/*******************************************************************
* <pre></pre>
* @文件名称: TodoMapper.java * @包 路 径: qm.dao.mapper * @Copyright:wy (C) 2024 * * @Description: * @Version: V1.0 * @Author: wy * @Date: 2024/8/26 23:39 * @Modify: */@Mapper
public interface TodoMapper extends BaseMapper<Todo> {
}
package qm.dao.service;
import com.baomidou.mybatisplus.extension.service.IService;
import qm.dao.entity.Todo;
/*******************************************************************
* <pre></pre>
* @文件名称: MpTodoService.java * @包 路 径: qm.dao.service * @Copyright:wy (C) 2024 * * @Description: * @Version: V1.0 * @Author: wy * @Date: 2024/8/26 23:17 * @Modify: */public interface MpTodoService extends IService<Todo> {
}
package qm.dao.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.enterprise.context.ApplicationScoped;
import qm.dao.entity.Todo;
import qm.dao.mapper.TodoMapper;
import qm.dao.service.MpTodoService;
/*******************************************************************
* <pre></pre>
* @文件名称: MpTodoServiceImpl.java * @包 路 径: qm.dao.service.impl * @Copyright:wy (C) 2024 * * @Description: * @Version: V1.0 * @Author: wy * @Date: 2024/8/26 23:40 * @Modify: */@ApplicationScoped
public class MpTodoServiceImpl extends ServiceImpl<TodoMapper, Todo> implements MpTodoService {
}
controller层(resoeurce资源)
package qm.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.smallrye.common.annotation.RunOnVirtualThread;
import jakarta.inject.Inject;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import org.eclipse.microprofile.metrics.annotation.Counted;
import qm.dao.entity.Todo;
import qm.dao.entity.User;
import qm.dao.mapper.TodoMapper;
import qm.dao.service.MpTodoService;
import qm.service.ITodoService;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/*******************************************************************
* <pre></pre>
* @文件名称: TodoResource.java * @包 路 径: qm.controller * @Copyright:wy (C) 2024 * * @Description: * @Version: V1.0 * @Author: wy * @Date: 2024/8/26 23:13 * @Modify: */@Path("/todos")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class TodoResource {
private static final ExecutorService executor = Executors.newFixedThreadPool(10);
@Inject
TodoMapper todoMapper;
@Inject
MpTodoService mpTodoService;
@Inject
ITodoService iTodoService;
@GET
@Path("/list/{page}/{size}")
public Page<Todo> getTodoPageList(@PathParam("page") Integer page, @PathParam("size") Integer size){
return todoMapper.selectPage(new Page<>(page, size), null);
}
@GET
@Path("/list/m/{page}/{size}")
public Page<Todo> getMTodoPageList(@PathParam("page") Integer page, @PathParam("size") Integer size){
return mpTodoService.page(new Page<>(page, size), null);
}
@GET
@Path("/list/i/{page}/{size}")
public Page<Todo> getITodoPageList(@PathParam("page") Integer page, @PathParam("size") Integer size){
return iTodoService.getTodoList(page, size);
}
@POST
@Path("/add")
@Counted(name = "addTodo", description = "How many times the addTodo method has been invoked")
@RunOnVirtualThread
public Todo add(Todo todo){
// CompletableFuture.runAsync(() -> todoMapper.insert(todo), executor);
todoMapper.insert(todo);
return todo;
}
}
配置好后,启动quarkus 开发模式,调用接口查看情况正常返回分页结果:
集成Prometheus以及Grafana
开发模式启动后,打开quakrus开发ui,查看当前项目使用的依赖,也可以查看依赖注入图
在第二项菜单可以查看项目的配置,可以筛选自己定义的配置,如果调整并保存了未添加的配置,quarkus会将内容放到项目的配置文件中。
调用/q/metrics 可以查看目前项目中的一些参数信息;
集成Prometheus和Grafana进行简单测试
当调试没有问题后,下载Prometheus和Grafana,配置参数
MacOS 官网下载:Download | Prometheus
下载长期支持版本
下载后解压,并在配置文件中配置上quarkus项目的监控参数url,并启动;
访问9090,可以查看Prometheus的面板输入对应的promsql查看参数;
Prometheus没问题后,通过homebrew或者在官网下载Grafana,并运行:
Download Grafana | Grafana Labs
访问默认的localhost:3000 查看Grafana面板,初次登录的密码和账号都是admin;
简单根据quarkus中提高的参数创建一个监控:
个人使用golang写了一个简单的模拟并发请求的客户端,对add方法模拟大约300QPS的并发请求,实际测试下来,完全没有压力。
使用quarkus自带的Dockerfile生成镜像
docker build -f src/main/docker/Dockerfile.jvm -t qm/quarkus-mybatis-jvm .
启动容器:
docker run -i --rm -p 8080:8080 --name quarkus-mybatis qm/quarkus-mybatis-jvm
Agroal有哪些核心特性?
1. 高性能
• 低开销: Agroal 连接池设计为轻量级,具有极低的性能开销,非常适合微服务和云原生应用。 • 快速连接分配: 连接池能够快速地从池中获取连接,减少了数据库连接建立的时间,从而提高了应用程序的响应速度。
2. 动态调整
• 智能连接管理: Agroal 能够根据负载动态调整连接池的大小,确保在高负载下有足够的连接,同时在低负载时释放不必要的连接以节省资源。 • 防止连接泄漏: 连接池会定期检测和回收长时间未使用的连接,避免连接泄漏导致的资源浪费。
3. 集成和配置灵活性
• Quarkus 集成: Agroal 与 Quarkus 紧密集成,提供了开箱即用的配置选项,并且能够利用 Quarkus 的配置机制轻松进行定制。 • 多数据库支持: 它能够同时管理多个数据库连接池,支持复杂的多数据源配置需求。
4. 资源管理
• 内存占用优化: Agroal 在设计时考虑了内存占用问题,通过高效的资源管理机制减少了内存的使用,特别适用于容器化环境。 • 连接池监控: Agroal 提供了丰富的监控和日志功能,帮助开发者实时掌握连接池的状态,便于排查问题和优化性能。
5. 线程安全
• 多线程支持: 连接池设计为线程安全,可以安全地在多线程环境中使用,从而确保并发访问时的稳定性。
6. 容错性和恢复能力
• 自动重试机制: 当数据库连接失败时,Agroal 可以配置为自动重试连接,增加系统的容错性和恢复能力。 • 错误检测和恢复: 它能够自动检测不可用的连接,并在必要时进行重建,从而减少因数据库连接问题导致的停机时间。
7. 支持高级功能
• 事务支持: Agroal 支持事务性操作,确保数据的一致性和完整性。 • 定制化扩展: 允许开发者通过扩展机制自定义连接池行为,满足特殊的业务需求。
转载自:https://juejin.cn/post/7410316451961241611