【Quarkus】云原生框架Quarkus快速上手

Quarkus是Red Hat开源的云原生的java框架。官网是https://cn.quarkus.io/。官网的口号是超音速/亚原子。意思是运行速度很快。支持Kubernetes Native Java栈,为OpenJDK HotSpot和GraalVM量身定制,从Java库和标准中精心打造。官方很简单的介绍 用了超大字体描述,内容简洁:超音速、亚原子的JAVA

目录

1.构建项目

1.1idea构建

2.导入依赖

3.项目结构

4.项目启动

4.1idea插件启动

4.2maven命令启动

2.常用注解

1.Rest

1.@Path请求路径

2.@POST,@GET等请求方式

3.@QueryParam获取请求路径上的参数

4.@PathParam请路径中获取参数

5.@HeaderParam从请求头中获取值

6.获取请求体中值并封装为对象

7.@FormParam获取form表单中的值

2. @Provider

2.1配置过滤器

2.1.1配置请求过滤器

2.2.2响应过滤器

2.2全局异常处理

2.2.1自定义业务异常

2.2.2全局异常处理器

3.@Inject依赖注入

4.@ConfigProperty读取配置文件

4.1注入config对象获取配置文件对象

5.@ApplicationScoped

6.@Produces将返回值注入到容器中

7.@Qualifier注解选择指定的bean

7.1自定一个注解,用来区分不同的bean

7.2在特定的bean上使用自定义的标记注解

7.3注入时使用自定义注解标记bean

8.@DefaultBean

9.@Named注解注解指定名称的bean

10.@PostConstruct和@PreDestroy 对bean在创建或销毁对象前后执行一些逻辑

11.监听应用程序的启动和销毁

12.@AroundInvoke与@Interceptor配置拦截器

12.1@InterceptorBinding定义切点

12.2@Interceptor定义拦截器(切面), @AroundInvoke标记通知

12.3使用

3.容错

3.1自动重试

3.2超时

3.3限流

3.4熔断

4.消息中间件

4.1消费者

4.1.@Incoming消费消息

4.1.2配置文件

4.2生产者

4.2.1@Outgoing发送消息

4.2.2配置文件

4.2.2通过Emitter发送指定内容

@QuarkusTest测试

@RegisterForReflection反射

@JsonProperty反序列化映射关系

5.集成mybatis持久化

5.1导入依赖

5.2添加配置

5.3编写XMl文件与mapper对象,实体对象,仓储对象


1.构建项目

1.1idea构建

通过idea构建项目,选择maven管理,jdk选择17(最低要求),maven版本要求3.6以上

2.导入依赖

