Spring Boot MVC控制器是多线程的吗? [英] Is Spring Boot MVC controller multithreaded?

查看:341
本文介绍了Spring Boot MVC控制器是多线程的吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是Spring MVC的新手,我在doc中没有找到任何相关信息。
假设我有一个控制器/帐户接受POST请求来创建一个帐户。
两个请求(几乎)同时使用相同的帐户ID。
ASFAIK dispatcherServlet 管理请求。

I'm new to Spring MVC and I didn't find anything about this in doc. Let's say I have a controller /accounts which accepts POST requests to create an account. Two requests comes (almost) in the same time with the same account id. ASFAIK dispatcherServlet manages the requests.

第二个请求是否排入队列,直到第一个请求已完成?
或者两个线程同时处理两个请求?

Is the 2nd request put in queue until the first one has been completed? Or there would be two threads working with two requests simultaneously?

更新:
查看官方的Spring教程:构建REST服务
控制器方法添加

@RequestMapping(method = RequestMethod.POST)
ResponseEntity<?> add(@PathVariable String userId, @RequestBody Bookmark input) {
    this.validateUser(userId);
    return this.accountRepository
            .findByUsername(userId)
            .map(account -> {
                Bookmark result = bookmarkRepository.save(new Bookmark(account,
                        input.uri, input.description));

                HttpHeaders httpHeaders = new HttpHeaders();
                httpHeaders.setLocation(ServletUriComponentsBuilder
                        .fromCurrentRequest().path("/{id}")
                        .buildAndExpand(result.getId()).toUri());
                return new ResponseEntity<>(null, httpHeaders, HttpStatus.CREATED);
            }).get();

}

控制器只是bean,默认情况下它们是单例。
当收到两个同时发出的请求时,两个线程使用相同的控制器实例。让我们假设第一个线程已经保存了新书签并执行

Controllers are just beans, by default they are singleton. When two simultaneous requests are received the same instance of the controller is used by two threads. Let's imagine the 1st thread already saved new bookmark and executing

httpHeaders.setLocation(ServletUriComponentsBuilder
                    .fromCurrentRequest().path("/{id}")
                    .buildAndExpand(result.getId()).toUri());

同时第二个线程刚刚执行

Meanwhile the 2nd thread just executed

Bookmark result = bookmarkRepository.save(new Bookmark(account,
                    input.uri, input.description));

在这种情况下,第一个线程将返回 result.getId())。 toUri()由第二个线程创建。

In this case the first thread will return result.getId()).toUri() which is created by the 2nd thread.

它的工作流程是否正确并且该控制器是否是线程安全的?

Is it correct workflow and is this controller thread-safe?

推荐答案

对于该类型的所有框架,可以安全地假设控制器方法将同时处理(即在多个并发线程中)。此外,以任何其他方式执行此操作都是一个严重的性能损失(是的,有一些框架的设计与全新的完全不同,但我们现在不讨论这些框架)。

With all frameworks of that type, it is safe to assume that the controller methods will be processed simultaneously (i.e. in multiple concurrent threads). Furthermore, it is a serious performance hit to do it in any other way (yes, there are frameworks designed very differently from the ground-up, but we're not talking about those now).

Spring本身无法知道URL的帐户ID部分在某种程度上是特殊的,以便在其上进行同步。如果它将在URL级别上同步,则会将性能降低到爬行状态。您可以自己实现此逻辑,例如,在此控制器方法操作的会话范围对象上进行同步(或使整个控制器会话范围为列夫的答案建议)。然后,这将仅为该用户(该HTTP会话)序列化调用。如果你想在所有用户之间进行同步,你可以拥有全局共享锁,但出于上述原因,这是一个非常糟糕的主意。

Spring itself has no way of knowing that the account id part of the URL is somehow special to synchronize on it. If it would synchronize on URL level, it would bring the performance down to a crawl. You could always implement this logic yourself by, for example, synchronizing on a session-scoped object that this controller method operates on (or make the entire controller session-scoped as Lev's answer suggests). This would then serialize the calls for that user only (that HTTP session). You could have a globally shared lock if you'd want to synchronize between all users, but that's a really bad idea for reasons mentioned above.

相反,设计应用程序的方式是所有控制器都是完全无状态的,并且如果可能,方法要么是幂等的,要么具有正确的前置条件检查逻辑。如果该方法正在写入数据库,则可以实现乐观锁定策略。 Hibernate已经有了这个功能,一些DB /驱动程序也有这个功能,所以请看一下适合你的情况。

Instead, design your application in a way that all controllers are completely stateless and make methods either idempotent if possible or have a correct precondition checking logic. If the method is writing to a DB, you could implement an optimistic-locking strategy. Hibernate already has that feature and some DBs/drivers have it as well, so look into what's applicable to your case.

UPDATE

在提供的示例中,两个线程都保存一个实例(我假设save意味着根据需要插入/更新),返回的对象是当前保存操作的结果,因此没有奇怪的线程间行为。这假设保存是事务性的,具有合理的事务隔离级别(每个其他假设都非常不寻常)。实际上,第二个线程只会覆盖第一个线程所写的值,但这可能就是你所期望的。也就是说,你只是在下一行中读取id,并且大概永远不会改变,所以URL构建似乎不受并发影响。

In the provided example, both threads are saving an instance (I'm assuming save means insert/update as needed) and the returned object is the result of the current save operation, thus no strange inter-thread behavior. This is assuming the save is transactional with a reasonable transaction isolation level (every other assumption would be highly unusual). As it is, the second thread would simply overwrite the values written by the first, but that is probably what you'd expect. That said, you're only reading the id in the following line, and that presumably never changes, so the URL building seems unaffected by concurrency anyway.

这篇关于Spring Boot MVC控制器是多线程的吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