Spring Boot的并发编程

使用Spring Boot构建服务时,我们必须处理并发!通常有这样的误解:由于使用Servlet,而Servlet背后是一个线程池支持,线程池会为每个请求分配的新线程,请求之间不存在冲突,因此无需考虑并发性,本文提供一些关于处理Spring Boot中多线程处理的实用建议。

Spring Boot并发基础知识
在考虑Spring Boot应用程序中的并发性时,值得考虑的关键因素有:

最大线程数 - 这是为处理对应用程序的请求而分配的最大线程数
共享外部资源 - 调用外部共享资源(如数据库)
异步方法调用 - 这些是在等待响应时将线程释放回线程池的方法调用
共享内部资源 - 调用内部共享资源 - 例如高速缓存和可能共享的应用程序状态

我们会逐个检查,看看这些条件是如何影响我们使用Spring Boot编写应用程序的方式。

Spring Boot Application中的最大线程数
首先要注意的是,你正在处理有限数量的线程。

如果你使用Tomcat作为嵌入式服务器(默认),则可以使用属性server.tomcat.max-threads 来控制要允许的线程数。0为默认设置 - 表示使用Tomcat时默认值为200。

了解这一点很重要,因为你可能需要增加此数字以有效地使用提供服务的资源。特别是处理外部资源时也会出现问题......

共享外部资源的问题
调用数据库和其他REST端点可能需要很长时间。

你正在处理的有限数量的线程意味着:你确实需要避免长时间运行,包括缓慢的同步请求,因为运行缓慢占据线程时间长,有限数量的线程会很快用光。如果你必须等待一些缓慢的过程来完成请求,那么意味着你可能无法充分利用服务器潜力。

如果你有许多等待响应的长时间运行的线程,你可能最终会遇到这样一种情况:真正快速、简单的请求反而需要等待很长时间,这些请求处于“永远等待”,当你发现它们时很奇怪,因为它们的运行时间并不长,怎么会被堵塞呢?

如何改进?

异步方法调用
一次请求做多件事通常会有所帮助,理想情况下,如果需要调用三种服务:服务A,服务B和服务C; 你不要这样做:

1.调用服务A.
2.等待服务A的响应
3.调用服务B
4.等待服务B的响应
5.调用服务C.
6.等待服务C的响应
7.组合A,B和C的响应并完成处理

如果每个服务调用需要3秒钟来响应,整个过程将需要9秒。按照下面步骤来做要好得多:

1.调用服务A.
2.调用服务B
3.调用服务C.
4.等待服务A,B和C的响应
5.组合A,B和C的响应并完成处理

在这种情况下,你可以在不等待请求全部完成的情况下进行三次调用,并假设服务A,B和C不相互依赖,三次调用并行进行,这样需要3秒钟就能响应。

异步和响应式微服务的想法本身就很有趣。建议使用Spring Boot2的WebFlux。

在Spring Boot中进行异步调用
如何在Spring Boot中启用异步方法调用?在应用入口类上使用@EnableAsync 注释,与@SpringBootApplication 注释一起使用。

启用后,可以在服务中使用@Async,这个服务返回CompletableFuture<>。因为之前使用了@EnableAsync ,所以这些@Async 方法将在后台线程池中运行。

充分利用异步执行,避免许多不必要的性能下降,使服务尽可能快速和响应。


共享内部资源
虽然前面章节提出的办法我们通常无法用来控制外部资源,但我们可以完全控制系统的内部资源。

意识到我们可以控制内部资源,避免发生与共享相关问题的最佳建议是不要共享它们!

Spring服务和控制器默认为单例!

重要的是要意识到这一点并且要非常小心,在你的服务中存在可变状态的那一刻(banq注:只能把状态放在外部资源如数据库,在内部放置可变状态要小心,包括内部放置一个JDK的线程池,JDK的线程池也是一种可变状态,有启动 销毁等状态生命周期),你需要像在任何标准应用程序中那样处理它。

使用共享状态的其他潜在案例是高速缓存和自定义的服务器范围组件(通常是监视、安全性等方面)。

如果你绝对需要分享一些状态,下面是建议:

1. 处理不可变对象。
如果对象是不可变的,则可以避免许多与并发相关的问题。如果你需要改变一些东西 - 只需创建一个新对象。

2.了解你的集合Collection。并非所有集合都是线程安全的。

3. 不要假设第三方库是线程安全的。大多数都不是,并且必须控制对共享状态的访问。

4. 如果你要依赖它 - 学习正确的并发性编程。

总结
Spring中的并发和多线程是重要的主题。在本文中,主要强调在编写Spring Boot应用程序时需要注意的关键地方。如果你想成功构建高实时、高质量的服务,需要围绕这一主题做出有意识的决策和权衡。

Introduction to Concurrency in Spring Boot | E4dev