对于Tomcat的一些理解
单台tomcat的最大连接数和最大线程数是有限的。当前端发送来一个请求,后端接口没有使用异步线程,那个这个请求应该为同步请求,此时Tomcat从自己的线程池里取出一个线程,用以从Tomcat到Spring的Controller,Service,Dao。
为了更加直观的理解这句话,我们开始一个test。
我们先做一个开始测试之前准备工作。首先我们把Springboot内置的Tomcat的最大线程数设置为3,然后我们写一个测试接口,用以查看当前是哪条线程在工作。
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
@GetMapping("hello")
public String hello() throws InterruptedException {
System.out.println("主线程开始---------->"+Thread.currentThread().getName());
Thread.sleep(30000);
System.out.println("主线程结束---------->"+Thread.currentThread().getName());
return "hello work";
}
}
完成以后我们正式开始测试,因为我们设置了Tomcat最大线程数为3,现在我们连续对这个接口发起四个请求,来看一下线程调用的情况。
我们可以看到,前三个请求把Tomcat线程池中的三个线程占用后,第四个请求会进入等待状态,在第一个请求完成后,空出来的线程才会被第四个请求调用。
可想而知,如果在某个时刻外部对接口同时进行大量的请求,线程池里的线程全被调用,大量请求进入等待队列,当超过最大等待队列数的时候,这个Tomcat就崩溃了。
那我们除了集群以外还有其他巧妙的方法提升最大请求量吗?是可以的,我们在接口中使用异步线程,
@GetMapping("helloTest")
public String helloTest() {
System.out.println("主线程开始---------->"+Thread.currentThread().getName());
new Thread(()->{
try {
Thread.sleep(30000);
}catch (InterruptedException e){
e.printStackTrace();
}
}).start();
System.out.println("主线程结束---------->"+Thread.currentThread().getName());
return "hello work";
}
我们来看下相同情况下的运行结果
可以明显的看出Tomcat的线程在调用接口后立刻返回了,内部的逻辑处理由Spring内部的线程进行处理,这样明显让调用请求量提升了。
那么有些人要问了如果我需要获取异步线程进行的逻辑处理结果怎么办呢,那我们用Callable尝试一下,代码如下:
@GetMapping("helloTest1")
public Callable<String> helloTest1() {
System.out.println("主线程开始---------->"+Thread.currentThread().getName());
Callable<String> callable=()->{
System.out.println("异步线程开始---------->"+Thread.currentThread().getName());
Thread.sleep(30000);
System.out.println("异步线程结束---------->"+Thread.currentThread().getName());
return "hello word";
};
System.out.println("主线程结束---------->"+Thread.currentThread().getName());
return callable;
}
然后我们继续对他进行四个连续的请求查看效果。你会发现一个很神奇的效果,tomcat的线程在调用完一个接口直接返回了,剩下的逻辑处理由Spring的异步线程处理,当有其他的请求过来时,返回的线程可以被这个请求调用,而且当异步线程Callable完成时,之前Tomcat线程会乖乖的回来把Callable返回的数据返回给前端。