如何将这段代码重构为更具反应性的代码

时间:2018-04-02 22:25:16

标签: spring spring-cloud spring-cloud-stream project-reactor

我们正在使用Spring Cloud Stream构建微服务。

我们正在运行这段代码,它基本上订阅来自任务频道的消息,并使用特定的类型标头,执行作业然后发布新消息到活动频道:

@EnableBinding({InboundChannels.class, OutboundChannels.class})
public class TasksProcessor {

private final UserService userService;
private final MessageChannel eventsChannel;
private final Logger logger;

public TasksProcessor(
        UserService userService,
        @Qualifier(OutboundChannels.EVENTS) MessageChannel eventsChannel,
        Logger logger
) {
    this.userService = userService;
    this.eventsChannel = eventsChannel;
    this.logger = logger;
}

    @StreamListener(value = TASKS, condition = "headers['type']=='" + CREATE_USER + "'")
    public void createUser(User user)  {
        userService.save(user)
            .subscribe(created -> {
                Message<User> successMessage = Events.create(Events.USER_CREATED, created).build();
                eventsChannel.send(successMessage);
            });
    }
}

我想知道是否有更好,更具反应性的方法来利用spring-cloud-stream-reactive API实现它。

尝试之后:

@StreamListener(value = TASKS, condition = "headers['type']=='" + CREATE_USER + "'")
public @Output(EVENTS) Flux<Message<User>> createUser(Flux<User> users)  {
       return users.flatMap(userService::save)
            .map(e -> Events.create(Events.USER_CREATED, created).build());
}

我在应用程序启动时收到此错误:

Caused by: java.lang.IllegalArgumentException: Cannot set a condition for methods that return a value
at org.springframework.util.Assert.isTrue(Assert.java:116)
at org.springframework.cloud.stream.binding.StreamListenerMethodUtils.validateStreamListenerMethod(StreamListenerMethodUtils.java:86)
at org.springframework.cloud.stream.binding.StreamListenerAnnotationBeanPostProcessor$DefaultStreamListenerSetupMethodOrchestrator.orchestrateStreamListenerSetupMethod(StreamListenerAnnotationBeanPostProcessor.java:352)
at org.springframework.cloud.stream.binding.StreamListenerAnnotationBeanPostProcessor.doPostProcess(StreamListenerAnnotationBeanPostProcessor.java:195)
at org.springframework.cloud.stream.binding.StreamListenerAnnotationBeanPostProcessor.lambda$postProcessAfterInitialization$0(StreamListenerAnnotationBeanPostProcessor.java:167)
at java.lang.Iterable.forEach(Iterable.java:75)
at org.springframework.cloud.stream.binding.StreamListenerAnnotationBeanPostProcessor.injectAndPostProcessDependencies(StreamListenerAnnotationBeanPostProcessor.java:285)
at org.springframework.cloud.stream.binding.StreamListenerAnnotationBeanPostProcessor.afterSingletonsInstantiated(StreamListenerAnnotationBeanPostProcessor.java:105)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:777)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:868)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:549)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:388)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:327)
at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:138)
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:99)
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:117)
... 49 more

所以我尝试了一种更通用的方法:

@StreamListener(value = TASKS)
public @Output(EVENTS) Flux<Message<?>> reducer(Flux<Message<?>> messages)  {
    return messages
            .filter(message -> message.getHeaders().containsKey(TYPE_HEADER))
            .flatMap(message -> {
                final String type = (String) message.getHeaders().get(TYPE_HEADER);

                switch (type) {

                    case CREATE_USER:
                        final User toCreate = (User) message.getPayload();
                        return userService.save(toCreate)
                                .map(created -> Events.create(USER_CREATED, created).build());


                    default:
                        return null;
                }

            });
}

但由于message.getPayload()的类型为byte[]而且我没有找到如何解析它,因此无法正常工作。

1 个答案:

答案 0 :(得分:0)

我认为这应该适合你:

def iri_to_uri(iri):
    p = urllib.parse.urlparse(iri)
    netloc = p.netloc.encode('idna').decode('ascii')
    path = urllib.parse.quote_from_bytes(p.path.encode('utf-8'))
    p = [:1] + (netloc, path) + p[3:]
    return urllib.parse.urlunparse(p2)

至于@StreamListener(value = TASKS, condition = "headers['type']=='" + CREATE_USER + "'") public @Output(EVENTS) Flux<Message<User>> createUser(Flux<User> users) { return users.flatMap(userService::save) .map(e -> Events.create(Events.USER_CREATED, created).build()); } 返回UserService.save(User)

请参阅Reference Manual中的示例。另请注意Reactive Sample

通过框架的反应性支持的要点是,您不应该订阅到该框架为您提供的Mono<User>