《Quarkus实战》总结
目录
- 一、优势
- 二、搭建脚手架
- 三、Rest
- 1)启用跨源资源共享
- 2)拦截HTTP请求
- 3)使用SSL进行安全连接
- 四、配置
- 1)以程序化的方式访问配置属性
- 2)在外部覆盖配置值
- 3)修改日志配置
- 五、编程模型
- 1)校验输入值和输出值
- 2)全局异常处理
- 3)创建自定义校验
- 4)以程序化的方式校验对象
- 5)依赖注入
- 6)创建工厂类
- 7)如何在创建或销毁对象前后执行一些逻辑
- 8)如何在应用程序启动或关闭后执行一些逻辑?
- 9)如何用名字限定一个注解?
- 10)如何使用注解来限定和配置依赖?
- 11)创建拦截器
- 12)测试端口
- 13)单元测试
- 14)使用Mockito创建mock对象
- 15)元注解
- 16)在测试代码前后执行代码
- 六、打包Quarkus应用程序
- 七、持久化
- 1)配置数据源
- 2)配置多数据源
- 3)事务定义
- 4)设置事务的上下文
- 5)用Panache持久化数据
- 6)更多Panache方法
- 7)用Panache连接mongo
- 八、容错
- 1)自动重试
- 2)超时
- 3)过载保护
- 4)断路器
- 5)禁用容错
- 九、可观察性
- 1)健康检查
- 2)自定义健康检查
- 3)暴露服务指标
- 4)创建指标
- 5)分布式链路追踪
- 十、认证和授权
- 1)使用Elytron Security JDBC配置进行认证授权
- 十一、使用Spring API开发Quarkus
- 1)Spring依赖注入
- 2)Spring Web
- 十二、Quarkus附加功能
一、优势
- 占用内容小,启动速度快,只要几分之一秒,可以在k8s部署尽可能多的实例,快速运行
- 开发热部署,边改代码直接生效
二、搭建脚手架
命令方式创建
mvn io.quarkus:quarkus-maven-plugin:1.4.1.Final:create \
-DprojectGroupId=org.acme \
-DprojectArtifactId=getting-started\
-DclassName="org.acme.quickstart.GreetingResource" \
-Dpath="/hello"
目录结构
开发模式实时重载
mvn compile quarkus:dev
三、Rest
// 获取请求的Uri信息
@Context UriInfo uriInfo,
@QueryParam("order") String order,
@HeaderParam("token") String token
// 获取表单参数
@FormParam
// 矩阵参数
@MatrixParam
@CookieParam
1)启用跨源资源共享
使用quarkus.http.cors配置属性来启用跨源资源共享(CORS)。
quarkus.http.cors=true
quarkus.http.cors.origins=http://example.com
quarkus.http.cors.methods=GET,PUT,POST,DELETE
quarkus.http.cors.headers=accept,authorization,content-type,x-requested-with
2)拦截HTTP请求
使用io.quarkus.vertx.http.runtime.filters.Filters,继承ContainerResponseFilter
@Provider
public class HeaderAdditionContainerResponseFilter implements ContainerResponseFilter {
@Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
responseContext.setEntity("haha-A");
}
}
3)使用SSL进行安全连接
quarkus.http.ssl-port=8443
quarkus.http.ssl.certificate.key-store-file=keystore.jks
quarkus.http.ssl.certificate.key-store-file-type=jks
quarkus.http.ssl.certificate.key-store-password=changeit
四、配置
在application.properties
文件中定义
@ConfigProperty(name = "quarkus.mailer.from")
String from;
1)以程序化的方式访问配置属性
注入org.eclipse.microprofile.config.Config
来程序化地获取属性值
2)在外部覆盖配置值
Quarkus允许你通过将配置设置为系统属性(-Dproperty.name=value)或环境变量(export PROPERTY_NAME=value)来覆盖任何配置属性。系统属性比环境变 量有更高的优先级。
举例:
3)修改日志配置
只需修改quarkus.log.level
设置
quarkus.log.level=DEBUG
限制类的日志级别
五、编程模型
1)校验输入值和输出值
使用Bean Validation规范来为模型添加校验。
mvn quarkus:add-extension -Dextensions="quarkus-hibernate-validator"
2)全局异常处理
继承javax.ws.rs.ext.ExceptionMapper
import javax.json.Json;
import javax.json.JsonArray;
import javax.json.JsonArrayBuilder;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
@Provider
public class BeanValidationExceptionMapper implements ExceptionMapper<ConstraintViolationException> {
@Override
public Response toResponse(ConstraintViolationException exception) {
return Response.status(Response.Status.BAD_REQUEST)
.entity(createErrorMessage(exception))
.type(MediaType.APPLICATION_JSON)
.build();
}
private JsonArray createErrorMessage(ConstraintViolationException exc) {
JsonArrayBuilder errors = Json.createArrayBuilder();
for (ConstraintViolation<?> violation : exc.getConstraintViolations()) {
errors.add(
Json.createObjectBuilder()
.add("path", violation.getPropertyPath().toString())
.add("message", violation.getMessage())
);
}
return errors.build();
}
}
3)创建自定义校验
实现javax.validation.ConstraintValidator接口和注解
最后使用方式
4)以程序化的方式校验对象
使用Bean Validation javax.validation.Validator
类
5)依赖注入
使用@Inject
6)创建工厂类
使用javax.enterise.inject.Produces
允许创建任何类型的对象,等同于Spring中@Bean
使用@io.quarkus.arc.DefaultBean
表示默认对象,等同Spring中@Default
7)如何在创建或销毁对象前后执行一些逻辑
使用@javax.annotation.PostConstruct和 @javax.annotation.PreDestroy注解进行对象生命周期管理。对于 PostConstruct来说,使用这些注解的方法将在对象创建之后被调用;对 于PreDestroy来说,使用这些注解的方法在对象被销毁之前被调用:
比较类似Spring
8)如何在应用程序启动或关闭后执行一些逻辑?
io.quarkus.runtime.StartupEvent和io.quarkus.runtime.ShutdownEvent事件:在应用程序启动时,Quarkus会产生StartupEvent事件;而在关闭时,会产生ShutdownEvent事件
9)如何用名字限定一个注解?
使用@javax.inspit.Named
注解
它的值并不是必需的,但是在没有实际名字的情况下使用@Named是没有意义的。当解析一个注解时,CDI将寻找任何同样包含相同限定符的正确类型的bean。在有@Named的情况下,注解的值部分也必须匹配。
10)如何使用注解来限定和配置依赖?
使用producer中的InjectionPoint和限定符注解上的非绑定属性的组合,可以同时限定和配置一个bean。
使用
11)创建拦截器
创建@javax.init.AroundInvoke
和@javax.init.AroundConstruct
两个具有相应拦截绑定功能的拦截器。你还需要创建CDI,以便将拦截器编译组合到一个注解中。
首先,使用@javax.interceptor.InterceptorBinding
创建一个注解,这将被用来关联实际的拦截器代码,并对任何你希望被拦截的方法或类进行注解:
@Inherited
@InterceptorBinding
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface LogEvent {
}
@LogEvent
@Interceptor
@Slf4j
public class LogEventInterceptor {
@AroundInvoke
public Object logEvent(InvocationContext ctx) throws Exception {
log.info("method:{}", ctx.getMethod().getName());
return ctx.proceed();
}
}
最后把注解@LogEvent
放到需要的方法上面就可以成功执行
12)测试端口
通过配置quarkus.http.test-port
改变测试端口,为0表示随机端口
13)单元测试
推荐使用AssertJ,依赖
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
记得添加quarkus-hibernate-validator
扩展
@ApplicationScoped
public class HelloService {
public String greeting(@Length(min = 6) String message) {
return "say:" + message;
}
}
测试方法如下
@QuarkusTest
class HelloServiceTest {
@Inject
HelloService helloService;
@Test
void greeting1() {
Assertions.assertThatExceptionOfType(ConstraintViolationException.class)
.isThrownBy(()-> helloService.greeting("hello"));
}
@Test
void greeting2() {
String message = helloService.greeting("hello man");
Assertions.assertThat(message).isEqualTo("say:hello man");
}
}
14)使用Mockito创建mock对象
引入pom
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5-mockito</artifactId>
<scope>test</scope>
</dependency>
15)元注解
再将这个注解应用到一个类上,也就是意味着你同时应用了@QuarkusTest
和@Transactional
注解:
16)在测试代码前后执行代码
继承io.quarkus.test.common.QuarkusTestResourceLifecycleManager
public class DefaultQuarkusTestResourceLifecycleManager implements QuarkusTestResourceLifecycleManager {
@Override
public Map<String, String> start() {
System.out.println("lifecycle start");
return Collections.emptyMap();
}
@Override
public void stop() {
System.out.println("lifecycle stop");
}
@Override
public void inject(Object testInstance) {
System.out.println("executing "+testInstance.getClass().getName());
}
@Override
public int order() {
return 0;
}
}
@QuarkusTest
@QuarkusTestResource(DefaultQuarkusTestResourceLifecycleManager.class)
public class HelloResourceTest {
@Test
public void testHelloEndpoint() {
given()
.when().get("/hello")
.then()
.statusCode(200)
.body(is("hello"));
}
}
❶在Quarkus启动前调用start方法
❷在HelloResourceTest运行前调用inject方法
❸在所有测试执行完毕后调用stop方法
六、打包Quarkus应用程序
-
在JVM中运行应用程序
mvn clean package
打包应用执行
java -jar target/hello-1.0-runner.jar
即可 -
若要把lib包的依赖都打到一个jar包中去,只需配置
quarkus.package.uber-jar=true
七、持久化
1)配置数据源
quarkus:
datasource:
jdbc:
url: jdbc:mysql://mysql.ops.svc.cluster.local:3306/seedserver_dev?autoReconnect=true&useUnicode=true&zeroDateTimeBehavior=convertToNull&useSSL=false&allowMultiQueries=true&serverTimezone=GMT%2b8
username: root
password: root
db-kind: mysql
2)配置多数据源
quarkus:
datasource:
dev:
jdbc:
url: jdbc:mysql://mysql.ops.svc.cluster.local:3306/seedserver_dev?autoReconnect=true&useUnicode=true&zeroDateTimeBehavior=convertToNull&useSSL=false&allowMultiQueries=true&serverTimezone=GMT%2b8
username: root
password: root
db-kind: mysql
test:
jdbc:
url: jdbc:mysql://mysql.ops.svc.cluster.local:3306/seedserver_test?autoReconnect=true&useUnicode=true&zeroDateTimeBehavior=convertToNull&useSSL=false&allowMultiQueries=true&serverTimezone=GMT%2b8
username: root
password: root
db-kind: mysql
测试代码
import io.agroal.api.AgroalDataSource;
import io.quarkus.agroal.DataSource;
import io.quarkus.test.junit.QuarkusTest;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import javax.inject.Inject;
import java.sql.Connection;
import java.sql.SQLException;
@QuarkusTest
class DataSourceServiceTest {
@Inject
@DataSource(value = "test")
AgroalDataSource devDataSource;
@Test
void printDev() throws SQLException {
Connection connection = devDataSource.getConnection();
Assertions.assertThat(connection.getCatalog()).isEqualTo("seedserver_test");
}
}
3)事务定义
使用quarkus-narayana-jta
扩展添加了@javax.transaction.Transactional注解以及TransactionManager和UserTransaction
4)设置事务的上下文
-
@Transactional(REQUIRED)(默认)
如果没有启动事务,则启动;否则,保持现有的事务。
-
@Transactional(REQUIRES_NEW)
如果没有启动事务,则启动;如果已经启动了一个现有的事务,则
暂停该事务,并在该方法的结尾启动一个新的事务。
-
@Transactional(MANDATORY)
如果没有启动事务,则失败;否则,使用现有的事务。
-
@Transactional(SUPPORTS)
如果一个事务已经开始,则加入它;否则,以无事务的方式工作。
-
@Transactional(NOT_SUPPORTED)
如果一个事务被启动,则暂停它,并在方法的结尾以无事务的方式
工作;否则,以无事务的方式工作。
-
@Transactional(NEVER)
如果一个事务被启动,则抛出一个异常;否则,以无事务的方式工
作。
5)用Panache持久化数据
调用PanacheEntity
中的persist
方法。
当然,你需要添加quarkus-hibernate-orm-panache
扩展,并为你的数据存储添加相应的JDBC扩展。接下来,你需要定义一个实体,也就是需要创建一个类,用@javax.persistence.Entity
注解它,并基于PanacheEntity
进行扩展。
简单例子:
@Entity
public class FruitEntity extends PanacheEntity {
@Column(length = 40, unique = true)
public String name;
public FruitEntity() {
}
public FruitEntity(String name) {
this.name = name;
}
}
@Path("entity/fruits")
@ApplicationScoped
@Produces("application/json")
@Consumes("application/json")
public class FruitEntityResource {
@POST
@Transactional
public Response create(FruitEntity fruit) {
if (fruit.id != null) {
throw new WebApplicationException("Id was invalidly set on request.", 422);
}
// 持久化
fruit.persist();
return Response.ok(fruit).status(201).build();
}
}
6)更多Panache方法
FruitEntity.listAll();
FruitEntity.findById()
FruitEntity.find(HQL语句)
FruitEntity.list()
FruitEntity.count(HQL语句)
7)用Panache连接mongo
添加pom
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-mongodb-panache</artifactId>
</dependency>
用法类似db
八、容错
1)自动重试
添加
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-fault-tolerance</artifactId>
</dependency>
简单例子,重试3次,执行错误结果
@ApplicationScoped
public class HelloService {
@Retry(maxRetries = 3)
@Fallback(FailCallback.class)
public String greeting(@Length(min = 6) String message) {
System.out.println("enter greeting");
if (message.length() == 7) {
throw new RuntimeException("错误7");
}
return "say:" + message;
}
public static class FailCallback implements FallbackHandler<String>{
@Override
public String handle(ExecutionContext executionContext) {
return "fail handle";
}
}
}
@QuarkusTest
class HelloServiceTest {
@Inject
HelloService helloService;
@Test
void greeting2() {
String message = helloService.greeting("hello m");
Assertions.assertThat(message).isEqualTo("fail handle");
}
}
执行结果
可以通过配置参数的形式进行方法或全局的属性配置
fully_qualified_class_name/method_name/fault_tolerant_annotation/parameter
例如:
# 全局级别
/Retry/delay: 3000
# 或方法级别
com:
lll:
component:
HelloService/greeting/Retry/delay: 3000
# 或类级别
com:
lll:
component:
HelloService/Retry/delay: 3000
当类级别的配置的时候,@Retry
注解也需要在类上,否则仅类中方法标注该注解无效
2)超时
@org.eclipse.microprofile.faultttoler ance.Timeout
注解类或方法
@Timeout(value = 1000)
public String hello(String message) throws InterruptedException {
System.out.println("enter hello");
TimeUnit.SECONDS.sleep(2);
return "say:" + message;
}
@Test
void hello() throws InterruptedException {
helloService.hello("hello");
}
结果
enter hello
org.eclipse.microprofile.faulttolerance.exceptions.TimeoutException: com.lll.component.HelloService#hello timed out
3)过载保护
如何限制并发请求数?
用@org.eclipse.microprofile.faultttolerance.Bulkhead
注解的类或方法
例子:
@Bulkhead(2)
public String hello(String message) throws InterruptedException {
System.out.println("enter hello");
TimeUnit.SECONDS.sleep(2);
return "say:" + message;
}
@Test
void hello() throws InterruptedException {
List<CompletableFuture> futures = new ArrayList<>();
for (int i = 0; i < 4; i++) {
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
try {
helloService.hello("hello");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
futures.add(future);
}
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
}
结果
enter hello
enter hello
java.util.concurrent.CompletionException: org.eclipse.microprofile.faulttolerance.exceptions.BulkheadException: com.lll.component.HelloService#hello rejected from bulkhead
at java.base/java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:315)
at java.base/java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:320)
at java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1807)
at java.base/java.util.concurrent.CompletableFuture$AsyncRun.exec(CompletableFuture.java:1796)
at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:373)
at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1182)
at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1655)
at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1622)
at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:165)
Caused by: org.eclipse.microprofile.faulttolerance.exceptions.BulkheadException: com.lll.component.HelloService#hello rejected from bulkhead
at io.smallrye.faulttolerance.core.bulkhead.BulkheadBase.bulkheadRejected(BulkheadBase.java:17)
并发数为2,同时到达的其它两个请求就会被拒绝
同样的和重试一样,它也支持全局属性配置
# 全局级别
/Bulkhead/value: 10
# 或方法级别
com:
lll:
component:
HelloService/greeting/Bulkhead/value: 10
# 或类级别
com:
lll:
component:
HelloService/Bulkhead/value: 10
4)断路器
用@org.eclipse.microprofile.faulttolerance.CircuitBreaker
注解的类或方法
@CircuitBreaker(requestVolumeThreshold = 5, failureRatio = 0.6, delay = 2000)
public String helloCircuit(String message) throws InterruptedException {
System.out.println("enter hello");
TimeUnit.MILLISECONDS.sleep(100);
if (RandomUtils.nextInt(1, 5) <= 2) {
System.out.println("enter fail");
throw new RuntimeException("hello fail");
}
return "say:" + message;
}
表示5个请求失败3个则断路保护,延迟2秒后恢复
HTTP Request to /hello/helloCircuit?text=helloCircuit failed, error id: 56145d9f-d726-4de1-a6af-057390cb74ff-20: org.eclipse.microprofile.faulttolerance.exceptions.CircuitBreakerOpenException: com.lll.component.HelloService#helloCircuit circuit breaker is half-open
5)禁用容错
类似自动重试配置
# 或方法级别
com:
lll:
component:
HelloService/greeting/CircuitBreaker/enabled: false
# 或类级别
com:
lll:
component:
HelloService/CircuitBreaker/enabled: false
# 全局级别
CircuitBreaker/enabled: false
# 禁用所有容错
MP_Fault_Tolerance_NonFallback_Enabled: false
九、可观察性
1)健康检查
引入pom
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-health</artifactId>
</dependency>
{
"status": "UP",
"checks": [
{
"name": "Database connections health check",
"status": "UP",
"data": {
"<default>": "UP"
}
}
]
}
2)自定义健康检查
通过创建一个注解为@org.eclipse.microprofile.health.Liveness
和@org.eclipse.microprofile.health.Readiness
的方法来创建自定义健康检查
3)暴露服务指标
引入
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-metrics</artifactId>
</dependency>
调用
curl --header "Accept:application/json" localhost:8080/q/metrics
结果
{
"base": {
"cpu.systemLoadAverage": 6.673828125,
"thread.count": 30,
"classloader.loadedClasses.count": 16004,
"classloader.unloadedClasses.total": 7,
"gc.total;name=G1 Young Generation": 14,
"jvm.uptime": 162059,
"thread.max.count": 62,
"memory.committedHeap": 566231040,
"classloader.loadedClasses.total": 16011,
"cpu.availableProcessors": 8,
"thread.daemon.count": 20,
"gc.total;name=G1 Old Generation": 0,
"memory.maxHeap": 4294967296,
"cpu.processCpuLoad": 0.0003392775819228651,
"gc.time;name=G1 Old Generation": 0,
"memory.usedHeap": 295558848,
"gc.time;name=G1 Young Generation": 190
},
"vendor": {
"memoryPool.usage.max;name=G1 Survivor Space": 21979920,
"memory.freePhysicalSize": 91815936,
"memoryPool.usage.max;name=CodeHeap 'non-profiled nmethods'": 3972608,
"memoryPool.usage;name=Metaspace": 83864016,
"memoryPool.usage;name=G1 Eden Space": 0,
"memoryPool.usage;name=CodeHeap 'non-profiled nmethods'": 3642880,
"memoryPool.usage;name=CodeHeap 'profiled nmethods'": 19791104,
"memoryPool.usage;name=G1 Old Gen": 160471552,
"memoryPool.usage.max;name=CodeHeap 'non-nmethods'": 1449856,
"memoryPool.usage.max;name=G1 Old Gen": 160471552,
"cpu.processCpuTime": 14605324000,
"memory.committedNonHeap": 123207680,
"memoryPool.usage.max;name=Compressed Class Space": 10443936,
"memoryPool.usage.max;name=G1 Eden Space": 285212672,
"memory.freeSwapSize": 1312292864,
"memoryPool.usage.max;name=Metaspace": 83863688,
"cpu.systemCpuLoad": 0.315098789643286,
"memory.usedNonHeap": 119182168,
"memoryPool.usage;name=CodeHeap 'non-nmethods'": 1439616,
"memoryPool.usage;name=G1 Survivor Space": 869568,
"memoryPool.usage;name=Compressed Class Space": 10443936,
"memory.maxNonHeap": -1,
"memoryPool.usage.max;name=CodeHeap 'profiled nmethods'": 21394432
},
"application": {
}
}
-
base
服务器的核心信息。这些指标总是必需的,因为它们在规范中被指定。可在/metrics/base访问它们。
-
vendor
供应商的具体信息。每个实施方案可能会提供不同的信息。可在/metrics/vendor访问它们。
-
application
使用MicroProfile Metrics扩展机制为该服务专门开发的自定义信息。可在/metrics/application访问它们。
4)创建指标
@Counted
计数器,用来记录方法执行次数
@Gauge
用来查询自定义指标
@Metered
用来查询方法调用速度
@Timed
用来记录方法调用时长
综合案例
@Timed(name = "checksTimer", description = "A measure how long it takes to perform the primality test.", unit = MetricUnits.MILLISECONDS)
@Metered(name = "helloMetered")
@Counted(name = "performedChecks", description = "How many primality checks have been performed.")
public String helloCircuit(String message) throws InterruptedException {
System.out.println("enter hello");
return "say:" + message;
}
@Gauge(name = "highestPrimeNumberSoFar", unit = MetricUnits.NONE, description = "Highest prime number so far.")
public Long getLong() throws InterruptedException {
System.out.println("enter getLong");
return RandomUtils.nextLong();
}
@GET
@Path("/helloCircuit")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public Result helloCircle(@QueryParam("text") String text) throws InterruptedException {
helloService.getLong();
return Result.<String>ok(helloService.helloCircuit(text));
}
调用localhost:8080/helloCircuit
多次后执行
curl --header "Accept:application/json" localhost:8080/q/metrics/application
结果
{
"com.lll.component.HelloService.helloMetered": {
"fiveMinRate": 0.8838044191690506,
"fifteenMinRate": 1.523369957905502,
"meanRate": 0.03955995332301929,
"count": 10,
"oneMinRate": 0.0337024025198951
},
"com.lll.component.HelloService.checksTimer": {
"p99": 102.830667,
"min": 0.152542,
"max": 102.830667,
"mean": 50.73874038036284,
"p50": 0.96975,
"p999": 102.830667,
"stddev": 50.675931154849316,
"p95": 102.830667,
"p98": 102.830667,
"p75": 101.428083,
"fiveMinRate": 0.8838044191690506,
"fifteenMinRate": 1.523369957905502,
"meanRate": 0.03955965314051408,
"count": 10,
"oneMinRate": 0.0337024025198951,
"elapsedTime": 510.434292
},
"com.lll.component.HelloService.highestPrimeNumberSoFar": 3072960605013709814,
"com.lll.component.HelloService.performedChecks": 10
}
5)分布式链路追踪
参考官网OpenTracing jaeger
十、认证和授权
1)使用Elytron Security JDBC配置进行认证授权
首先加入pom
mvn quarkus:add-extension -Dextensions="quarkus-elytron-security-jdbc,quarkus-jdbc-mysql"
配置
quarkus:
security:
jdbc:
enabled: true
principal-query:
sql: select u.password,u.role from test_user u where u.username=?
clear-password-mapper:
enabled: true
password-index: 1
attribute-mappings:
0:
index: 2
to: groups
@Path("/hello")
@RolesAllowed("Tester")
public String hello(@Size(min = 5, max = 10) @QueryParam("text") String text) {
return "hello";
}
调用localhost:8080/hello
需要认证
输入数据库用户名密码通过
以上为明文密码,实际生成需要使用加密配置
Quarkus还提供了jwt加密,openId加密方式等具体详看文末链接
十一、使用Spring API开发Quarkus
1)Spring依赖注入
引入包
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-spring-di</artifactId>
</dependency>
以下注解等价可替换
2)Spring Web
引入包
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-spring-web</artifactId>
</dependency>
com.lll.component.HelloService;
import com.lll.entity.Result;
import com.lll.intercept.LogEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.constraints.Size;
import javax.ws.rs.QueryParam;
@RestController
@RequestMapping("/hello2")
public class HelloSpringResource {
@Autowired
HelloService helloService;
@GetMapping
public String hello(@Size(min = 5, max = 10) @QueryParam("text") String text) {
Person person = new Person();
person.setName(text);
return text;
}
@GetMapping("/helloCircuit")
public Result helloCircle(@QueryParam("text") String text) throws InterruptedException {
helloService.getLong();
return Result.<String>ok(helloService.helloCircuit(text));
}
}
和Spring用法一致
还有Spring的JPA、security、config兼容等等,感兴趣可以看看
十二、Quarkus附加功能
- 模板引擎
Qute
,提供创建模板的功能 - 发送电子邮件
mailer
扩展 - 调度任务
scheduler
- 本地缓存
cache
详情看文末链接
📌《Quarkus实战》链接: https://pan.baidu.com/s/1U3WVpIkFb_mjffTyF1zT2w?pwd=2p7r 提取码: 2p7r