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

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

相关推荐
wb043072019 小时前
使用 Java 开发 MCP 服务并发布到 Maven 中央仓库完整指南
java·开发语言·spring boot·ai·maven
nbwenren10 小时前
Springboot中SLF4J详解
java·spring boot·后端
helx8211 小时前
SpringBoot中自定义Starter
java·spring boot·后端
掘根11 小时前
【微服务即时通讯项目】系统联调
微服务·云原生·架构
rleS IONS11 小时前
SpringBoot获取bean的几种方式
java·spring boot·后端
R***z10112 小时前
Spring Boot 整合 MyBatis 与 PostgreSQL 实战指南
spring boot·postgresql·mybatis
赵丙双13 小时前
spring boot AutoConfiguration.replacements 文件的作用
java·spring boot
计算机学姐14 小时前
基于SpringBoot的兴趣家教平台系统
java·spring boot·后端·spring·信息可视化·tomcat·intellij-idea
却话巴山夜雨时i15 小时前
互联网大厂Java面试场景:从基础到微服务的循序渐进提问
java·数据库·spring·微服务·面试·消息队列·技术栈
bearpping16 小时前
Spring Boot + Vue 全栈开发实战指南
vue.js·spring boot·后端