• 作者:老汪软件技巧
  • 发表时间:2024-09-22 10:01
  • 浏览量:

实际场景案例

假设你正在构建一个电子商务平台,该平台每天会有大量的用户在特定的时间段(如促销时)同时下单。这些订单请求会同时涌入服务器,如果没有有效的流量控制,服务器可能会被瞬时过载,导致崩溃或响应延迟。为了避免这种情况,你决定使用“削峰填谷”策略,即限制同一时间内允许处理的请求数(例如100个),而多余的请求则会被放入队列等待处理。

为了确保用户体验,前端需要处理这种情况下的请求超时或重试逻辑,防止用户感受到系统卡顿。同时,后端也需要有相应的机制来控制请求流量并避免高峰期间的资源过载。

Step 1: 设计方案思路请求限制: 每次允许最多100个请求同时进入系统,超过这个数量的请求将被放入等待队列。超时处理: 如果请求等待的时间超过一定的阈值(例如5秒),则应返回给前端相应的超时错误。前端响应策略: 前端在收到超时错误后,可以通过弹出提示框让用户知道当前系统繁忙,或者进行重试。后端实现: 使用Spring Boot实现请求限制和队列管理。Step 2: 后端实现思路(基于Spring Boot)RateLimiter的使用: 使用限流算法来限制并发请求数,常用的工具如Guava的RateLimiter或使用Semaphore。请求队列: 多余的请求将被放入队列,等待前面的请求被处理。超时处理: 使用@ControllerAdvice统一处理超时异常,返回超时错误。代码实现

1. 引入依赖

首先,确保你在pom.xml中引入了必要的依赖:

<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
    <groupId>com.google.guavagroupId>
    <artifactId>guavaartifactId>
dependency>

2. 具体代码实现

场景流量是什么__场景流什么意思

package com.example.ratelimiter;
import com.google.common.util.concurrent.RateLimiter;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.*;
@RestController
@RequestMapping("/order")
public class OrderController {
    // 使用Guava RateLimiter来限制每秒100个请求
    private final RateLimiter rateLimiter = RateLimiter.create(100); // 这里表示每秒最多处理100个请求
    // 阻塞队列,保存多余的请求
    private final BlockingQueue> requestQueue = new LinkedBlockingQueue<>(1000);
    @GetMapping("/place")
    public String placeOrder() throws Exception {
        if (rateLimiter.tryAcquire()) {
            // 如果限流器允许请求,直接处理
            return processOrder();
        } else {
            // 如果请求超过了限制,则将请求放入队列
            Future future = handleInQueue();
            return future.get(5, TimeUnit.SECONDS); // 设置5秒超时,超时则抛出异常
        }
    }
    // 模拟订单处理逻辑
    private String processOrder() throws InterruptedException {
        // 模拟订单处理时间
        Thread.sleep(2000); 
        return "订单处理成功!";
    }
    // 将请求放入队列
    private Future handleInQueue() throws InterruptedException {
        Callable task = this::processOrder;
        if (!requestQueue.offer(task, 100, TimeUnit.MILLISECONDS)) {
            throw new RejectedExecutionException("服务器繁忙,请稍后再试!");
        }
        // 使用线程池异步处理队列中的请求
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        return executorService.submit(task);
    }
}

3. 异常处理

为了统一处理请求超时或者拒绝的情况,我们可以通过@ControllerAdvice来实现。

package com.example.ratelimiter;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import java.util.concurrent.TimeoutException;
@ControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(TimeoutException.class)
    @ResponseStatus(HttpStatus.REQUEST_TIMEOUT)
    public String handleTimeoutException(TimeoutException ex) {
        return "请求超时,请稍后再试";
    }
    @ExceptionHandler(RejectedExecutionException.class)
    @ResponseStatus(HttpStatus.SERVICE_UNAVAILABLE)
    public String handleRejectedExecutionException(RejectedExecutionException ex) {
        return "服务器繁忙,请稍后再试";
    }
}

Step 3: 前端处理

在前端,可以捕获请求超时或者服务不可用的响应,进行相应的处理。

async function placeOrder() {
    try {
        const response = await fetch("/order/place");
        if (!response.ok) {
            if (response.status === 503) {
                alert("服务器繁忙,请稍后再试");
            } else if (response.status === 408) {
                alert("请求超时,请稍后再试");
            }
        } else {
            const result = await response.text();
            console.log(result);
        }
    } catch (error) {
        console.error("请求失败", error);
    }
}

Step 4: 思考总结削峰填谷的实际场景: 在电商、抢票等需要高并发的场景下,非常适合使用限流策略来避免系统崩溃。通过将多余的请求放入队列,系统可以在承载能力范围内平稳处理请求。超时处理的重要性: 前端和后端都需要考虑超时的处理,以提升用户体验。请求如果等待时间过长,用户体验会大大下降,因此应及时返回错误提示或进行重试。扩展思路: 限流方式可以灵活变换,比如根据用户的优先级、IP 地址等做不同的限流策略。

通过这种方式,我们可以有效控制系统的负载,确保服务的稳定性。