V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
qm332207252
V2EX  ›  Java

异步任务问题

  •  
  •   qm332207252 · 2021-01-20 17:42:02 +08:00 · 2008 次点击
    这是一个创建于 1163 天前的主题,其中的信息可能已经有所发展或是发生改变。

    一个接口,接收到一次请求,调用 service 执行异步任务,异步任务执行中,直接返回响应,再次请求该接口或者其他用户请求该接口(更或者是自动任务执行),调用 service,上次请求这个任务还在执行中,因此返回处理中,这种 java 后台有比较好的实现吗?

    15 条回复    2021-01-21 11:44:00 +08:00
    dqzcwxb
        1
    dqzcwxb  
       2021-01-20 17:44:51 +08:00
    加个锁
    stonewu
        2
    stonewu  
       2021-01-20 17:46:12 +08:00
    最简单的方式就是定义一个全局变量
    qm332207252
        3
    qm332207252  
    OP
       2021-01-20 17:46:46 +08:00
    @dqzcwxb 能详细点说明下不
    zoharSoul
        4
    zoharSoul  
       2021-01-20 17:46:56 +08:00
    记到任务表.
    qm332207252
        5
    qm332207252  
    OP
       2021-01-20 17:47:45 +08:00
    @stonewu 我也想过在调用异步方法外面用个什么变量标记执行状态
    MicroGalaxy
        6
    MicroGalaxy  
       2021-01-20 17:49:27 +08:00
    这种需求还是全局变量最简单好用
    qm332207252
        7
    qm332207252  
    OP
       2021-01-20 17:58:31 +08:00
    @MicroGalaxy 类似前端做登录按钮,请求表格数据什么的异步请求加状态标记那样咯?
    killergun
        8
    killergun  
       2021-01-20 18:08:48 +08:00
    Ticket ?根据 Ticket 来获取进展
    guoyuchuan
        9
    guoyuchuan  
       2021-01-20 18:08:50 +08:00
    异步任务可以配置线程池,这样就不会存在你说的这个问题了
    guoyuchuan
        10
    guoyuchuan  
       2021-01-20 18:09:22 +08:00
    @guoyuchuan #9 不对,说错了
    liian2019
        11
    liian2019  
       2021-01-20 18:34:58 +08:00
    单机就整个 AtomicBoolean,分布式就整个 redis 记一下
    RedBeanIce
        12
    RedBeanIce  
       2021-01-20 19:28:18 +08:00
    java 异步任务有很多方法,CompletableFuture.runAsync

    至于第二次进来的问题,加个锁就行,楼上有说明,单机和分布式
    oneisall8955
        13
    oneisall8955  
       2021-01-20 19:36:08 +08:00 via Android
    单机还是集群,单机配只静态变量,集群搞 redis,加个 key 标识
    siweipancc
        14
    siweipancc  
       2021-01-21 11:37:24 +08:00 via iPhone
    占坑#
    siweipancc
        15
    siweipancc  
       2021-01-21 11:44:00 +08:00
    @Log
    @RestController
    @SpringBootApplication
    public class WebApplication {

    public static void main(String[] args) {
    SpringApplication.run(WebApplication.class, args);
    }

    static final String CREATED = "create";
    static final String PROCESS = "process";
    static final String COMPLETE = "complete";


    @PostMapping("generate")
    public ResponseEntity<Response> generate(@RequestBody Request request) {
    return ResponseEntity.ok(generate(request.input, request.taskId));
    }


    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;
    @Autowired
    private ThreadPoolTaskExecutor executor;

    @PostConstruct
    public void init() {
    redisTemplate.setKeySerializer(RedisSerializer.string());
    redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
    }

    Function<UUID, String> valKeyFunc = uuid -> "value::" + uuid;
    Duration expire = Duration.ofMinutes(30L);

    public Response generate(Integer input, UUID taskId) {

    if (taskId == null) {
    check(input != null, "请输入值");
    UUID uuid = createTask(input);
    return new Response(CREATED, null, uuid, expire.getSeconds());
    }

    String key = valKeyFunc.apply(taskId);
    Long expire = redisTemplate.getExpire(key);
    Entity value;
    try {
    value = (Entity) redisTemplate.opsForValue().get(key);
    } catch (ClassCastException e) {
    log.warning(e.getMessage());
    e.printStackTrace();
    throw new RuntimeException("服务器内部错误");
    }
    check(value != null, "任务 " + taskId + " 不存在或者已经过期");
    assert value != null;
    check(value.complete != null, "服务器内部错误");
    if (!value.complete) {
    return new Response(PROCESS, null, taskId, expire);
    }

    return new Response(COMPLETE, value, taskId, expire);
    }


    private static void check(Boolean condition, String msg) {
    if (!Boolean.TRUE.equals(condition)) {
    throw new RuntimeException(msg);
    }
    }


    private UUID createTask(Integer input) {
    UUID uuid = UUID.randomUUID();
    executor.execute(() -> {
    try {
    Entity entity = new Entity(uuid, false, new Date(), null, input, null);
    log.info(String.format("begin task: %s, input: %s", uuid, input));
    String key = valKeyFunc.apply(uuid);
    redisTemplate.opsForValue().set(key, entity, expire);
    log.info(String.format("set task: %s, input: %s, key: %s", uuid, input, key));
    TimeUnit.SECONDS.sleep(RandomUtil.randomInt(10, 20));
    String value = RandomUtil.randomString(Math.abs(input));
    entity.complete = true;
    entity.completeTime = new Date();
    entity.output = value;
    log.info(String.format("end task: %s, input: %s, generated: %s", uuid, input, value));
    redisTemplate.opsForValue().set(key, entity, expire);
    } catch (InterruptedException e) {
    log.warning(e.getMessage());
    e.printStackTrace();
    }
    });
    return uuid;
    }

    @Data
    static class Request {
    Integer input;
    UUID taskId;
    }

    @AllArgsConstructor
    @NoArgsConstructor
    @Data
    static class Response {
    String status;
    Object data;
    UUID taskId;
    Long expire;
    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    static class Entity {
    UUID taskId;
    Boolean complete;
    Date beginTime;
    Date completeTime;

    Integer input;
    String output;
    }

    }
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   3204 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 12:17 · PVG 20:17 · LAX 05:17 · JFK 08:17
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.