云计算百科
云计算领域专业知识百科平台

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

我们在java项目开发中,如何设置服务器最大负载,过了服务器承受范围之后,提示用户稍后重试,避免 服务器无法提供正常服务 如何设置服务器负载比如:最大线程数,等待数量等,请看:spring+tomcat 用户每次发请求,tomcat 站在线程的角度是如何处理用户请求的,spinrg的bean 是共享的吗?

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

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

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)

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

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

@Configuration
public class FilterConfig {

@Bean
public FilterRegistrationBean<RequestThrottlingFilter> throttlingFilter(
RequestThrottlingFilter filter) {

FilterRegistrationBean<RequestThrottlingFilter> registration =
new FilterRegistrationBean<>(filter);
registration.setOrder(Ordered.HIGHEST_PRECEDENCE); // 最高优先级
return registration;
}
}

工作原理解释:

  • 线程池监控:

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

    系统负载率 = (活跃线程数 + 等待队列长度) / 最大线程数 * 100%
    触发条件:负载率 ≥ 阈值 或 等待队列满

  • 用户通知机制:

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

    // 在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. 客户端处理建议:

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

  • 性能影响:

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

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

    • 需结合Redis分布式计数器
    • 使用Redisson的RAtomicLong替代AtomicInteger
  • 此方案能在不影响核心业务的情况下,当系统压力过大时提供友好的用户提示,有效防止服务雪崩。

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » springboot 微服务 根据tomcat maxthread 和 等待用户数量,达到阈值后,通知用户前面还有多少用户等待,请稍后重试
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!