计算机系统应用教程网站

网站首页 > 技术文章 正文

如何实现限流功能? 如何实现限流功能设置

btikc 2024-10-29 13:14:22 技术文章 23 ℃ 0 评论

在使用Spring Boot开发应用时,实现限流是一个常见的需求,主要是为了保护系统在高并发情况下的稳定性和可用性。下面是一些常见的限流技术及其实现方式:

  1. Semaphore(信号量):
    • 使用Java的java.util.concurrent.Semaphore类可以很容易地实现。信号量允许限定同时访问某个特定资源的线程数量。
  1. RateLimiter(基于令牌桶算法的限流):
    • Google Guava库中的RateLimiter类提供了一个灵活的令牌桶实现。你可以定义每秒生成的令牌数来限制访问频率。
  1. Bucket4j(基于桶算法的限流):
    • Bucket4j是一个基于Java的库,它提供了更为复杂的桶限流策略,如基本的令牌桶和漏桶算法。这可以集成在Spring Boot应用中,以保护API端点。
  1. Spring Cloud Gateway中的RequestRateLimiter:
    • 如果你使用的是Spring Cloud Gateway作为API网关,可以利用其内置的RequestRateLimiter过滤器来实现限流。这通常依赖于Redis等外部存储来维护状态。
  1. 注解和AOP(面向切面编程):
    • 可以创建自定义注解和切面来实现方法级别的限流。例如,定义一个@RateLimit注解,并通过AOP拦截所有带有此注解的方法来控制调用频率。
  1. 使用Redis:
    • 利用Redis的原子操作,可以实现复杂的限流策略。例如,使用Redis脚本来维护计数器,并在短时间内限制请求次数。
  1. 分布式限流:
    • 在微服务架构中,可能需要一个分布式的限流方案。这时候可以使用如Redis, ZooKeeper等工具来保证在多个服务实例间的限流一致性。
  1. Resilience4j:
    • 这是一个为Java8及以上版本设计的轻量级的故障处理库,包括限流、断路器等多种功能。Resilience4j提供了限流器,允许你通过配置时间窗口、请求量等参数来实现限流。

每种技术或工具都有其适用场景。在选择具体实现方式时,需要考虑应用的具体需求、系统架构以及技术栈的兼容性。例如,对于简单的单体应用,Semaphore或RateLimiter可能就足够了;而对于需要高可用性的分布式系统,则可能需要考虑使用Redis或Resilience4j这样的更为复杂的解决方案。

Semaphore(信号量)

让我们通过一个实际场景来理解如何使用 java.util.concurrent.Semaphore 实现限流。假设我们有一个在线票务系统,需要限制同时访问票务数据库的线程数,以避免过多的并发请求影响数据库性能和数据一致性。

场景描述

我们的目标是限制同时访问票务数据库的线程数不超过10。当有超过10个线程尝试访问数据库时,额外的线程需要等待,直到有其他线程完成访问释放了信号量。

步骤一:创建Semaphore实例

首先,我们需要创建一个Semaphore实例,并设定最大的并发线程数(许可数)。

import java.util.concurrent.Semaphore;

public class TicketService {
    private final Semaphore semaphore = new Semaphore(10);

    public void accessDatabase() {
        try {
            semaphore.acquire();  // 请求一个许可
            performDatabaseOperations();  // 执行数据库操作
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            System.out.println("Thread was interrupted.");
        } finally {
            semaphore.release();  // 释放一个许可
        }
    }

