如何在 Java Microprofile 中正确写入异步 POST/GET

How to write async POST/GET in Java Microprofile correctly

我是一名 C 开发人员,需要使用 Microprofile (Helidon MP) 框架在 Java 中创建一个基本的异步 REST 服务器(同步版本工作正常)。这是我的策略:

客户端应执行 POST 调用并提供 POST 端点将接收的 JSON 对象。然后,POST 端点方法将调用一个业务逻辑,该逻辑应该对接收到的 JSON 对象进行操作。 此逻辑必须 运行 异步。 POST 应立即 return 202 接受。 客户端应使用 GET 请求(简单池化样式)检查异步任务完成状态。

是否应该 POST return GET 调用将使用的 URI?如何?如果任务正在进行中,此 GET 还应提供任务完成的百分比。最后,如果业务逻辑完成,GET 应该 return 结果。

我以前对异步有一点经验Java,但是在这个Microprofile/Java EE/Jakarta 或其他方面没有任何异步经验。我尝试了几种不同的方法(AsyncResponse、CompletitionStatus 等)来编写此代码(async POST 方法),但似乎没有任何效果。 POST 函数的框架如下所示:

@Path("/logic")
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response logic(JsonObject jsonObject){

  BusinessLogic bl = new BusinessLogic();

  // This should be done asynchronously.
  //How to do it correctly in Microprofile?
  bl.doSomethingWithInput(jsonObject); 

  return Response.status(Response.Status.ACCEPTED).build(); //Return ACCEPTED and the task URI???
}

GET 处理程序:

@Path("/logicstatus")
@GET
@@Produces(MediaType.APPLICATION_JSON)
public Response logicStatus(URI as query param???) {

  // Magic here?

  retrun Response.status(Response.Status.OK).entity(resultJson).build();
}

非常感谢。 Java 让我想起我是多么喜欢用 C :D 编写设备驱动程序。

所以,要事第一。对于 运行 事物异步 Jakarta EE 有注解 @Asynchronous。因此,使用 @RequestScoped 创建一个 CDI bean,并将您的业务方法放在那里并用 @Asynchronous.

注释

@ApplicationScoped CDI 注释添加到您的 JAXRS 服务,以便您可以注入您的业务 bean。

然后,为了监控任务,您有两种可能性。

  1. 如果您将状态保存在数据库中,您只需使用 @GET 方法来检查数据库中的状态。但我认为这不是你的情况。

  2. 如果第一种方法不适合,那么您必须以某种方式保持不同的 inProgress 任务的状态。我会为此准备另一个 @ApplicationScoped CDI bean。这将包含一个 ConcurrentHashMap,以 UUID 作为键,作为值,您自己的对象包含任何特定异步作业的当前状态。

UUID 必须在 @POST 方法中生成,并作为参数发送到 @Asynchronous 方法。这样 @POST 方法可以 return UUID,然后在 @GET 中使用它来请求任务的状态(查询 @ApplicationScoped bean ).

所以应用程序范围的异步任务状态持有者应该是这样的

@ApplocationScoped
public class AsyncTasksStateHolder {
     private Map<UUID, MyCustomState> tasksState = new ConcurrentHashMap<>();

// here go methods to add and get from the map

}

那么,异步业务逻辑可能是这样的:

@RequestScoped
public class AsyncTaskExecutor {

    @Inject
    AsyncTasksStateHolder asyncTasksStateHolder;

    @Asynchronous
    public void doAsyncStuff(UUID uuid, JsonObject jsonObject) {
         // create the initial state and save it to asyncTasksStateHolder
         // Do your dirty deeds
         // Keep updatinf the state in asyncTasksStateHolder
    }
}

现在是 JAX-RS 部分:

@Inject
AsyncTasksStateHolder asyncTasksStateHolder

@Inject
AsyncTaskExecutor asyncTasksExecutor;

@Path("/logic")
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.TEXT_PLAIN)
public Response logic(JsonObject jsonObject){

  UUID uuid = UUID.randomUUID();
  asyncTasksExecutor.doAsyncStuff(uuid, jsonObiect); // this returns immediately because of the @Asynchronous
  return Response
      .status(Response.Status.OK)
      .entity(uuid.toString())
      .build();
}

@Path("/logicstatus/{uuid}")
@GET
@Produces(MediaType.APPLICATION_JSON)
public Response logicStatus(@PathParam("uuid") String uuidAsString) {
    // And here you just query the asyncTasksStateHolder
   // with the UUID to retreive the state.
}

这里有几点需要注意:

  1. 您将需要一种机制来移除陈旧状态,否则地图将无限增长。

  2. 我使用 PLAIN_TEXT 来实现简单性,您可以随意对 @POST 响应进行建模。

  3. 根据您的 MicroProfile 版本,您需要创建 beans.xml 以便激活 CDI。

我认为仅此而已。有任何问题或疑问,请随时发表评论。