springboot 微服务 根据tomcat maxthread 和 等待用户数量,达到阈值后,通知用户前面还有多少用户等待,请稍后重试

我们在java项目开发中,如何设置服务器最大负载,过了服务器承受范围之后,提示用户稍后重试,避免

服务器无法提供正常服务

如何设置服务器负载比如:最大线程数,等待数量等,请看:spring+tomcat 用户每次发请求,tomcat 站在线程的角度是如何处理用户请求的,spinrg的bean 是共享的吗?

在Spring Boot微服务中,可以通过监控Tomcat线程池状态实现流量控制,当请求数超过阈值时通知用户等待情况。

1. 核心实现类 - Tomcat线程池监控与响应

java 复制代码
import org.apache.tomcat.util.threads.ThreadPoolExecutor;
import org.springframework.boot.web.embedded.tomcat.TomcatWebServer;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;

@Component
public class RequestThrottlingFilter extends OncePerRequestFilter 
    implements ApplicationListener<ContextRefreshedEvent> {

    // 配置参数(可放入application.properties)
    private static final int MAX_THREADS = 200;       // Tomcat最大线程数
    private static final int QUEUE_CAPACITY = 100;    // 等待队列容量
    private static final int ALERT_THRESHOLD = 80;    // 触发通知的阈值(%)
    
    private ThreadPoolExecutor tomcatThreadPool;
    private final AtomicInteger waitingRequests = new AtomicInteger(0);

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        // 获取Tomcat线程池实例
        Optional.ofNullable(event.getApplicationContext().getWebServer())
                .filter(ws -> ws instanceof TomcatWebServer)
                .map(ws -> (TomcatWebServer) ws)
                .map(ws -> ws.getTomcat().getConnector().getProtocolHandler().getExecutor())
                .filter(exec -> exec instanceof ThreadPoolExecutor)
                .ifPresent(exec -> tomcatThreadPool = (ThreadPoolExecutor) exec);
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, 
                                   HttpServletResponse response, 
                                   FilterChain filterChain) 
        throws ServletException, IOException {
        
        waitingRequests.incrementAndGet(); // 进入等待计数
        
        try {
            // 检查线程池状态
            if (isSystemOverloaded()) {
                int queuePosition = waitingRequests.get();
                sendBusyResponse(response, queuePosition);
                return;
            }
            
            filterChain.doFilter(request, response);
        } finally {
            waitingRequests.decrementAndGet(); // 完成处理,减少计数
        }
    }

    private boolean isSystemOverloaded() {
        if (tomcatThreadPool == null) return false;
        
        int activeThreads = tomcatThreadPool.getActiveCount();
        int queueSize = tomcatThreadPool.getQueue().size();
        
        // 计算系统负载率
        double loadFactor = (activeThreads + queueSize) * 100.0 / MAX_THREADS;
        
        return loadFactor >= ALERT_THRESHOLD || queueSize >= QUEUE_CAPACITY;
    }

    private void sendBusyResponse(HttpServletResponse response, int queuePosition) 
        throws IOException {
        
        response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());
        response.setContentType("application/json");
        
        String jsonResponse = String.format(
            "{\"status\": 429, \"message\": \"系统繁忙,当前等待人数:%d,请稍后重试\"}", 
            queuePosition
        );
        
        response.getWriter().write(jsonResponse);
        response.getWriter().flush();
    }
}

2. 配置Tomcat参数(application.yml)

yaml 复制代码
server:
  tomcat:
    max-threads: 200     # 最大工作线程数
    max-connections: 1000 # 最大连接数
    accept-count: 100    # 等待队列长度

3. 注册过滤器(可选,Spring Boot自动注册)

java 复制代码
@Configuration
public class FilterConfig {
    
    @Bean
    public FilterRegistrationBean<RequestThrottlingFilter> throttlingFilter(
        RequestThrottlingFilter filter) {
        
        FilterRegistrationBean<RequestThrottlingFilter> registration = 
            new FilterRegistrationBean<>(filter);
        registration.setOrder(Ordered.HIGHEST_PRECEDENCE); // 最高优先级
        return registration;
    }
}

