如何在Reactor中使用Context与flatMap()?

时间:2019-02-06 22:00:31

标签: java project-reactor

我在理解上下文时遇到问题。因此文档说 Context 是:

  

在诸如以下组件之间传播的键/值存储   操作员通过上下文协议。上下文是传输的理想选择   正交信息,例如跟踪或安全令牌。

太好了。

现在让我们假设我们想使用 Context 传播某些东西,以使其遍地开花。要调用另一个异步代码,我们只需使用 flatMap()方法。

问题如何在调用的方法内部访问上下文?

示例(简单)代码:

public class TestFlatMap {
    public static void main(final String ...args) {
        final Flux<String> greetings = Flux.just("Hubert", "Sharon")
            .flatMap(TestFlatMap::nameToGreeting)
            .subscriberContext(context ->
                Context.of("greetingWord", "Hello")  // context initialized
            );
        greetings.subscribe(System.out::println);
    }

    private static Mono<String> nameToGreeting(final String name) {
        return Mono.just("Hello " + name + " !!!");  // ALERT: we don't have Context here
    }
}

被调用的方法可以(并且很可能是)在另一个类中。

谢谢您的帮助!

编辑:删除了一些代码,以使问题更简洁明了。

2 个答案:

答案 0 :(得分:2)

束缚您的Publisher,也许Context与您同在

在这种情况下,您连接了所有Publisher(并且包括flatMap / concatMap内的连接以及类似的运算符),Context将会正确传播整个流运行时。

要使用Context方法访问nameToGreeting,如果方法似乎无关,则可以调用Mono.subscribeContext并检索存储的信息事件。 下面显示了所提到的概念:

public class TestFlatMap {
    public static void main(final String ...args) {
        final Flux<String> greetings = Flux.just("Hubert", "Sharon")
                                           .flatMap(TestFlatMap::nameToGreeting)
                                           .subscriberContext(context ->
                                                   Context.of("greetingWord", "Hello")  // context initialized
                                           );
        greetings.subscribe(System.out::println);
    }

    private static Mono<String> nameToGreeting(final String name) {
        return Mono.subscriberContext()
                   .filter(c -> c.hasKey("greetingWord"))
                   .map(c -> c.get("greetingWord"))
                   .flatMap(greetingWord -> Mono.just(greetingWord + " " + name + " " + "!!!"));// ALERT: we have Context here !!!
    }
}

此外,您可以使用zip运算符通过以下方式进行相同操作,以便以后合并结果:

public class TestFlatMap {
    public static void main(final String ...args) {
        final Flux<String> greetings = Flux.just("Hubert", "Sharon")
                                           .flatMap(TestFlatMap::nameToGreeting)
                                           .subscriberContext(context ->
                                                   Context.of("greetingWord", "Hello")  // context initialized
                                           );
        greetings.subscribe(System.out::println);
    }

    private static Mono<String> nameToGreeting(final String name) {
        return Mono.zip(
            Mono.subscriberContext()
                .filter(c -> c.hasKey("greetingWord"))
                .map(c -> c.get("greetingWord")), // ALERT: we have Context here !!!
            Mono.just(name),
            (greetingWord, receivedName) -> greetingWord + " " + receivedName + " " + "!!!"
        );
    }
}

那么,为什么它起作用?

从上面的示例中可以看到,nameToGreeting在主Flux的上下文中被调用。在后台-> (Here Some FluxFlatMap internals)下,每个映射的PublisherFlatMapInner订阅。如果我们查看FlatMapInner并查找the currentContext override,我们会看到FlatMapInner使用父级Context,这意味着父级是否有Reactor Context-那么此上下文将传播到每个内部Publisher

因此,由nameToGreeting方法Mono返回的Context将与其父对象相同

答案 1 :(得分:0)

Reactor-Core v3.4 引入了 Mono.deferContextualFlux.deferContextual,它们取代了 v3.3 中引入的 Mono.deferWithContextFlux.deferWithContext

使用这些方法,Oleh Dokukas zip example 可以简化为

public class TestFlatMap {
    public static void main(final String ...args) {
        final Flux<String> greetings = Flux.just("Hubert", "Sharon")
                .flatMap(TestFlatMap::nameToGreeting)
                .subscriberContext(context ->
                        Context.of("greetingWord", "Hello"));  // context initialized
        greetings.subscribe(System.out::println);
    }

    private static Mono<String> nameToGreeting(final String name) {
        return Mono.deferContextual(c -> Mono.just(name)
                .filter(x -> c.hasKey("greetingWord"))
                .map(n -> c.get("greetingWord") + " " + n + " " + "!!!"));
    }
}