    private void performDatabaseOperations() {
        // 这里模拟数据库操作
        System.out.println("Accessing database by " + Thread.currentThread().getName());
        try {
            Thread.sleep(1000);  // 假设操作数据库需要一秒钟
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

步骤二:使用Semaphore控制访问

上面的代码中,我们在accessDatabase()方法中使用semaphore.acquire()来请求许可。如果所有许可都被占用了,调用这个方法的线程将会被阻塞,直到有其他线程释放一个许可。操作完成后,我们在finally块中调用semaphore.release()以确保许可被释放,这样其他正在等待的线程可以获取许可继续执行。

步骤三:测试代码

我们可以创建多个线程来模拟高并发场景,看看Semaphore如何工作。

public class Application {
    public static void main(String[] args) {
        TicketService service = new TicketService();
        for (int i = 0; i < 20; i++) {  // 创建20个线程
            Thread t = new Thread(service::accessDatabase);
            t.start();
        }
    }
}

小结

在这个例子中,我们通过Semaphore成功地限制了同时访问数据库的线程数。这个机制对于控制资源的并发访问非常有效,尤其是在资源可能成为瓶颈的场景中。使用Semaphore时,要确保所有的acquire()操作都有对应的release(),否则可能会导致死锁或资源永久不可用。

RateLimiter(基于令牌桶算法的限流)

使用Google Guava库中的 RateLimiter 类可以非常灵活地实现基于令牌桶算法的限流。这种限流方式适用于需要平滑处理突发请求的场景,比如API服务器,以保证资源的均匀使用,防止瞬间的高流量导致系统压力过大。

场景描述

假设我们正在开发一个提供天气信息的API服务。为了防止用户在短时间内过多调用API(可能是因为恶意攻击或者配置错误),我们需要限制每个用户每秒可以调用的最大次数。

步骤一:引入Guava库

首先,你需要在项目的 pom.xml 中添加Guava库的依赖:

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>30.1-jre</version>
</dependency>

步骤二:创建RateLimiter实例

创建一个 RateLimiter 实例,设定每秒钟允许的请求数。例如,我们允许每秒钟不超过5个请求。

import com.google.common.util.concurrent.RateLimiter;

public class WeatherApiService {
    private final RateLimiter rateLimiter = RateLimiter.create(5.0);  // 每秒不超过5个请求

    public void accessWeatherAPI() {
        if (rateLimiter.tryAcquire()) {  // 尝试获取令牌
            provideWeatherInfo();
        } else {
            System.out.println("Rate limit exceeded. Please try again later.");
        }
    }

    private void provideWeatherInfo() {
        // 模拟提供天气信息
        System.out.println("Providing weather info to " + Thread.currentThread().getName());
    }
}

步骤三:测试代码

为了验证 RateLimiter 的行为,我们可以创建多个线程同时请求这个API。

public class Application {
    public static void main(String[] args) {
        WeatherApiService service = new WeatherApiService();
        for (int i = 0; i < 10; i++) {  // 同时启动10个线程
            Thread t = new Thread(service::accessWeatherAPI);
            t.start();
        }
    }
}

小结

在这个例子中,RateLimiter 确保了即使在多个线程同时请求服务时,API调用的频率也不会超过我们设置的限制。这是通过令牌桶算法实现的,该算法允许一定程度的突发流量,同时在长时间尺度上平滑流量。使用 RateLimiter 特别适合处理那些需要对请求速率有精确控制的应用场景,如API限流、数据库操作等。通过 tryAcquire() 方法可以非阻塞性地检查是否有可用的请求权限,这是非常适用于需要立即响应的web应用。

Bucket4j(基于桶算法的限流)

Bucket4j 是一个基于 Java 的强大库,提供了丰富的基于桶算法的限流策略,非常适合在复杂的应用场景中实施限流。与简单的令牌桶算法相比,Bucket4j 支持更多的配置选项和更精细的控制。

场景描述

考虑一个在线电影票订购系统,我们需要限制用户在高峰期对购票功能的访问频率,以防系统过载和提高服务的公平性。这种情况下,我们可以利用 Bucket4j 在Spring Boot应用中对每个用户的购票请求进行限流。

步骤一:添加Bucket4j依赖

首先,在Spring Boot项目的pom.xml中添加Bucket4j的依赖。

<dependency>
    <groupId>io.github.bucket4j</groupId>
    <artifactId>bucket4j-core</artifactId>
    <version>6.2.0</version>
</dependency>

步骤二:配置和使用Bucket4j

接下来,我们将创建一个简单的Spring Boot REST控制器,并在其中集成Bucket4j限流逻辑。

import io.github.bucket4j.Bandwidth;
import io.github.bucket4j.Bucket;
import io.github.bucket4j.Bucket4j;
import io.github.bucket4j.Refill;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.Duration;

@RestController
public class TicketBookingController {
    // 创建一个桶实例,这个桶允许每10秒最多3次请求
    private final Bucket bucket;

    public TicketBookingController() {
        Refill refill = Refill.greedy(3, Duration.ofSeconds(10));
        Bandwidth limit = Bandwidth.classic(3, refill);
        this.bucket = Bucket4j.builder()
                .addLimit(limit)
                .build();
    }

    @GetMapping("/bookTicket")
    public String bookTicket() {
        // 检查是否能从桶中获取令牌
        if (bucket.tryConsume(1)) {
            return "Ticket booked successfully!";
        } else {
            return "Rate limit exceeded. Please try again later.";
        }
    }
}

步骤三:测试限流

你可以通过发起快速连续的HTTP请求到 /bookTicket 端点来测试这个限流策略。可以使用Postman、curl或任何HTTP客户端。

小结

在这个例子中,Bucket4j 用于创建一个简单的令牌桶,这个桶每10秒重新填充3个令牌,并限制最大令牌数为3。这意味着用户最多可以每10秒访问3次购票服务,如果超过这个频率,就会收到限流通知。这种方式适用于需要按时间窗口限流的场景,例如API调用、数据库写操作等。通过这种方式,Bucket4j 提供了一种强大而灵活的方法来处理对特定资源的并发访问控制,可以很好地扩展以满足各种复杂应用场景的需求。

Spring Cloud Gateway中的RequestRateLimiter

在使用Spring Cloud Gateway作为API网关时,RequestRateLimiter过滤器提供了一种方便的方式来实现限流。这种限流通常使用Redis来维持状态,从而支持分布式限流,这对于微服务架构特别有用。

场景描述

假设我们有一个电子商务平台,该平台通过API网关管理各种服务,如订单处理、用户管理等。为了防止过度使用或滥用API,特别是在促销或高峰时期,我们需要对API请求进行限流。

步骤一:配置Spring Cloud Gateway和Redis

首先,确保你的项目中已经添加了Spring Cloud Gateway和Redis的依赖。在pom.xml中添加以下依赖:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>

步骤二:配置路由和限流规则

在Spring Cloud Gateway中配置路由和限流规则,使用Redis Rate Limiter。你需要在应用的配置文件(例如application.yml)中设置这些参数。

spring:
  cloud:
    gateway:
      routes:
        - id: order-service
          uri: http://localhost:8082  # 假设订单服务运行在8082端口
          predicates:
            - Path=/order/**
          filters:
            - name: RequestRateLimiter
              args:
                redis-rate-limiter.replenishRate: 10
                redis-rate-limiter.burstCapacity: 20
                key-resolver: "#{@userKeyResolver}"

  redis:
    host: localhost
    port: 6379

步骤三:定义Key Resolver

key-resolver 是一个决定限流策略应用于哪些用户或请求的Spring Bean。通常,这可以根据请求的来源或用户的身份来解决。在Spring配置中创建一个Bean来解析限流的键。

import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import reactor.core.publisher.Mono;

@Configuration
public class RateLimiterConfig {

    @Bean
    KeyResolver userKeyResolver() {
        return exchange -> Mono.just(
            exchange.getRequest().getQueryParams().getFirst("userId")  // 假设每个请求的查询参数中都包含userId
        );
    }
}

步骤四:启动Redis服务器

确保你的Redis服务器是启动状态,Spring Cloud Gateway将使用这个Redis实例来存储和检索速率限制数据。

小结

在这个设置中,我们为特定API路径/order/**配置了限流规则,允许每个用户每秒钟最多访问10次,但在一开始可以容忍突发流量到20次请求。这种配置有助于在不影响用户体验的情况下保护后端服务免受高流量冲击。通过使用RequestRateLimiter过滤器和Redis,Spring Cloud Gateway能够提供一个强大且易于配置的限流机制,适用于处理大规模分布式系统中的流量管理问题。

注解和AOP(面向切面编程)

在Spring框架中使用AOP(面向切面编程)结合自定义注解来实现方法级别的限流是一种非常灵活的方法。这可以让你非常精确地控制特定方法的访问频率,适合在业务逻辑层实施限流。

场景描述

假设我们在开发一个电子商务系统,该系统中的商品详情页面在促销期间可能会接受大量访问。为了防止数据库过载,我们需要限制商品详情页面的访问频率。

步骤一:定义自定义注解

首先,我们需要定义一个名为 @RateLimit 的注解,用于标记需要限流的方法。

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimit {
    int permitsPerSecond() default 1; // 每秒允许的最大访问次数,默认为1
}

步骤二:创建切面

接下来,我们创建一个切面来拦截带有 @RateLimit 注解的方法。我们将使用Guava的 RateLimiter 来实现实际的限流逻辑。

import com.google.common.util.concurrent.RateLimiter;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

import java.util.concurrent.ConcurrentHashMap;

@Aspect
@Component
public class RateLimitAspect {
    private final ConcurrentHashMap<String, RateLimiter> limiters = new ConcurrentHashMap<>();

    @Pointcut("@annotation(rateLimit)")
    public void rateLimitPointcut(RateLimit rateLimit) {}

    @Around("rateLimitPointcut(rateLimit)")
    public Object rateLimit(ProceedingJoinPoint joinPoint, RateLimit rateLimit) throws Throwable {
        RateLimiter limiter = limiters.computeIfAbsent(
            joinPoint.getSignature().toString(),
            key -> RateLimiter.create(rateLimit.permitsPerSecond())
        );

        if (limiter.tryAcquire()) {
            return joinPoint.proceed();
        } else {
            throw new RuntimeException("Rate limit exceeded");
        }
    }
}

步骤三:应用注解

现在,我们可以在需要限流的业务方法上使用这个注解。例如,对于一个获取商品详情的方法:

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ProductController {

    @GetMapping("/product/details")
    @RateLimit(permitsPerSecond = 5)  // 每秒最多允许5次请求
    public String getProductDetails() {
        // 模拟获取商品详情
        return "Product details";
    }
}

小结

通过使用AOP和自定义注解,我们成功地在方法级别实现了动态可配置的限流。这种方法的好处是它不侵入业务代码,易于维护和扩展。此外,它使得对不同方法使用不同的限流策略成为可能,从而可以根据每个方法的实际需求灵活调整限流策略。这种方式适合那些对性能要求较高,且需要精细控制服务访问频率的应用。

使用Redis

在使用Redis实现限流时,可以利用其支持的Lua脚本功能来确保操作的原子性,这是非常适合实现复杂且高性能的限流策略的。

场景描述

考虑一个社交媒体应用,用户可以发布状态更新。为了防止滥用(比如自动化的大量发送或垃圾信息发布),我们需要限制每个用户每分钟的发布次数。

步骤一:配置Redis环境

首先,确保你的开发环境中已经安装了Redis,并且Spring Boot项目中包含了对应的Redis依赖。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

步骤二:实现Redis限流逻辑

我们将使用Redis的Lua脚本来实现限流。Lua脚本在Redis服务器上执行,可以保证操作的原子性。

下面是一个基本的Lua脚本,它尝试为特定的键增加计数,同时设置一个过期时间。如果键不存在,它将创建键并设置过期时间。如果计数超过限制,则脚本返回0,表示请求应该被拒绝。

local key = KEYS[1]
local limit = tonumber(ARGV[1])
local expire_time = ARGV[2]

local current = redis.call('GET', key)
if current and tonumber(current) > limit then
    return 0
else
    if current then
        redis.call('INCR', key)
    else
        redis.call('SET', key, 1, 'EX', expire_time)
    end
    return 1
end

步骤三:集成到Spring Boot中

接下来,我们需要在Spring Boot应用中集成这个限流逻辑。我们可以创建一个服务方法来调用这个脚本。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

@Service
public class RateLimiterService {

    @Autowired
    private StringRedisTemplate redisTemplate;

    public boolean rateLimit(String userId, int limit, int expireTime) {
        String key = "rate_limit:" + userId;
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptText("local key = KEYS[1] "
                                + "local limit = tonumber(ARGV[1]) "
                                + "local expire_time = ARGV[2] "
                                + "local current = redis.call('GET', key) "
                                + "if current and tonumber(current) > limit then "
                                + "return 0 "
                                + "else "
                                + "if current then "
                                + "redis.call('INCR', key) "
                                + "else "
                                + "redis.call('SET', key, 1, 'EX', expire_time) "
                                + "end "
                                + "return 1 "
                                + "end");
        redisScript.setResultType(Long.class);
        Long result = redisTemplate.execute(redisScript, List.of(key), String.valueOf(limit), String.valueOf(expireTime));
        return result != null && result == 1;
    }
}

步骤四:应用限流服务

在你需要限流的业务逻辑中调用这个服务方法。例如,对用户的状态更新方法进行限流:

@RestController
public class PostController {

    @Autowired
    private RateLimiterService rateLimiterService;

    @PostMapping("/postStatus")
    public ResponseEntity<String> postStatus(@RequestBody String status, @AuthenticationPrincipal User user) {
        boolean allowed = rateLimiterService.rateLimit(user.getId(), 10, 60); // 每分钟最多10次请求
        if (!allowed) {
            return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).body("Rate limit exceeded");
        }
        // 业务逻辑...
        return ResponseEntity.ok("Status posted successfully");
    }
}

小结

使用Redis和Lua脚本进行限流可以高效地处理高频请求,尤其适合分布式环境。这种方法可以减轻服务端的计算压力,因为计数和限流的逻辑是在Redis服务器上执行的。此外,使用Lua脚本可以确保操作的原子性,避免了并发环境下的计数错误。

分布式限流

在微服务架构中,由于服务可能被部署为多个实例,因此需要一个分布式限流方案以确保整个系统的负载平衡和稳定性。利用Redis实现分布式限流是一种常见的实践,因为Redis提供了高性能、高可用性和原子性操作,非常适合用于处理跨多个服务实例的数据一致性问题。

场景描述

假设我们有一个电子商务平台,用户可以在多个微服务实例上进行商品搜索。为了防止过度负载,我们需要限制对搜索服务的请求频率。

步骤一:配置Redis环境

首先,确保你的开发环境中已经安装了Redis,并且在你的Spring Boot项目中包含了对应的Redis依赖。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

步骤二:实现Redis限流逻辑

我们将使用Redis的滑动窗口算法来实现限流。这种算法通过记录在一个固定时间窗口内的请求次数来决定是否允许当前请求。

下面是使用Redis的ZSET(有序集合)来实现滑动窗口算法的示例:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.Instant;
import java.util.UUID;

@Service
public class RateLimitingService {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    public boolean isAllowed(String userId, int maxRequestPerMinute, long windowSizeInMilliseconds) {
        String key = "rate:limit:" + userId;
        long currentTimeMillis = Instant.now().toEpochMilli();
        long windowStart = currentTimeMillis - windowSizeInMilliseconds;

        // 移除时间窗口之前的所有记录
        redisTemplate.opsForZSet().removeRangeByScore(key, 0, windowStart);

        // 获取当前窗口内的请求数量
        Long currentRequestCount = redisTemplate.opsForZSet().count(key, windowStart, currentTimeMillis);

        if (currentRequestCount != null && currentRequestCount >= maxRequestPerMinute) {
            return false; // 如果超出限制,则请求被拒绝
        }

        // 将当前请求记录到Redis中
        redisTemplate.opsForZSet().add(key, UUID.randomUUID().toString(), currentTimeMillis);
        redisTemplate.expireAt(key, Instant.ofEpochMilli(currentTimeMillis + windowSizeInMilliseconds)); // 设置过期时间
        return true;
    }
}

步骤三:应用限流服务

你可以在需要限流的服务方法中调用这个限流服务。例如,对商品搜索的方法进行限流:

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class SearchController {

    @Autowired
    private RateLimitingService rateLimitingService;

    @GetMapping("/search")
    public ResponseEntity<?> search(@RequestParam String query, @AuthenticationPrincipal User user) {
        boolean isAllowed = rateLimitingService.isAllowed(user.getId(), 5, 60000); // 每分钟最多5次请求

        if (!isAllowed) {
            return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).body("Too many requests");
        }

        // 执行搜索逻辑
        return ResponseEntity.ok("Results for query: " + query);
    }
}

小结

这种使用Redis的滑动窗口算法实现的分布式限流方案可以有效地控制请求频率,保证系统的稳定性。此方案适用于分布式系统中的任何组件,因为它通过Redis实现跨服务实例的状态共享。此外,由于使用了Redis的有序集合和时间戳,这种方案可以精确地管理时间窗口内的请求频率,同时自动清理过期数据,保证了存储的高效利用。

Resilience4j

Resilience4j 是一个适用于Java应用的故障处理和弹性库,它包括了断路器、限流器、隔离仓、重试和超时等多种功能。这些工具可以帮助应用更好地处理潜在的系统故障。在这里,我们重点关注它的限流功能。

场景描述

假设我们有一个在线支付系统,为了防止服务在高峰时段过载,我们需要对某些关键服务进行限流,如处理支付请求的服务。通过限流,我们可以确保服务的稳定性,防止系统因超载崩溃。

步骤一:添加Resilience4j依赖

首先,确保你的Spring Boot项目中添加了Resilience4j的依赖。在pom.xml中添加以下内容:

<dependency>
    <groupId>io.github.resilience4j</groupId>
    <artifactId>resilience4j-ratelimiter</artifactId>
    <version>1.7.0</version>
</dependency>

步骤二:配置限流器

在application.yml文件或者通过Java配置类中配置Resilience4j限流器。例如,我们可以配置一个每秒允许5次请求的限流器:

resilience4j.ratelimiter:
  instances:
    paymentRatelimiter:
      limitForPeriod: 5
      limitRefreshPeriod: 1s
      timeoutDuration: 500ms

这里配置说明:

  • limitForPeriod:指定在limitRefreshPeriod设定的时间窗口内允许的最大请求数。
  • limitRefreshPeriod:限流器刷新时间窗口的频率。
  • timeoutDuration:请求在超时前等待权限的最长时间。

步骤三:集成限流器到服务中

接下来,在Spring服务中使用配置的限流器。可以通过注入RateLimiterRegistry来获取限流器实例:

import io.github.resilience4j.ratelimiter.RateLimiter;
import io.github.resilience4j.ratelimiter.RateLimiterRegistry;
import io.github.resilience4j.ratelimiter.RequestNotPermitted;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class PaymentService {

    private final RateLimiter rateLimiter;

    @Autowired
    public PaymentService(RateLimiterRegistry rateLimiterRegistry) {
        this.rateLimiter = rateLimiterRegistry.rateLimiter("paymentRatelimiter");
    }

    public String processPayment() {
        RateLimiter.decorateCheckedSupplier(rateLimiter, this::attemptPayment).apply();
        return "Payment processed";
    }

    private String attemptPayment() throws RequestNotPermitted {
        // 模拟支付处理逻辑
        System.out.println("Processing payment");
        return "Success";
    }
}

步骤四:处理限流响应

如果请求超出了配置的限制,RateLimiter将抛出RequestNotPermitted异常。你可以捕获这个异常并给用户合适的响应:

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class PaymentController {

    @Autowired
    private PaymentService paymentService;

    @GetMapping("/pay")
    public ResponseEntity<String> makePayment() {
        try {
            return ResponseEntity.ok(paymentService.processPayment());
        } catch (RequestNotPermitted exception) {
            return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).body("Too many requests");
        }
    }
}

小结

通过这个示例,我们展示了如何在Spring Boot应用中使用Resilience4j实现限流。这不仅可以帮助保护系统免受高流量的冲击,还能提高整体的系统稳定性和可靠性。Resilience4j的限流器特别适合于需要精确控制请求速率的应用场景。


作者:一只爱撸猫的程序猿

链接:https://juejin.cn/post/7384632027230928930

#头条创作挑战赛#

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表