工作原理解释:

  1. 线程池监控

    • 通过ContextRefreshedEvent获取Tomcat线程池实例
    • 实时监控:activeCount(活跃线程) + queue.size()(等待请求)
  2. 流量控制逻辑

    java 复制代码
    系统负载率 = (活跃线程数 + 等待队列长度) / 最大线程数 * 100%
    触发条件:负载率 ≥ 阈值 或 等待队列满
  3. 用户通知机制

    • 使用AtomicInteger计数器跟踪实时等待人数
    • 返回429状态码(Too Many Requests)
    • 响应示例:{"status":429, "message":"系统繁忙,当前等待人数:85,请稍后重试"}

4. 高级优化建议:

java 复制代码
// 在sendBusyResponse方法中添加重试建议
private void sendBusyResponse(HttpServletResponse response, int queuePosition) 
    throws IOException {
    
    // 计算建议重试时间(按每请求50ms估算)
    long waitTimeMs = queuePosition * 50;
    String suggestTime = waitTimeMs > 1000 ? 
        (waitTimeMs/1000) + "秒后" : "片刻后";

    String jsonResponse = String.format(
        "{\"status\":429, \"message\":\"系统繁忙,您前面还有%d人等待\", " +
        "\"suggestion\":\"建议%s重试\", \"retry_after\":%d}", 
        queuePosition, suggestTime, waitTimeMs
    );
    
    // 添加标准重试头
    response.setHeader("Retry-After", String.valueOf(waitTimeMs/1000));
    response.getWriter().write(jsonResponse);
}

5. 客户端处理建议:

  1. 前端收到429响应时:
    • 显示友好提示:系统繁忙,您前面还有XX人等待
    • 根据retry_after字段设置倒计时重试按钮
    • 使用指数退避策略:首次2秒后重试,失败则4秒、8秒...

注意事项:

  1. 性能影响

    • 使用AtomicInteger计数器无锁竞争,性能损耗可忽略
    • 避免在过滤器中执行阻塞操作
  2. 阈值设置建议

    • 生产环境推荐值:ALERT_THRESHOLD = 75-85
    • 公式:max_threads = (平均响应时间(ms) * QPS) / 1000
  3. 集群环境扩展

    • 需结合Redis分布式计数器
    • 使用Redisson的RAtomicLong替代AtomicInteger

此方案能在不影响核心业务的情况下,当系统压力过大时提供友好的用户提示,有效防止服务雪崩。

相关推荐
泉城老铁4 小时前
Spring Boot对接抖音获取H5直播链接详细指南
spring boot·后端·架构
后端小张1 天前
基于飞算AI的图书管理系统设计与实现
spring boot
考虑考虑2 天前
Jpa使用union all
java·spring boot·后端
阿杆2 天前
同事嫌参数校验太丑,我直接掏出了更优雅的 SpEL Validator
java·spring boot·后端
昵称为空C3 天前
SpringBoot3 http接口调用新方式RestClient + @HttpExchange像使用Feign一样调用
spring boot·后端
麦兜*3 天前
MongoDB Atlas 云数据库实战:从零搭建全球多节点集群
java·数据库·spring boot·mongodb·spring·spring cloud
麦兜*3 天前
MongoDB 在物联网(IoT)中的应用:海量时序数据处理方案
java·数据库·spring boot·物联网·mongodb·spring
汤姆yu3 天前
基于springboot的毕业旅游一站式定制系统
spring boot·后端·旅游
沐矢羽3 天前
Tomcat PUT方法任意写文件漏洞学习
学习·tomcat
计算机毕业设计木哥3 天前
计算机毕设选题推荐:基于Java+SpringBoot物品租赁管理系统【源码+文档+调试】
java·vue.js·spring boot·mysql·spark·毕业设计·课程设计