完整的pom文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>cn.lgc</groupId>
    <artifactId>quarkus-day01</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <compiler-plugin.version>3.12.1</compiler-plugin.version>
        <maven.compiler.release>17</maven.compiler.release>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
        <quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
        <quarkus.platform.version>3.10.2</quarkus.platform.version>
        <skipITs>true</skipITs>
        <surefire-plugin.version>3.2.5</surefire-plugin.version>
    </properties>
    <!--Quarkus平台BOM(Bill of Materials,物料清单)的依赖管理配置-->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>${quarkus.platform.group-id}</groupId>
                <artifactId>${quarkus.platform.artifact-id}</artifactId>
                <version>${quarkus.platform.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <!--Arc是Quarkus中的一个核心组件,
        负责管理bean的生命周期、依赖注入(DI)等,
        类似于Spring框架中的IoC容器。
        使用@Inject注解进行依赖注入,或者使用@ApplicationScoped、@Singleton等范围注解定义bean的生命周期。
        -->
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-arc</artifactId>
        </dependency>
        <!--
        在Quarkus框架中,用于构建RESTful服务的扩展;
        类似于springmvc的功能
        -->
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-resteasy-reactive</artifactId>
        </dependency>
        <!--json支持,配合quarkus-resteasy依赖使用,
        这两个依赖项会帮助您在Quarkus应用中快速搭建RESTful API,
        利用RESTEasy框架简化HTTP请求处理,并通过Jackson处理JSON序列化和反序列化-->
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-resteasy-reactive-jackson</artifactId>
        </dependency>
        <!--单元测试集成-->
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-junit5</artifactId>
            <scope>test</scope>
        </dependency>
        <!--
        HTTP请求测试库,
        特别适用于进行RESTful服务的自动化测试
        -->
        <dependency>
            <groupId>io.rest-assured</groupId>
            <artifactId>rest-assured</artifactId>
            <scope>test</scope>
        </dependency>
        <!--mybatis依赖-->
        <dependency>
            <groupId>io.quarkiverse.mybatis</groupId>
            <artifactId>quarkus-mybatis</artifactId>
            <version>2.2.2</version>
        </dependency>
        <!--MySQL数据库连接支持集成到Quarkus-->
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-jdbc-mysql</artifactId>
        </dependency>

        <!--lombok依赖,用于简化Java代码的书写-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.32</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <!--这个插件是Quarkus应用的核心构建工具,
                用于构建、测试、运行和打包Quarkus应用,支持原生镜像的构建等特性-->
                <groupId>${quarkus.platform.group-id}</groupId>
                <artifactId>quarkus-maven-plugin</artifactId>
                <version>${quarkus.platform.version}</version>
                <extensions>true</extensions>
                <executions>
                    <execution>
                        <goals>
                            <goal>build</goal>
                            <goal>generate-code</goal>
                            <goal>generate-code-tests</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <!--用于编译Java源代码-->
                <artifactId>maven-compiler-plugin</artifactId>
                <version>${compiler-plugin.version}</version>
                <configuration>
                    <compilerArgs>
                        <arg>-parameters</arg>
                    </compilerArgs>
                </configuration>
            </plugin>
            <plugin>
                <!--负责运行单元测试-->
                <artifactId>maven-surefire-plugin</artifactId>
                <version>${surefire-plugin.version}</version>
                <configuration>
                    <systemPropertyVariables>
                        <java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
                        <maven.home>${maven.home}</maven.home>
                    </systemPropertyVariables>
                </configuration>
            </plugin>
            <plugin>
                <!--用于执行集成maven测试-->
                <artifactId>maven-failsafe-plugin</artifactId>
                <version>${surefire-plugin.version}</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>integration-test</goal>
                            <goal>verify</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <systemPropertyVariables>
                        <native.image.path>${project.build.directory}/${project.build.finalName}-runner
                        </native.image.path>
                        <java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
                        <maven.home>${maven.home}</maven.home>
                    </systemPropertyVariables>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <profiles>
        <profile>
            <id>native</id>
            <activation>
                <property>
                    <name>native</name>
                </property>
            </activation>
            <properties>
                <skipITs>false</skipITs>
                <quarkus.native.enabled>true</quarkus.native.enabled>
            </properties>
        </profile>
    </profiles>
</project>

3.项目结构

app:对应业务模块接口以及领域对象

repository:数据层,用于持久化

app/port:定义资源接口

package cn.lgc.demo.app.example1.port;

import cn.lgc.demo.app.example1.model.dto.UserDto;

/**
 * 资源定义:示例接口
 *
 * @author clid
 * @date 2024-05-23
 */
public interface ExampleApi {
    String hello();
}

app/api:定义资源实现(类似于mvc中的controller)

package cn.lgc.demo.app.example1.api;

import cn.lgc.demo.app.example1.model.dto.UserDto;
import cn.lgc.demo.app.example1.port.ExampleApi;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;

/**
 * 资源实现:示例资源
 *
 * @author clid
 * @date 2024-05-23
 */
@Path("/hello")//定义资源路径
@ApplicationScoped//定义对象作用域,次注解表示为单例
public class ExampleResource implements ExampleApi {
    @Inject
    ObjectMapper objectMapper;

    @GET//GET请求
    @Produces(MediaType.TEXT_PLAIN)//响应内容类型为纯文本
    @Override
    public String hello() {
        return "Hello1 from Quarkus REST";
    }
}

model:领域对象

package cn.lgc.demo.app.example1.model.dto;

import jakarta.enterprise.context.ApplicationScoped;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@NoArgsConstructor
@AllArgsConstructor
@Data
@ApplicationScoped//单例,并且支持CDI依赖注入
public class UserDto {
    private String name;
    private String sex;
}

4.项目启动

4.1idea插件启动

4.2maven命令启动
mvn compile quarkus:dev  

2.常用注解

1.Rest

Rest类注解类似于springMvc的一系列注解

1.@Path请求路径

设置接口请求的路径

@Path("/v70/gpx/buyPlan")
@ApplicationScoped
public class PlanResource {

    @POST
    @Path("/getBuyPlan")
    public Response getBuyPlan(ReceivedRequestDTO payload) {
        
    }
2.@POST,@GET等请求方式

设置接口中方法的请求方式

3.@QueryParam获取请求路径上的参数

只能获取标准格式的参数,如:http://localhost:8080/hello?name=lgc

    @GET
    @Path("/name")
    @Produces(MediaType.TEXT_PLAIN)
    public String hello2(@QueryParam("name") String name) {
        return "从路径上获取到的内容为:" + name;
    }
4.@PathParam请路径中获取参数

获取格式标准,如:http://localhost:8080/hello/lgc

 @GET
    @Path("/name/{name}")
    @Produces(MediaType.TEXT_PLAIN)
    public String hello4(@PathParam("name") String name) {
        System.out.println(name);
        return name;
    }
5.@HeaderParam从请求头中获取值
@GET
    @Path("/sex")
    @Produces(MediaType.TEXT_PLAIN)
    public String hello3(@HeaderParam("sex") String sex) {
        System.out.println(sex.equals("1") ? "男" : "女");
        return sex;
    }

请求示例

6.获取请求体中值并封装为对象
  /**
     * 获取请求体中参数并封装为对象
     */
    @POST
    @Path("/user")
    public UserDto hello5(UserDto user) {
        System.out.println(user);
        user.setName("lgc");
        return user;
    }

请求示例

7.@FormParam获取form表单中的值
/**
     * 获取form表单中指定内容的值,例如:content:123
     */
    @POST
    @Path("/form")
    public String hello6(@FormParam("content") String content) {
        System.out.println(content);
        return content;
    }

请求示例

2. @Provider

在Jakarta EE的上下文中,@Provider注解主要用于JAX-RS(Java API for RESTful Web Services)规范中。这个注解标记了一个类,表明该类是一个服务提供者(Provider),能够参与处理HTTP请求和响应的生命周期。

标注了@Provider的类可以是消息体读取器(MessageBodyReader)、消息体写入器(MessageBodyWriter)、异常映射提供者(ExceptionMapper)等。这些组件使得开发者能够自定义数据的序列化与反序列化过程,或者异常的处理方式,从而增强RESTful服务的功能性和灵活性

2.1配置过滤器
2.1.1配置请求过滤器

进入接口前会先进入到请求过滤器

package cn.lgc.demo.infrastructure.filter;


import jakarta.ws.rs.container.ContainerRequestContext;
import jakarta.ws.rs.container.ContainerRequestFilter;
import jakarta.ws.rs.ext.Provider;

import java.io.IOException;
@Provider
public class MyRequestFilter implements ContainerRequestFilter {
    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {
        System.out.println(requestContext);
    }
}
2.2.2响应过滤器

响应给用户前,会先进入响应过滤器

package cn.lgc.demo.infrastructure.filter;


import jakarta.ws.rs.container.ContainerRequestContext;
import jakarta.ws.rs.container.ContainerResponseContext;
import jakarta.ws.rs.container.ContainerResponseFilter;
import jakarta.ws.rs.ext.Provider;
import org.jboss.logging.Logger;

import java.io.IOException;
@Provider
public class MyResponseFilter implements ContainerResponseFilter {
    private static final Logger logger = Logger.getLogger(MyResponseFilter.class);
    @Override
    public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
        logger.infof("requestContext:%s----responseContext:%s",requestContext,responseContext);
    }
}
2.2全局异常处理
2.2.1自定义业务异常
/**
 * 业务异常
 *
 * @author 
 * @date 2024-05-24
 */
public class BusinessException extends RuntimeException{
    public BusinessException(String message) {
        super(message);
    }
}
2.2.2全局异常处理器
/**
 * 异常处理程序
 *
 * @author 
 * @date 2024-05-24
 */
@Provider
public class MyExceptionHandler implements ExceptionMapper<Exception> {

    private static final Logger logger = Logger.getLogger(MyExceptionHandler.class);

    @Override
    public Response toResponse(Exception exception) {
        logger.infof("异常日志: %s ", exception.getMessage());
        if (exception instanceof BusinessException) {
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                    .entity("业务繁忙").encoding("UTF-8").build();
        } else {
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                    .entity(exception.getMessage()).build();
        }
    }
}

3.@Inject依赖注入

在类的字段、构造函数或方法上使用@Inject时,容器(如Jakarta EE应用服务器)会自动为该注解的元素提供一个依赖的实例

@QuarkusTest
class PlanResourceTest {

    @Inject
    ObjectMapper mapper;

    @ConfigProperty(name = "system.gpms.encrypt.key")
    String key;

4.@ConfigProperty读取配置文件

更加推荐构造器注入:

当类上加了@ApplicationScoped注解,字段使用final修饰,提供构造方法,无需使用@Inject注入

@ApplicationScoped
public class PlanResource extends AbstractResource implements PlanApi {

    private final ObjectMapper mapper;
    private final ForwardRepository repository;
    private final RequestForwardAdapter adapter;

    private static final Logger logger = Logger.getLogger(PlanResource.class);

    /**
     * 构造器(依赖注入)
     *
     * @param mapper     对象序列化映射工具
     * @param repository 数据存储服务
     * @param adapter    请求转发服务
     * @param key        数据交换秘钥
     */
    public PlanResource(
            ObjectMapper mapper,
            ForwardRepository repository,
            RequestForwardAdapter adapter,
            @ConfigProperty(name = "system.gpms.encrypt.key") String key) {

        this.mapper = mapper;
        this.repository = repository;
        this.adapter = adapter;

        super.setKey(key);
    }
4.1注入config对象获取配置文件对象
@Path("/hello")//定义资源路径
@ApplicationScoped
public class ExampleResource implements ExampleApi {
    @Inject
    Config config;

    /**
     * @PathParam:从路径中获取值 获取格式标准,如:http://localhost:8080/hello/lgc
     */
    @Override
    @GET
    @Path("/name/{name}")
    @Produces(MediaType.TEXT_PLAIN)
    @LogEvent
    public String hello4(@PathParam("name") String name) {
        String value = config.getValue("quarkus.http.port", String.class);
        return value;
    }

5.@ApplicationScoped

类似于@Singleton注解

在Java EE(现在称为Jakarta EE)中用于定义Bean的作用域。当一个类被标记为@ApplicationScoped时,这意味着对于整个Web应用程序,该Bean只有一个共享的实例。这个实例在应用程序启动时创建,并且在整个应用程序的生命周期中都存在,直到应用程序关闭。

@ApplicationScoped
public class AgentRepository 

6.@Produces将返回值注入到容器中

允许创建任何类型的对象,等同于Spring中@Bean

/**
 * 用户工厂
 *
 * @author 
 * @date 2024-05-24
 */
@ApplicationScoped
public class UserDtoFactory {
    @Produces
    public UserDto create() {
        return new UserDto("李四", "男");
    }
}

7.@Qualifier注解选择指定的bean

当有多个相同的bean时,通过@Qualifier选择。这里和spring中的@Qualifier使用方式不太一样

7.1自定一个注解,用来区分不同的bean
@Qualifier
@Retention(RUNTIME)
@Target({METHOD, FIELD, PARAMETER, TYPE})
public @interface SpecialUser {
}
7.2在特定的bean上使用自定义的标记注解

普通注册

@NoArgsConstructor
@AllArgsConstructor
@Data
@ApplicationScoped
public class UserDto {

    private String name;
    private String sex;
}

工厂注册

/**
 * 用户工厂
 *
 * @author 
 * @date 2024-05-24
 */
@ApplicationScoped
public class UserDtoFactory {
    @Produces
    @SpecialUser
    public UserDto create() {
        return new UserDto("李四", "男");
    }
}
7.3注入时使用自定义注解标记bean

这样注入的则会是同样被注解标记的bean

@Path("/hello")//定义资源路径
@ApplicationScoped
public class ExampleResource implements ExampleApi {
    @Inject
    ObjectMapper objectMapper;
    @Inject
    @SpecialUser
    UserDto userDto;

8.@DefaultBean

果一个bean使用这个注释进行注释,这意味着该bean将仅在没有配置其他此类型的bean时用作默认bean。被这个注解标记的bean会失去注入优先级。

9.@Named注解注解指定名称的bean

@NoArgsConstructor
@AllArgsConstructor
@Data
@ApplicationScoped
@Named("user")
public class UserDto {

    private String name;
    private String sex;
}
@ApplicationScoped
public class UserDtoFactory {
    @Produces
    @Named("userDto")
    public UserDto create() {
        return new UserDto("李四", "男");
    }
}

注入时通过指定name来获取指定的bean

@Path("/hello")//定义资源路径
@ApplicationScoped
public class ExampleResource implements ExampleApi {
    @Inject
    ObjectMapper objectMapper;
    @Inject
    @Named("userDto")
    UserDto userDto;

10.@PostConstruct和@PreDestroy 对bean在创建或销毁对象前后执行一些逻辑

@NoArgsConstructor
@AllArgsConstructor
@Data
@ApplicationScoped
@Named("user")
public class UserDto {

    private String name;
    private String sex;

    /**
     * 初始化后被调用
     */
    @PostConstruct
    private void init() {
        System.out.println("init");
    }

    /**
     * 摧毁前被调用
     */
    @PreDestroy
    public void destroy() {
        System.out.println("destroy");
    }
}

11.监听应用程序的启动和销毁

io.quarkus.runtime.StartupEvent和io.quarkus.runtime.ShutdownEvent事件:在应用程序启动时,Quarkus会产生StartupEvent事件;而在关闭时,会产生ShutdownEvent事件

/**
 * 应用程序事件监听器
 *
 * @author 
 * @date 2024-05-24
 */
@ApplicationScoped
public class ApplicationEvenListener {

    private void onStartup(@Observes StartupEvent event) {
        System.out.println("Application startup");
    }

    private void onShutdown(@Observes ShutdownEvent event) {
        System.out.println("Application shutdown");
    }
}

12.@AroundInvoke与@Interceptor配置拦截器

@AroundInvoke类似于AOP中的环绕通知,@Interceptor类似于spring中的切面,还需要通过@InterceptorBinding定义切点与切面绑定

12.1@InterceptorBinding定义切点
/**
 * 日志事件
 *
 * @author 
 * @date 2024-05-24
 */
@InterceptorBinding
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface LogEvent {
}
12.2@Interceptor定义拦截器(切面), @AroundInvoke标记通知
/**
 * 日志事件拦截器
 *
 * @author 
 * @date 2024-05-24
 */
@Interceptor
@LogEvent
public class LogEventInterceptor {
    @AroundInvoke
    public Object logEvent(InvocationContext ctx) throws Exception {
        System.out.println("方法执行前执行");
        Object proceed = ctx.proceed();
        System.out.println("方法执行后执行");
        return proceed;
    }

}
12.3使用

hello4方法执行前后会先进入到LogEventInterceptor中执行logEvent方法


    @GET
    @Path("/name/{name}")
    @Produces(MediaType.TEXT_PLAIN)
    @LogEvent
    public String hello4(@PathParam("name") String name) {
        System.out.println(name);
        System.out.println(userDto);
        return name+"哈哈";
    }

3.容错

3.1自动重试

导入依赖

        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-smallrye-fault-tolerance</artifactId>
        </dependency>

@Retry自动重试

用@org.eclipse.microprofile.faulttolerance.Retry注解的类或方法会在抛出异常时执行自动重试。你可以设置不同的参数,比如最大重试次数、最大持续时间。你也可以指定重试应该执行的异常类型。

@Fallback降级数据

@org.eclipse.microprofile.faulttolerance.Fallback注解方法来实现回调逻辑。作为回调执行的逻辑可以以实现org.eclipse.microprofile.faulttolerance.FallbackHandler接口的类来实现

 @POST//GET请求
    @Path("/add")
    @Transactional
    //maxRetries重试次数,delay间隔时间
    @Retry(maxRetries = 4, delay = 1000)
    @Fallback(ExampleFallback.class)
    public Response add(UserDto userDto) {
        User user = new User();
        user.setName(userDto.getName());
        user.setAge(10);
        System.out.println("重试");
        System.out.println(1/0);
        userRepository.insertUser(user);
        return Response.ok(userDto).build();
    }

兜底数据

public class ExampleFallback implements FallbackHandler<Response> {
    @Override
    public Response handle(ExecutionContext context) {
        return Response.ok("网络异常").status(Response.Status.INTERNAL_SERVER_ERROR).build();
    }
}

3.2超时

MicroProfile Fault Tolerance规范提供了一种方法来实现操作的超时,防止执行永远等待。当有一个外部服务的调用时,确保这个操作有一个与之相关的超时是一个好的做法。这样,如果出现网络延迟或故障,进程不会等待很长时间最后以失败告终,而是快速失败,这样你就可以尽快对问题做出反应。

用@org.eclipse.microprofile.faultttoler ance.Timeout注解的类或方法定义了一个超时,如果有超时,那么就会抛出org.eclipse.microprofile.faulttolerance.exceptions.TimeoutException异常。

    @POST//GET请求
    @Path("/add")
    @Transactional
    @Timeout(value = 3000)
    public Response add(UserDto userDto) {
        User user = new User();
        user.setName(userDto.getName());
        user.setAge(10);
        userRepository.insertUser(user);
        return Response.ok(userDto).build();
    }

全局配置

org.acme.quarkus.ServiceInvoke/add/Timeout/value=2000 #当前add方法
org.acme.quarkus.ServiceInvoke/Timeout/value=2000 #当前类
Timeout/value=2000 #全局

3.3限流

使用MicroProfile Fault Tolerance规范提供的bulkhead pattern(舱壁模式)来实现。bulkhead模式限制了可以同时执行的操作,让新的请求保持等待,直到当前的执行请求完成。如果等待的请求在一定时间后不能执行,则会被丢弃并抛出异常。用@org.eclipse.microprofile.faultttolerance.Bulkhead注解的类或方法应用了一个bulkhead限制。如果有同步调用,当达到并发执行的限制时,会抛出org.eclipse.microprofile.faulttolerance.exceptions.BulkheadException异常,而不是将请求排队。

    @POST
    @Path("/add")
    @Transactional
    @Bulkhead(value = 2)//将并发请求上限设置为2
    public Response add(UserDto userDto) {
        User user = new User();
        user.setName(userDto.getName());
        user.setAge(10);
        userRepository.insertUser(user);
        return Response.ok(userDto).build();
    }

全局配置

org.acme.quarkus.ServiceInvoke/add/Bulkhead/value=2 #当前add方法
org.acme.quarkus.ServiceInvoke/Bulkhead/value=2 #当前类
Bulkhead/value=2 #全局

3.4熔断

MicroProfile Fault Tolerance规范提供了circuit breaker pattern(断路器模式),以避免在出现错误时进行不必要的调用。

    @POST//GET请求
    @Path("/add")
    @Transactional
    @CircuitBreaker(
            requestVolumeThreshold = 4,//请求次数
            failureRatio = 0.75,//失败率
            delay = 10000//重新打开时间
    )
    public Response add(UserDto userDto) {
        User user = new User();
        user.setName(userDto.getName());
        user.setAge(10);
        System.out.println(1/0);
        userRepository.insertUser(user);
        return Response.ok(userDto).build();
    }

用@org.eclipse.microprofile.faulttolerance.CircuitBreaker注解的类或方法为该操作定义了一个断路器。如果电路被打开,就会抛出org.eclipse.microprofile.faultttolerance.CircuitBreakerOpenException异常。

也可以将@CircuitBreaker与@Timeout、@Fallback、@Bulkhead或@Retry混合使用,但必须考虑到以下几点:

  • 如果使用@Fallback,且CircuitBreakerOpenException被抛出,回退逻辑将被执行。
  • 如果使用@Retry,每次重试都由断路器处理,并记录成功或失败。
  • 如果使用@Bulkhead,则在试图进入bulkhead之前检查断路器。

4.消息中间件

4.1消费者

4.1.@Incoming消费消息

是Eclipse MicroProfile Reactive Messaging规范中的一部分,它提供了一种声明式的方式来处理从消息渠道(通常称为消息通道message channel)接收到的消息。这个注解主要用于接收异步消息,是实现消息驱动的微服务架构的关键元素之一 。

  • 消息消费:当你在一个方法上使用@Incoming注解时,这个方法将成为一个消息处理器,用于消费来自指定消息通道的消息。Eclipse MicroProfile框架会在后台监听这个通道,每当有新消息到来时,就会自动调用这个标注了@Incoming的方法来处理消息。
  • 异步处理:通过@Incoming注解,应用能够以非阻塞的方式处理消息,这对于构建高性能、可扩展的系统至关重要,特别是在处理高吞吐量消息流时。
  • 灵活配置:你可以指定从哪个消息通道接收消息,还可以配置消息的消费模式(如:是否是只处理一次、是否批量处理等),以及错误处理策略等。
  • 集成多样:MicroProfile Reactive Messaging支持多种消息中间件的集成,比如Apache Kafka、AMQP、MQTT等,使得开发者可以灵活选择适合自己系统的消息基础设施。

示例:消费gpms通道中有新的消息写入时,会调用这个方法


@ApplicationScoped
public class BusinessConsumer {

    @Blocking
    @Incoming("demoi")
    public CompletionStage<Void> process(Message<String> event) {
        System.out.println(event.getPayload());
        return event.ack();
    }

}
4.1.2配置文件
rabbitmq-host                                      = 121.36.99.171
rabbitmq-port                                      = 18026
rabbitmq-username                                  = rabbitmq-zfcg-test
rabbitmq-password                                  = bssoft@2020

mp.messaging.incoming.demoi.connector               = smallrye-rabbitmq
mp.messaging.incoming.demoi.queue.name              = Demo_Queue
mp.messaging.incoming.demoi.exchange.name           = Demo_Exchange

4.2生产者

4.2.1@Outgoing发送消息
@ApplicationScoped
public class BusinessProducer {
    @
    public Flow.Publisher<String> send() {
        return Multi.createFrom().ticks().every(Duration.ofSeconds(1))
                .map(tick -> "哈哈");
    }

}
4.2.2配置文件
mp.messaging.outgoing.demo.connector               = smallrye-rabbitmq
mp.messaging.outgoing.demo.queue.name              = Demo_Queue
mp.messaging.outgoing.demo.exchange.name           = Demo_Exchange
4.2.2通过Emitter发送指定内容
    @Inject
    @Channel("demo")
    Emitter<String> emitter;

    @GET//GET请求
    @Path("/demo/{context}")
    public Response demo(@PathParam("context") String context) {
        System.out.println(context);
        CompletionStage<Void> send = emitter.send(context);
        return Response.ok().build();
    }

@QuarkusTest测试

@QuarkusTest是Quarkus框架提供的一个注解,用于进行集成测试。

@QuarkusTest
class PlanResourceTest {
    @Inject
    ObjectMapper mapper;

    @ConfigProperty(name = "system.gpms.encrypt.key")
    String key;

    @Test
    void getBuyPlanTest(){
        
    }

@RegisterForReflection反射

指示Quarkus在构建原生可执行文件时,应当将被注解的类或其成员(如字段、方法)包含在反射配置中。这对于使用GraalVM的原生镜像构建特别重要,因为在该过程中,GraalVM会执行严格的静态分析来确定哪些类和方法在运行时需要反射访问,未被明确标记的可能会被树莓掉以减小最终应用的大小和提升启动速度。

@RegisterForReflection
@Data
public class BusinessMessageDTO {

    @JsonProperty("orgCode")
    String org;

    @JsonProperty("messageTopic")
    String topic;

    @JsonProperty("businessID")
    String uuid;

}

@JsonProperty反序列化映射关系


    @JsonProperty("orgCode")
    String org;

这个函数是用在Jackson库中的,用于将JSON字符串反序列化成Java对象或者将Java对象序列化成JSON字符串时,指定对象属性和JSON字段之间的映射关系。具体来说,@JsonProperty("orgCode")注解表示将JSON字符串中的"orgCode"字段的值映射到Java对象的该属性上。

例如

现有一个json字符串,字段中有orgCode,反序列化为java对象时,会将值赋值给org

ObjectMapper mapper;
//ObjectMapper是Jackson库中的一个类,用于进行对象与JSON之间的相互转换
BusinessMessageDTO exchange = 
mapper.readValue(message, BusinessMessageDTO.class);

5.集成mybatis持久化

5.1导入依赖

  <!--mybatis依赖-->
        <dependency>
            <groupId>io.quarkiverse.mybatis</groupId>
            <artifactId>quarkus-mybatis</artifactId>
            <version>2.2.2</version>
        </dependency>
        <!--MySQL数据库连接支持集成到Quarkus-->
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-jdbc-mysql</artifactId>
        </dependency>

5.2添加配置

quarkus.datasource.username                        = root
quarkus.datasource.password                        = 123456
quarkus.datasource.jdbc.url                        = jdbc:mysql://124.70.38.118:3306/test?serverTimezone=Asia/Shanghai&characterEncoding=utf8
quarkus.datasource.jdbc.driver                     = com.mysql.cj.jdbc.Driver

quarkus.mybatis.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

5.3编写XMl文件与mapper对象,实体对象,仓储对象

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.lgc.demo.repository.mapper.UserMapper">
	<insert id="insertUser" parameterType="cn.lgc.demo.repository.entity.User">
		insert into t_user(name,age) values(#{name},#{age})
	</insert>

	<select id="getUserById" resultType="cn.lgc.demo.repository.entity.User">
		select * from t_user where id=#{id}
	</select>
	<select id="getAllUser" resultType="cn.lgc.demo.repository.entity.User">
		select * from t_user
	</select>
	<update id="updateUser" parameterType="cn.lgc.demo.repository.entity.User">
		update t_user set name=#{name},age=#{age} where id=#{id}
	</update>
	<delete id="deleteUser" parameterType="int">
		delete from t_user where id=#{id}
	</delete>
</mapper>

mappr接口

@Mapper
public interface UserMapper {
     void insertUser(User user);

     User getUserById(Long id);

     List<User> getAllUser();

     void updateUser(User user);

     void deleteUser(Integer id);
}

Entity实体类

@RegisterForReflection
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Long id;
    private String name;
    private Integer age;
}

Repository仓储对象

@ApplicationScoped
public class UserRepository {
    private final UserMapper userMapper;

    public UserRepository(UserMapper userMapper) {
        this.userMapper = userMapper;
    }

    public void insertUser(User user) {
        userMapper.insertUser(user);
    }

    public User getUserById(Long id) {
        return userMapper.getUserById(id);
    }

    public void updateUser(User user) {
        userMapper.updateUser(user);
    }

    public void deleteUser(Integer id) {
        userMapper.deleteUser(id);
    }

    public List<User> getAllUser() {
        return userMapper.getAllUser();
    }
}

依赖注入即可使用

  • 26
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值