【问题标题】:Multithreading + RxJava observe on condition多线程 + RxJava 观察条件
【发布时间】:2016-10-25 20:09:36
【问题描述】:

我有一个长时间运行的任务,因此会生成常规文件和列出其他文件的主文件。

调度程序通过 cron 每天重新生成一次此文件。

任务流程使用rx-java实现。

问题是,如果一个请求进入并启动任务,或者任务由调度程序运行,然后在任务正在进行时,另一个请求到来并且不等待任务完成,而是触发另一个执行。

所以问题是如何在任务执行上同步,所以只会执行一次?

这是示例代码:

@Service
public class FileService {
    @Autowired FileRepository fileRepository;
    @Autowired List<Pipeline> pipelines;

    public Observable<File> getMainFile() {
        if (fileRepository.isMainFileExists())
            return Observable.just(fileRepository.getMainFile());
        else
            return generate(() -> fileRepository.getMainFile());
    }

    public Observable<File> getFile(String fileName) {
        if (fileRepository.isMainFileExists())
            return Observable.just(fileRepository.getFile(fileName));
        else
            return generate(() -> fileRepository.getFile(fileName));
    }

    Observable<File> generate(Func0<File> whenGenerated) {
        return Observable.from(pipelines)
                // other business logic goes here
                // after task execution finished just get needed file
                .map(isAllPipelinesSuccessful -> {
                    return whenGenerated.call();
                });
    }

    @Scheduled(cron = "0 0 4 * * ?")
    void scheduleGeneration() {
        generate(() -> fileRepository.getMainFile()).subscribe();
    }
}

它是从控制器调用的,示例代码如下:

@RestController
public class FileController {
    private static final Long TIMEOUT = 1_000 * 60 * 10L; //ten mins
    @Autowired FileService fileService;

    @RequestMapping(value = "/mainfile", produces = "application/xml")
    public DeferredResult<ResponseEntity<InputStreamResource>> getMainFile() {
        DeferredResult<ResponseEntity<InputStreamResource>> deferredResult = new DeferredResult<>(TIMEOUT);
        Observable<File> observableMainFile = fileService.getMainFile();
        observableMainFile
                .map(this::fileToInputStreamResource)
                .map(resource -> ResponseEntity.ok().cacheControl(CacheControl.maxAge(1, TimeUnit.HOURS).cachePublic()).body(resource))
                .subscribe(deferredResult::setResult, ex -> {
                deferredResult.setErrorResult(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null));
                });
        return deferredResult;
    }
    @RequestMapping(value = "/files/{filename:.+}", produces = "application/xml")
    public DeferredResult<ResponseEntity<InputStreamResource>> getFile(@PathVariable("filename") String filename) {
        DeferredResult<ResponseEntity<InputStreamResource>> deferredResult = new DeferredResult<>(TIMEOUT);
        Observable<File> observableFile = fileService.getFile(filename);
        observableFile
                .map(this::fileToInputStreamResource)
                .map(resource -> ResponseEntity.ok().cacheControl(CacheControl.maxAge(1, TimeUnit.HOURS).cachePublic()).body(resource))
                .subscribe(deferredResult::setResult, ex -> {
                    boolean isFileNotFound = FileNotFoundException.class.isInstance(ex.getCause());
                    HttpStatus status = isFileNotFound ? HttpStatus.NOT_FOUND : HttpStatus.INTERNAL_SERVER_ERROR;
                    deferredResult.setErrorResult(ResponseEntity.status(status).body(null));
                });
        return deferredResult;
    }
}

【问题讨论】:

    标签: java spring multithreading rx-java


    【解决方案1】:

    我有类似以下的内容,但我认为有更好的解决方案。我正在使用 RxJava2-RC5。

    1. Answer 缺少检查,该任务已执行。 https://gist.github.com/anonymous/7b4717cea7ddce270a2e39850a3bd2a4

    更新::

    interface FileRepository {
            String getFile();
    
            Boolean isMainFileExists();
    }
    
    private static Scheduler executorService = Schedulers.from(Executors.newFixedThreadPool(1));
    
    @org.junit.Test
    public void schedulerTest123() throws Exception {
            FileRepository fRepo = mock(FileRepository.class);
    
            when(fRepo.getFile()).thenReturn("");
            when(fRepo.isMainFileExists()).thenReturn(false);
    
            Thread t1 = new Thread(() -> {
                getFile(fRepo, executorService).subscribe();
            });
    
            Thread t2 = new Thread(() -> {
                getFile(fRepo, executorService).subscribe();
            });
    
            t1.start();
            t2.start();
    
            Thread.sleep(3_000);
    
            when(fRepo.getFile()).thenReturn("DasFile");
            when(fRepo.isMainFileExists()).thenReturn(true);
    
            Thread t3 = new Thread(() -> {
                getFile(fRepo, executorService).subscribe();
            });
    
            t3.start();
    
            Thread.sleep(5_000);
    }
    
    private Observable<String> getFile(FileRepository fileRepo, Scheduler scheduler) {
            return Observable.defer(() -> {
                try {
                    if (fileRepo.isMainFileExists()) {
                        return Observable.fromCallable(fileRepo::getFile)
                                .subscribeOn(Schedulers.io())
                                .doOnNext(s -> printCurrentThread("Get File from Repo"));
                    } else {
                        return startLongProcess().doOnNext(s -> printCurrentThread("Push long processValue"));
                    }
    
                } catch (Exception ex) {
                    return Observable.error(ex);
                }
            }).subscribeOn(scheduler).doOnSubscribe(disposable -> printCurrentThread("SUB"));
        }
    
    private Observable<String> startLongProcess() {
            return Observable.fromCallable(() -> {
                printCurrentThread("Doing LongProcess");
    
                Thread.sleep(5_000);
    
                return "leFile";
            });
    }
    
    private void printCurrentThread(String additional) {
            System.out.println(additional + "_" + Thread.currentThread());
    }
    

    【讨论】:

    • 如果我错了,请纠正我:通过这种方式,所有对 generate 的调用都将被线程限制,但它们不会被排队,所以当一个完成时,另一个会被调用?
    • 是的,你是对的。我将编辑我的答案。我虽然你会添加你的签入 fromCallable。
    • 代码有点乱,但想法是将订阅排入一个线程并延迟执行,因此当每个从队列中拉出时,会进行额外检查?
    • 另外你能解释一下吗,我在pipelines上使用observeOn(我没有把它放在问题中)如果我使用这样的模型会有任何干扰吗?
    • 如果多个线程订阅 getFile()-observable,在 observable 完成之前,只有一个线程会被允许进入。只有一个因为“subscribeOn”而有一个只有一个线程的线程池。 SubscribeOn 将在给定的调度程序上创建排放。所以一切都将在同一个线程上。 flatMap 将在同一个线程上调用。 ObserveOn 只会通过调度程序将 oNext 事件移动到另一个线程。我不认为我的解决方案很好,因为 getFile() 会阻塞直到 longProcess 完成。
    猜你喜欢
    • 2015-06-04
    • 1970-01-01
    • 2014-11-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多