Log inside Mono.error 输出两次

Log inside Mono.error gets outputted twice

由于某些奇怪的原因,“未找到用户”日志在我的应用程序日志中输出两次,尽管调用了 findUserById只有一次。 我不确定是什么导致了这个问题。

有没有更好的方法来解决这个问题(记录并抛出异常)?

请注意 findById 调用来自 API。

编辑: 似乎只抛出一次异常。此外,如果我将 Mono.error 替换为 Mono.defer,日志也会打印两次。

public Mono<User> getUser(String id) {
    
    Mono<User> thisIsEmpty = getNoUser(); // Assume that this is empty

    return Mono.defer(() -> thisIsEmpty.switchIfEmpty(Mono.defer(() -> findUserById(id))));
}


public Mono<User> findUserById(String id) {
        log.info("This is printed once.");
        Mono<User> user = repository.findById(id).switchIfEmpty(Mono.error(() -> { // findById is an API call of a library I use
            log.error("User not found (this is printed twice)"); // Gets printed twice
            throw new UserException(MY_ERROR_CODE, 401);
        }));

        user.subscribe(User -> ... // Do something if it is not empty

        return user;
}

嗯,来自Mono#error docs

Create a Mono that terminates with an error immediately after being subscribed to. The Throwable is generated by a Supplier, invoked each time there is a subscription and allowing for lazy instantiation.

首先您的订阅在这里:

user.subscribe(User -> ...

而且我假设您有另一个使用 getUser 的代码订阅。

这就是它打印两次的原因。

正如@Eugene 提到的,您的问题是您可能订阅了两次。

如果你想调用它一次,那么你的方法应该被重写

public Mono<User> findUserById(String id) {
    log.info("This is printed once.");
    return repository.findById(id)
            .switchIfEmpty(Mono.error(() -> { // findById is an API call of a library I use
                log.error("User not found (this is printed twice)"); // Gets printed twice
                throw new UserException(MY_ERROR_CODE, 401);
            }))
            .flatMap(user-> {
                // do smth here 
                return Mono.just(user);
            });


    return user;
}

Reactive 有点不同,所以当你 return mono 时,它不会被执行,直到你订阅它(或者阻止你不应该做,直到你必须)。

所以基本上你第一次“执行”时你的情况发生了什么

user.subscribe(User -> ... // Do something if it is not empty

第二次可能是你有控制器,它是 returning mono 并且 spring boot 正在订阅

此外,您在服务订阅中所做的任何事情都不会发送到它之外。您应该为此使用 flatMap 或任何其他 Reactive 运算符。