【问题标题】:Jersey Rest API returns immediate and the long task continuesJersey Rest API 立即返回,漫长的任务继续
【发布时间】:2018-10-08 18:57:51
【问题描述】:

我的项目中有一个要求,用户调用 REST API - POST 操作来执行长时间等待的任务,现在我们不想让用户等待,而是希望立即通知用户说“任务已接受”,并在后台触发另一个线程来执行长任务。

我已经尝试了以下代码,但是当 API 返回其他线程时,不会记录任何消息或执行任何任务。我在这里错过了什么。

@POST
@Path("/longtask")
@Consumes(MediaType.APPLICATION_JSON)
public Response longTask() {
    LOGGER.info("longTask started by Thread: "+Thread.currentThread().getName());
    executor.submit(
            new Runnable() {
                @Override
                public void run() {
                    new AsyncService().longTask();
                }
            });       
    LOGGER.info("longTask completed by Thread: "+Thread.currentThread().getName());
    return Response.ok(new MyResponse<>("Task Accepted")).build();
}

异步服务

public class AsyncService {

    /**
     *
     */
    public void longTask(){
        LOGGER.info("AsyncService - longTask() Started: "+Thread.currentThread().getName());
        try{
            //Tried thenAcceptAsync as well
            CompletableFuture.supplyAsync(new MyAsyncSupplier()).thenAccept(new MyAsyncConsumer());
        }finally{
            LOGGER.info("AsyncService - longTask() Completed: "+Thread.currentThread().getName());
        }

    }   

}

MyAsyncSupplier() 和 MyAsyncConsumer() 是一些调用数据库并执行长时间等待任务的任务。 MyAsyncSupplier() 和 MyAsyncConsumer() 下的记录器不会打印。我仅使用示例 java 项目和 main() 方法尝试了相同的操作,并且发生了同样的情况。主要完成 System.out 的那一刻不会打印。

[744832b5-deed-4043-11-111] [myproject] 2018-10-08 18:19:16,836 [http-nio-8080-exec-31] INFO  mypackage.MyResource - longTask started by Thread: http-nio-8080-exec-31
[744832b5-deed-4043-11-111] [myproject] 2018-10-08 18:19:16,836 [http-nio-8080-exec-31] INFO  mypackage.MyResource - longTask completed by Thread: http-nio-8080-exec-31
[] [] 2018-10-08 18:19:16,837 [pool-64-thread-1] INFO  mypackage.AsyncService - AsyncService - longTask() Started: pool-64-thread-1
[744832b5-deed-4043-11-11] [myproject] 2018-10-08 18:19:16,844 [http-nio-8080-exec-31] DEBUG mypackage.InitializationResponseFilter - Media Type set to : application/json 

我怎样才能让这个任务继续?

CompletableFuture.supplyAsync(new MyAsyncSupplier()).thenAccept(new MyAsyncConsumer());

更新 1:

由于 MyAsyncSupplier 和 MyAsyncConsumer 类存在一些对象分配问题,上述代码无法正常工作。我进一步调试了代​​码并修复了它的工作原理。

更新 2:

实际上不需要执行者提交。

@POST
@Path("/longtask")
@Consumes(MediaType.APPLICATION_JSON)
public Response longTask() {
    LOGGER.info("longTask started by Thread: "+Thread.currentThread().getName());
    new AsyncService().longTask();
    LOGGER.info("longTask completed by Thread: "+Thread.currentThread().getName());
    return Response.ok(new MyResponse<>("Task Accepted")).build();
}

【问题讨论】:

  • 我有点困惑。您说 /longtask 返回后没有记录任何内容,但在您的日志中,您清楚地看到 AsyncService 在打印完整消息后启动。您还记得在 main 中放置一个等待或循环以使其保持活动状态。一旦 main 完成,JVM 就会被杀死。这就是为什么您的简单 java 项目不起作用的原因。
  • 记录器在打印的 CompleableFuture 之前。 MySupplier 和 MyConsumer 的一次未在数据库中记录和执行。如果我添加一个等待,那不会让主线程等待吗?

标签: java multithreading jakarta-ee jersey java-threads


【解决方案1】:

我认为您发布的代码很好并且可以按预期工作。也许您对 MyAsyncSupplier 和 MyAsyncSupplier 的实现是错误的。这是我的实现,试一试。

public class FunFun {
    ExecutorService executor = Executors.newFixedThreadPool(2);

    public static void main(String[] args) {
        FunFun f = new FunFun();
        f.longTask();
        f.shutdown();
//        Scanner scanner = new Scanner(System.in); changed to use shutdown.
//        scanner.nextLine();
//        System.exit(0);
    }

    public void shutdown() {
        executor.shutdown();
    }

    public String longTask() {
        System.out.println("longTask started by Thread: "+Thread.currentThread().getName());
        executor.submit(
                new Runnable() {
                    @Override
                    public void run() {
                        new AsyncService().longTask();
                    }
                });
        System.out.println("longTask completed by Thread: "+Thread.currentThread().getName());
        return "Task Accepted";
    }

    public class AsyncService {
        public void longTask(){
            System.out.println("AsyncService - longTask() Started: "+Thread.currentThread().getName());
            try{
                //Tried thenAcceptAsync as well
                CompletableFuture.supplyAsync(new MyAsyncSupplier()).   thenAcceptAsync(new MyAsyncConsumer());
            }finally{
                System.out.println("AsyncService - longTask() Completed: "+Thread.currentThread().getName());
            }
        }
    }

    public class MyAsyncSupplier implements Supplier<String>{

        @Override
        public String get() {
            System.out.println("Supplying Food");
            return "Food";
        }

    }

    public class MyAsyncConsumer implements Consumer<String>{

        @Override
        public void accept(String t) {
            System.out.println("Nom Nom " + t);
        }
    }
}

输出:

longTask started by Thread: main
longTask completed by Thread: main
AsyncService - longTask() Started: pool-1-thread-1
Supplying Food
Nom Nom Food
AsyncService - longTask() Completed: pool-1-thread-1

【讨论】:

  • 如果你投了反对票,至少告诉我为什么这样我可以从中学习。
  • 查尔斯,我不确定谁/为什么投反对票。但 IMO 我觉得不需要“扫描仪”和“System.exit(0)”代码。
  • 在这种情况下很好,因为您有一个执行程序,如果您没有 System.exit(0); 它将保持程序运行;更好的解决方案是关闭执行程序。还注意到 thenAccept 没有使用 thenAcceptAsync。你是对的,longTask 不需要执行器,但是你可以在 supplyAsync 和 AccpetAsync 调用上使用它,它会给你更多的控制权。此外,如果您觉得该帖子有帮助,您可以投票甚至接受答案。没有压力,但我喜欢代表:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-10-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-23
相关资源
最近更新 更多