复杂的Spring框架服务层

时间:2014-02-27 19:59:43

标签: java multithreading spring spring-mvc architecture

邮寄的短篇版本

没有足够的观点,所以我总结了这个问题:

我的架构是完全无状态和异步的,前端向REST API发出请求,然后长时间轮询响应。此Rest API将请求排入队列,每个请求都由后端出列并处理。

我希望这个后端遵循“传统的”Spring @Service接口和ServiceImpl方法,但是由于我是如何做的而有点难。

一个线程使请愿(生成器)出列,生成一个新的线程(消费者),然后它处理该线程中的所有请求,该线程随后发送回“响应池”进行轮询。该请愿可能需要使用几个@Service并合并每个的响应,甚至可能是相同的@Service两次。

你会怎么做?有关更多信息,请查看以下说明!

原始长期发布:

我有一个大型应用程序,包含3层:

  1. 前端(Spring-MVC):视图和控制器,“模型”是对中间件中REST API的异步请求,首先将请求排队,然后长时间轮询答案
  2. 中间件(Spring-MVC):其余的API。两个主要功能:从前端接收请求并将其排队,从后端接收答案并将其存储在响应缓存中,直到由前端检索
  3. 后端(Spring Standalone App):制作者/消费者模式,ONE Producer将请愿书排队并为每个请愿书创建一个Prototype Consumer。消费者实现InitializingBean ,所以它是这样的:它被初始化,许多自动装配字段被初始化然后 afterPropertiesSet 被执行并且许多字段这取决于请愿书。
  4. 我还有一个HibernateDaos的Repository Layer,它可以对数据库进行所有查询。

    我错过了正确构建的服务层,这就是这个问题的全部内容。

    让我更多地介绍一下背景。我现在拥有的只是一个只有一个巨大的服务,有221个函数(消费者的文件非常长),一个请愿书可能需要调用其中的几个函数,并且每个函数的结果都合并到一个DTO列表中,这是后来收到了前端。

    我想将这个和唯一的服务拆分成几个,与“it”的相应存储库进行逻辑匹配,但是我遇到了以下问题:

    记住这一点:

    • 一个请愿书有很多动作,一个动作是对服务功能的调用。
    • 每个消费者都是一个独特的线程。
    • 每次消费者线程启动时,都会启动一个事务,并在返回之前提交,除非回滚。
    • 我需要在同一个帖子和交易中执行该请愿书的所有服务。
    • 当消费者运行 afterPropertiesSet 时,特定于该请求的多个字段将由一些始终发送的参数初始化。

    有一个好的服务层我想要完成:

    • 我不想总是为请愿的每个服务初始化所有这些参数,我希望它们对请愿/线程是全局的,但是,我不想将其作为参数传递给所有221个功能。
    • 我想懒洋洋地初始化新服务,只在需要时,并且在初始化时,我想设置上面提到的所有参数。每个服务都需要成为请愿书的原型,但我觉得如果需要在同一请愿书中对其进行两次初始化(在一个请愿书中对同一服务进行2次操作),即我希望它表现得像“请求”范围但是,它不是一个请求,因为它不是基于Web的,它是一个新的线程,由生产者在请求出发时初始化。

    我想在每个Consumer中有一个原型ServicesFactory,它使用Consumer中的所有参数afterPropetiesSet进行初始化,在此ServicesFactory中,所有可能的Services都被声明为Class字段,并且当请求特定服务时,如果它的字段为null它被初始化并且所有字段都被设置,如果不是null,则返回相同的实例。这种方法的问题,我在所有服务上都失去了依赖注入。我一直在阅读关于ServiceFactoryBean的想法,也许这是要走的路,但我真的无法控制它。它需要Consumer的所有参数,并且每个Consumer需要一个唯一的ServiceFactoryBean这一事实真的令人困惑。

    有什么想法吗?

    由于

2 个答案:

答案 0 :(得分:1)

根据描述,我不认为这是使用原型范围的好例子,在这种情况下,理想范围似乎是thread scope

作为一种解决方案,最简单的方法是制作所有服务singleton。然后,消费者从入站队列中读取请求并开始处理。

其中一项服务singleton也会被注入所有需要的服务,我们称之为PetitionScopedService

此服务在内部使用ThreadLocal,它是PetitionContext类型变量的线程范围持有者。 PetitionContext依次包含该请愿的全局信息。

所有消费者需要做的是设置请愿上下文的初始值,并且同一线程上的任何PetitionScopedService调用者都能够以透明的方式读取这些值。以下是一些示例代码:

public class PetitionContext {
    ... just a POJO, getters and setters etc. 
}

@Service
public class PetitionScopedService {

    private ThreadLocal<PetitionContext> = new ThreadLocal<PetitionContext>();


    public doSomethingPetitionSpecific() {
        ... uses the petition context ...
    }
}

@Service
public class SomeOtherService {

    @Autowired
    private PetitionScopedService petitionService;

    ... use petition service that is a singleton with thread scoped internal state, effectivelly thread scoped ...
} 

答案 1 :(得分:0)

第2点和第3点需要更多的重组,更喜欢检查“Spring Integration”的“中间件”和“(Spring Standalone App):生产者/消费者模式”实际上是弹簧集成,以解决这两点,并使用发布/订阅如果您同时执行2个或更多操作,那么您在“中间件”中使用REST的另一个原因是这些“中间件”服务是由另一个应用程序而不是您的前端公开的,在这种情况下您可以将此部分集成到您的Spring-MVC前端应用程序使用“内容协商”,否则如果您打算使用“Spring Integration”,您将找到多种通信方式。