赞
踩

/** * @description: 测试 Controller * @author: Lorin * @create: 2023-08-27 07:46 **/ @RestController public class TestController { /** * 并发测试 * 接受并发请求,接受请求后睡眠 2 分钟阻塞线程,用于判断实例可以处理多少请求 * * @param num 请求ID 表示已经收到多少个请求 */ @GetMapping("/test-with-sleep") public void testWithSleep(@RequestParam Integer num) throws InterruptedException { System.out.println("接受到请求: " + num); Thread.sleep(1000 * 120); System.out.println("请求执行完成"); } /** * http test with no sleep * * @param num */ @GetMapping("/test1") public void test1(@RequestParam Integer num) { System.out.println("接受到请求: " + num); System.out.println("请求执行完成"); } } /** * @description: 并发测试 * @author: Lorin * @create: 2023-08-27 **/ class TestControllerTest { /** * 为了方便测试直接起多线程并发请求测试 当然也可以用 jmeter 等压测工具 */ @Test void test1() { for (int i = 0; i < 1000; i++) { int num = i; new Thread(() -> { try { URL url = new URL("http://localhost:8080/test1?num=" + num); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.connect(); System.out.println(connection.getResponseCode()); connection.disconnect(); } catch (Exception exception) { System.out.println(exception); } }).start(); } } }

这里并发为什么是200,我们可以调整?
如果并发数小于200或者大于200是如何表现?




核心线程数:10
非核心线程数:200
任务队列长度:2147483647 Integer.MAX_VALUE

org.apache.tomcat.util.threads.ThreadPoolExecutor#execute(java.lang.Runnable)

private void executeInternal(Runnable command) { if (command == null) { throw new NullPointerException(); } /* * Proceed in 3 steps: * * 1. If fewer than corePoolSize threads are running, try to * start a new thread with the given command as its first * task. The call to addWorker atomically checks runState and * workerCount, and so prevents false alarms that would add * threads when it shouldn't, by returning false. * * 2. If a task can be successfully queued, then we still need * to double-check whether we should have added a thread * (because existing ones died since last checking) or that * the pool shut down since entry into this method. So we * recheck state and if necessary roll back the enqueuing if * stopped, or start a new thread if there are none. * * 3. If we cannot queue task, then we try to add a new * thread. If it fails, we know we are shut down or saturated * and so reject the task. */ int c = ctl.get(); // 判断是否小于核心线程数 小于则创建核心线程处理任务 if (workerCountOf(c) < corePoolSize) { if (addWorker(command, true)) { return; } c = ctl.get(); } // 判断任务是否可以放入队列中 if (isRunning(c) && workQueue.offer(command)) { int recheck = ctl.get(); if (! isRunning(recheck) && remove(command)) { reject(command); } else if (workerCountOf(recheck) == 0) { addWorker(null, false); } } // 尝试创建非核心线程 else if (!addWorker(command, false)) { reject(command); } } // 既然没有放入队列中 那么我们看看为什么没有放入队列中 public class TaskQueue extends LinkedBlockingQueue<Runnable> { @Override public boolean offer(Runnable o) { //we can't do any checks if (parent==null) { return super.offer(o); } //we are maxed out on threads, simply queue the object if (parent.getPoolSize() == parent.getMaximumPoolSize()) { return super.offer(o); } //we have idle threads, just add it to the queue if (parent.getSubmittedCount()<=(parent.getPoolSize())) { return super.offer(o); } //if we have less threads than maximum force creation of a new thread if (parent.getPoolSize()<parent.getMaximumPoolSize()) { return false; } //if we reached here, we need to add it to the queue return super.offer(o); } } // parent 就是我们的 tomcat 线程池,parent.getPoolSize() 返回当前线程池中的数量 // 关键在这段代码,若当前当前线程数小于最大线程数,则不放入队列中,结合上文逻辑,则进入创建非核心线程逻辑 // if we have less threads than maximum force creation of a new thread if (parent.getPoolSize()<parent.getMaximumPoolSize()) { return false; }
# 非核心线程数 server.tomcat.threads.max=200 # 核心线程数 server.tomcat.threads.min-spare=10 ---------------------------------- # 在拒绝连接之前可排队的连接数 server.tomcat.accept-count=100 # 非核心线程空闲时间 1 分钟 # 同时处理最大接收连接数 # maxConnections和accept-count的关系为:当连接数达到最大值maxConnections后,系统会继续接收连接,但不会超过acceptCount的值 # 当该值小于 server.tomcat.threads.max 时会影响最大可并发处理请求数 server.tomcat.max-connections=8192 # 每个连接可以处理的最大请求数 server.tomcat.max-keep-alive-requests=100 # 连接存活时间 默认使用 connection-timeout server.tomcat.keep-alive-timeout=-1 # 连接超时时间 默认 20s server.tomcat.connection-timeout=20000

这里并发为什么是 200,我们可以调整?
- 因为 tomcat 线程池类似 Java 线程池,但有一定区别,会先创建非核心线程后再放入队列中。
- 我们可以通过调整 最大线程数来 控制并发数量
如果并发数小于 200 或者大于 200 是如何表现?(默认配置下)
- 小于 200 会创建核心线程和非核心线程立即处理任务,大于 200 的任务会放入等待队列中
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-undertow</artifactId> </dependency> // 观察启动日志可以看到我们已经成功启动 2023-08-27 20:56:29.531 INFO 2618 --- [ main] org.jboss.threads : JBoss Threads version 3.1.0.Final 2023-08-27 20:56:29.575 INFO 2618 --- [ main] o.s.b.w.e.undertow.UndertowWebServer : Undertow started on port(s) 8080 (http)



可以看到 maxSize、coreSize 来自于 Builder 这个对象,我们回到 Builder 看看这两个参数来自哪里:
org.jboss.threads.EnhancedQueueExecutor.Builder

我们看到 maxSize、coreSize 原始值为 64、16,那么是在哪里改变的呢?接着往下断点,我们可以找到:
org.xnio.XnioWorker#XnioWorker 中创建了 修改了 Builder 创建了 EnhancedQueueExecutorTaskPool:



# 线程数 核心线程和非核心线程都使用该值
server.undertow.threads.worker=5
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。