【问题标题】:How to handle multiple requests in parallel with SpringBoot's @RestController?SpringBoot的@RestController如何并行处理多个请求?
【发布时间】:2017-06-29 19:26:35
【问题描述】:

我对 Spring Boot 比较陌生,并从他们的 getting started site 中的一个非常简单的示例开始,即(在控制器端):

@RestController
public class HelloController {

    @RequestMapping("/")
    public String index() {
        return "Greetings from Spring Boot!";
    }

}

我现在想要的是,同一控制器的多个(可能长时间运行的)请求可以并行处理。

因为我已经知道 @RestController 将被实例化为单例,所以对我来说很清楚,多个请求(由相同的方法处理)将按顺序处理 . 更新:我的错:我认为这与控制器是单例有关。但确实如此:为什么它不能并行运行呢?

所以我将上面的示例更改如下,以便在每个请求上创建一个新的控制器实例,并通过某种方式检查实际发生的情况:

@RestController
@Scope(value = "request")
public class HelloController {

    private static AtomicInteger count = new AtomicInteger(0);

    public HelloController() {
        count.incrementAndGet();
    }

    @PostConstruct
    public void init() {
        System.out.println("start request " + count);
    }

    @PreDestroy
    public void onDestroy() {
        System.out.println("end request " + count);
    }

    @RequestMapping("/")
    public String index() throws InterruptedException {
        LocalDateTime now = LocalDateTime.now();
        TimeUnit.SECONDS.sleep(15);
        System.out.println(now);
        return "Greetings from Spring Boot! " + now + "  " + count.get();
    }

}

现在我希望看到请求在大约 15 秒内并行处理,但实际上我只能看到它显然是按顺序处理的,并且需要 30 秒(在标准输出上):

start request 1
2017-02-11T14:19:47.429
end request 1
start request 2
2017-02-11T14:20:02.467
end request 2

所以我的问题是:我如何才能实现并行处理此类请求,因为为每个请求创建一个实例显然是不够的?


小评论:我已经尝试将@Asnync 注释与@EnableAsync 结合使用作为应用程序类,但这似乎是“一劳永逸”,因此我无法在客户端显示响应.

stackoverflow 上的几个条目(例如 thisthis)很有趣,但也没有回答我的问题,本教程也没有回答关于 asynchronous methods 的问题。


更新:由于一些人指出问题可能与我测试的方式有关,我尝试使用不同的浏览器运行它。有趣的是,我在 Chrome 和 Firefox 上都遇到了同样的问题。但是当每个请求都执行一个请求时,它显示了预期的行为(并行处理请求)——所以我被浏览器愚弄了......

【问题讨论】:

  • 如何执行并行请求?
  • 你如何测试调用你的 HelloController 端点?通过使用浏览器(打开 2 个选项卡)或单元测试(使用线程)?
  • “因为我已经了解到@RestController 将被实例化为单例,所以我很清楚,多个请求(由相同的方法处理)将按顺序处理。”这种说法是错误的。在 servlet 容器中,每个请求都在不同的线程中处理。除非您的控制器方法是同步的,否则请求将由您的控制器并行处理
  • 此外,由于您的控制器是单例,@PostConstruct@PreDestroy 在容器生命周期内只应调用一次
  • @gregfqt:他修改Controller使用scope="request"

标签: java spring-boot parallel-processing


【解决方案1】:

你写道:

因为我已经了解到@RestController 将被实例化为 单身人士,对我来说很清楚,多个请求(它们是 以相同的方法处理)将按顺序处理。

...将按顺序处理” - 这是错误的陈述。我什至不知道它是根据什么制作的。直到您使方法同步(或将使用其他锁定技术),它才能被多个线程同时访问。

在第二种情况下,您刚刚将其配置为为每个请求创建新实例。我只有一个想法来解释为什么您会在调试器中收到顺序结果:您也可以顺序地从浏览器执行此请求。

如果您想测试即使在第一种情况下如何完美处理乘法请求,只需在浏览器中打开单独的选项卡并几乎同时启动请求。

【讨论】:

  • 感谢您的回答! :) 我在同一个浏览器的两个选项卡中对此进行了测试,并在另一个请求之后立即启动了一个请求(绝对是在 15 秒的时间范围内),所以我认为这些请求是并行完成的,不是吗?!但我认为你指出我的一个假设是错误的是正确的......谢谢!
【解决方案2】:

控制器是单例与是否可以并发访问无关。

你真的不应该使用基于请求的范围,除非你有一些不能跨线程共享的可变字段,但是实现这样的东西可能只是糟糕的设计。

我想您看到这两个请求需要 30 秒的原因是因为您实际上并没有发出并发请求,而是在开始第二个请求之前等待第一个请求完成。

【讨论】:

  • 首先:感谢您的回答! :) 好吧,我在两个单独的浏览器窗口中发出了两个请求,第二个请求在第一个请求之后大约两三秒。而且我希望这两个请求都会在大约 18 秒内得到处理——或者我的假设是错误的?
  • 是的,所以如果您的请求休眠 15 秒,并且在每个浏览器选项卡之间切换需要 3 秒,那么整个过程应该需要 18 秒。
【解决方案3】:

我从 2009 年开始使用 Spring,我知道他们的 Controller 是 Singleton。 但这并不意味着他们会按顺序处理您的请求(等待一个完成,然后执行下一个)。

来自 Spring Boot 网站的示例代码能够处理并行请求。它将创建 HelloController 的单例实例,但 Spring 能够同时(并行)多次调用 index()

@RestController
public class HelloController {

    @RequestMapping("/")
    public String index() {
        return "Greetings from Spring Boot!";
    }

}

【讨论】:

    【解决方案4】:

    实际上,问题不在于您的控制器,tomcat 服务器为您发送的每个请求创建一个线程,问题出在浏览器本身,如果您向同一端点发送 2 个请求,浏览器会暂停第二个请求直到第一个请求一个得到响应“我不知道为什么或如何解决它”,你会发现服务器实际花费的时间是发送请求到获取响应TTFB时间check this screenshot from browser Networking of the second request的第一个字节的时间形式,你可以使用其他浏览器或连接到您网络的其他设备来测试同时发送 2 个请求

    【讨论】:

    • 正如目前所写,您的答案尚不清楚。请edit 添加其他详细信息,以帮助其他人了解这如何解决所提出的问题。你可以找到更多关于如何写好答案的信息in the help center
    猜你喜欢
    • 2023-01-30
    • 1970-01-01
    • 2017-11-20
    • 1970-01-01
    • 1970-01-01
    • 2013-06-12
    • 1970-01-01
    • 2020-01-19
    • 2018-01-19
    相关资源
    最近更新 更多