【问题标题】:Spring 4 @Service with @RequestScopeSpring 4 @Service 与 @RequestScope
【发布时间】:2016-09-26 10:57:56
【问题描述】:

为了优化sql请求,我做了一个聚合其他服务消耗的服务,以避免不必要的调用。 (我的 webapp 的某些页面每天被调用数百万次,所以我想在每个请求上尽可能多次重用数据库查询的结果)

我创建的解决方案是这个:

我的服务有 @RequestScope 而不是默认范围(单例)

在我的服务中

@Service
@RequestScope
public MyService {

private int param;

@Autowired
private OtherService otherService;

@Autowired
private OtherService2 otherService2;

private List<Elements> elements;

private List<OtherElements> otherElements;

public void init(int param) {
    this.param = param;
}

public List<Elements> getElements()  {

    if(this.elements == null) {
        //Init elements
        this.elements = otherService.getElements(param);
    }

    return this.elements;

}

public List<OtherElements> getOtherElements()  {

    if(this.otherElements == null) {
        //Init otherElements
        this.otherElements = otherService2.getOtherElements(param);
    }

    return this.otherElements;

}

public String getMainTextPres() {

    //Need to use lElements;
    List<Elements> elts = this.getElements();

    ....

    return myString;

}

public String getSecondTextPres() {

    //Need to use lElements;
    List<Elements> elts = this.getElements();
    //Also Need to use lElements;
    List<OtherElements> otherElts = this.getOtherElements();        

    ....

    return myString;

}
}

在我的控制器中:

public class myController {
@Autowired MyService myService;

@RequestMapping...
public ModelAndView myFunction(int param) {
myService.init(param);

String mainTextPres = myService.getMainTextPres();

String secondTextPres = myService.getSecondTextPres();

}

@OtherRequestMapping...
public ModelAndView myFunction(int param) {
myService.init(param);

String secondTextPres = myService.getSecondTextPres();

}
}   

当然,我已经简化了我的示例,因为 myService 使用了很多其他元素,并且我保护了他的成员属性的初始化

此方法的优点是仅在需要时才延迟加载属性。 如果在我的项目中的某个地方(在同一个或其他控制器中)我只需要 SecondTextPres,那么调用“getSecondTextPres”将初始化两个列表,这在我的示例中不是这种情况,因为在调用“getMainTextPres”时第一个列表已被初始化。

我的问题是:

  • 您如何看待这种做事方式?
  • 我是否会因为每个请求都实例化我的服务而出现性能问题?

非常感谢!

朱利安

【问题讨论】:

  • 为什么要使用请求范围的 bean 进行优化。每个请求都有自己的实例,没有重用。如果您真的想优化修复您的查询或使用缓存。我建议研究 Spring 可以为您做什么(以及如何缓存结果),而不是自己尝试破解某些东西。
  • 它是优化的,因为一个相同的请求我多次使用相同的低级信息,这种处理方式有助于我重用服务层上已有的数据。我不打算针对 2 个不同的请求对其进行优化
  • 如果您需要在单个请求中进行这样的优化,您可能需要重新考虑您的流程。在一个请求中,只咨询一个控制器,其他一切都是一个新请求。从您的描述来看,您似乎正在尝试优化您甚至没有测量过的东西,或者这确实是一个问题。看起来您正在尝试优化甚至可能不需要优化的东西。请记住:过早的优化是万恶之源。除了引入范围之外,可能还有更好(更简单?)的方法来改进事情。但首先要衡量、衡量、衡量。
  • 接下来,如果数据被大量查询,当使用一个像样的数据库时,这将优化数据的获取或优化查询。
  • 你说得对,我没有办法。事实上,我知道这样做可以减少数据库调用,这就是我想要这样做的原因,我的数据库服务器负载很重,所以如果我可以减少请求的数量,这对我来说是件好事。但我知道我这样做的方式不寻常,这就是我今天提出问题的原因:)

标签: java spring spring-mvc service scope


【解决方案1】:

我认为你的想法不会成功。我调用相同或不同的控制器,这将是不同的请求 - 在这种情况下,将创建新的 bean(元素和其他元素再次为空)。

您是否考虑过缓存? Spring 提供了很好的支持,您可以在其中定义缓存过期等

【讨论】:

  • 感谢您的回答。事实上,我想要/需要有多个实例,因为每个请求都是不同的(取决于参数)
  • 关于缓存:我已经简化了我的示例,因为数据来自不同的来源。我使用的主要数据来自拥有数百万条记录的数据库,所以我不确定是否能够拥有良好的缓存(内存不足)
  • “取决于参数”这可能是一个错误的假设。您只需要一个能够为不同参数提供不同答案且不保留任何内部状态的服务。
【解决方案2】:

我不太清楚你到底想在请求范围内优化什么实例化服务?如果您担心内存占用,您可以通过 JMX 或 VisualVM 轻松测量它。

另一方面,您可以使所有服务调用纯,即仅取决于函数参数和(ofc)数据库状态,并将默认范围的服务实例化为单例。

此决定将为您节省合理数量的资源,因为您不会在每次调用时实例化可能的大型对象图,并且在请求完成后不需要 GC 清理事物。

经验法则是想一想为什么您需要在每次调用时实例化特定的类,如果它不保留任何特定于调用的状态,则将其设为单例。

谈到延迟加载,考虑重复 100 次的最坏情况总是有帮助的。与加载一次和整个容器生命周期相比,它真的会为您节省一些东西吗?

【讨论】:

  • 谢谢。我想我还不够清楚。每天,同一个“模板”页面被加载数百万次,但数据不同。每次加载页面时,我都会使用大约十个输出信息来构造返回给客户端的信息。我做这项服务的目的是避免在数据库(或其他来源)中多次获取相同的数据来构造输出信息。
  • 此外,我还有其他页面(和其他控制器)有时可以使用此服务来获取信息。例如,我可能有另一个只需要打印 secondTextPres 的控制器。
  • @JulienO 我想说您需要将此服务拆分为两个独立的东西:缓存和实际服务。第一个可以在许多不同的级别(DB、HTTP、服务)上提供,如果不非常了解您的应用程序,我无法建议您应该使用哪一个。请深入研究缓存类型并确定最适合您的缓存类型。第二个应该是纯的并作为单例提供。
  • 我会看看缓存的可能性。我必须验证它是否可以应用(我使用其他服务,带有外部数据源)
  • 从昨天开始,我用 Jmeter 做了一些测试:1000 页,每页 10 次。使用 simplecache:平均响应时间:290ms 使用我的方法,没有缓存:平均响应时间:271ms 但主要是标准偏差是使用缓存的 202ms,而我的方法只有 95ms。
猜你喜欢
  • 2016-08-18
  • 2021-04-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-09
  • 1970-01-01
相关资源
最近更新 更多