Java 项目反应堆:用单核反应堆的结果来丰富通量的结果

Java 项目反应堆:用单核反应堆的结果来丰富通量的结果,java,reactive-programming,project-reactor,reactive-streams,Java,Reactive Programming,Project Reactor,Reactive Streams,我正在努力向我的公司介绍反应式编程。我正在构建一个简单的活动推荐系统演示,以证明其性能优势 我遇到的一个挑战是用一个流的结果丰富另一个流的结果。我有一个工作示例,如下所示,但我不确定这种方法是否存在任何问题。有人能看一下并提供任何潜在的改进吗 public Flux<Integer> getRecommendedActivities(Long userId, String location, Integer limit) { Flux<ActivityD

我正在努力向我的公司介绍反应式编程。我正在构建一个简单的活动推荐系统演示,以证明其性能优势

我遇到的一个挑战是用一个流的结果丰富另一个流的结果。我有一个工作示例,如下所示,但我不确定这种方法是否存在任何问题。有人能看一下并提供任何潜在的改进吗

    public Flux<Integer> getRecommendedActivities(Long userId, String location, Integer limit) {
        Flux<ActivityData> activities = activityDatabaseService.getByLocation(location);
        Mono<Map<String,BigInteger>> userCategoryScores = userScoresDatabaseService.get(userId);

        return activities
            .zipWith(userCategoryScores.cache().repeat(), this::scoreActivitiesBasedOnUserCategoryScores)
            .sort(compareActivityScoreStrength)
            .map(ScoredActivityData::getActivityId)
            .take(limit);
    }

    private ScoredActivityData scoreActivitiesBasedOnUserCategoryScores(ActivityData deal,Map<String, BigInteger> categoryScores){
        //This method combines the deal score and user category scores to come up with a final score
    }
公共流量getRecommendedActivities(长用户ID、字符串位置、整数限制){ Flux activities=activityDatabaseService.getByLocation(位置); Mono userCategoryScores=userScoresDatabaseService.get(userId); 返回活动 .zipWith(userCategoryScores.cache().repeat(),this::scoreActivitiesBasedOnUserCategoryScores) .排序(比较活动核心强度) .map(ScoredActivityData::getActivityId) .采取(限制); } 私有ScoredActivityData ScoreActivities基于用户分类核心(ActivityData交易、地图分类核心){ //此方法结合交易分数和用户类别分数得出最终分数 } 谢谢,
卡尔

这里的代码没有本质上的错误。一些可能有用也可能无用的文体要点:

  • 反应式编程的“标准”是使用流畅的风格并贯穿始终,而不是在方法顶部声明单独的局部变量,并使用反应链中的变量
  • x.zipWith(y.cache().repeat())
    模式可以很好地工作,但是如果可以避免的话,我发现它有点难看(
    zipWith()
    意味着我头脑中有两个真正的
    数据流,而不是一个被任意缓存和重复的
    单声道
    ,因此这种行为不一定是“突出的”相反,我更喜欢
    y.flatMapMany(x)
    ——它更清楚地表明,您正在获取一个值,并通过实
    通量对多个值应用转换。因此,在您的情况下,这可能看起来像:

    userScoresDatabaseService.get(userId)
            .flatMapMany(c -> activityDatabaseService.getByLocation(location)
                    .map(a -> scoreActivitiesBasedOnUserCategoryScores(a, c))
            )
            .sort() //etc.
    
    scoresDb.get(userId)
            .flatMapMany(c -> activityDb.getByLocation(location, limit, comparator)
                    .map(a -> score(a, c).getActivityId())
            )
    
  • Flux.sort()
    确实应该是“最后的手段”操作,特别是因为您将性能优势作为探索反应式操作的理由。(从数据库服务中读取整个数据,然后对其进行排序,然后只获取第一个
    n
    值会导致效率低下-最好在数据层中对值进行排序和限制。)请记住
    Flux.sort()
    必须等待整个源代码
    通量
    完成,然后才能对值进行排序和返回,并将每个值存储在内存中,因此这样做首先会失去
    通量
    的许多好处。它还使您的反应链更短、更简单,因为它不需要担心排序和限制
  • Nit:有些方法名和变量名看起来很长。如果可能的话,我会缩短它们——我发现这使得在反应链的上下文中阅读起来更容易
通过以上几点,您的整个
getRecommendedActivities()
可以阅读如下内容:

userScoresDatabaseService.get(userId)
        .flatMapMany(c -> activityDatabaseService.getByLocation(location)
                .map(a -> scoreActivitiesBasedOnUserCategoryScores(a, c))
        )
        .sort() //etc.
scoresDb.get(userId)
        .flatMapMany(c -> activityDb.getByLocation(location, limit, comparator)
                .map(a -> score(a, c).getActivityId())
        )

…至少对我来说,它读起来更短更简单。

感谢您提供的详细信息真的很有用!决定遵循这种模式,但要更进一步,在数据层做更多的工作,以简化应用程序逻辑!