国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

自定義redission裝配和集成分布式開源限流業(yè)務組件ratelimiter-spring-boot-starter的正確姿勢

這篇具有很好參考價值的文章主要介紹了自定義redission裝配和集成分布式開源限流業(yè)務組件ratelimiter-spring-boot-starter的正確姿勢。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

自定義redission裝配和集成分布式開源限流業(yè)務組件ratelimiter-spring-boot-starter的正確姿勢

1.說明

1.1 pom依賴

<dependency>
  <groupId>com.github.taptap</groupId>
  <artifactId>ratelimiter-spring-boot-starter</artifactId>
  <version>1.3</version>
</dependency>

<dependency>
   <groupId>redis.clients</groupId>
   <artifactId>jedis</artifactId>
   <version>3.3.0</version>
</dependency>

 <dependency>
   <groupId>org.redisson</groupId>
   <artifactId>redisson-spring-boot-starter</artifactId>
   <version>${redisson.version}</version>
 </dependency>

??由于使用了redisson-spring-boot-starter,在自定義redisson裝配的時候會被redisson-spring-boot-starter里面的start默認裝配了,同時在使用開源分布式限流組件ratelimiter-spring-boot-starter的時候,這個里面也會自動裝配一個redisson,所以就會產(chǎn)生沖突,容器中會有2個redisson的bean從而導致報錯,所以解決辦法是移除redisson-spring-boot-starter的依賴,加入redisson的依賴,或者不加redisson的依賴,redisson-spring-boot-starter里面包含了redisson-spring-boot-starter的依賴,是在啟動類上將redisson-spring-boot-starter的start排除:

1.2 引入redisson不引入redisson-spring-boot-starter依賴

<dependency>
   <groupId>org.redisson</groupId>
   <artifactId>redisson</artifactId>
   <version>3.13.14</version>
</dependency>

redisson

https://github.com/redisson/redisson#quick-start

1.3 引入redisson-spring-boot-starter不引入redisson,啟動類排除redisson-spring-boot-starter的自動裝配

@SpringBootApplication(exclude = {
        RedissonAutoConfiguration.class})
@EnableTransactionManagement
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

??此時啟動容器中還是會有2個redisson的bean,所以需要自定義裝配一個,然后加上@Primary為主的redisson

2.自定義redission裝配

2.1 RedissonLockProperties

package xxx.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

@Data
@ConfigurationProperties(prefix = "redisson.lock.config")
public class RedissonLockProperties {

    private String address;

    private String password;

    /**
     * 1.single
     * 2.master
     * 3.sentinel
     * 4.cluster
     */
    private int mode = 1;

    /**
     * 在master模式下需配置這個
     */
    private String masterAddress;

    /**
     * 在master模式下需配置這個
     */
    private String[] slaveAddress;

    /**
     * 在sentinel模式下需配置這個
     */
    private String masterName;

    /**
     * 在sentinel模式下需配置這個
     */
    private String[] sentinelAddress;

    /**
     * 在cluster模式下需配置這個
     */
    private String[] nodeAddress;

    private int database = 5;

    private int poolSize = 64;

    private int idleSize = 24;

    private int connectionTimeout = 10000;

    private int timeout = 3000;

}

2.2 RedissonLockAutoConfiguration

package xx.config;

import jodd.util.StringUtil;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.ClusterServersConfig;
import org.redisson.config.Config;
import org.redisson.config.MasterSlaveServersConfig;
import org.redisson.config.SentinelServersConfig;
import org.redisson.config.SingleServerConfig;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

/**
 * 分布式鎖自動化配置
 *
 * @author zlf
 */
@Configuration
@ConditionalOnClass(RedissonClient.class)
@EnableConfigurationProperties(RedissonLockProperties.class)
@ConditionalOnProperty(value = "redisson.lock.enabled", havingValue = "true")
public class RedissonLockAutoConfiguration {

    private static Config singleConfig(RedissonLockProperties properties) {
        Config config = new Config();
        SingleServerConfig serversConfig = config.useSingleServer();
        serversConfig.setAddress(properties.getAddress());
        String password = properties.getPassword();
        if (StringUtil.isNotBlank(password)) {
            serversConfig.setPassword(password);
        }
        serversConfig.setDatabase(properties.getDatabase());
        serversConfig.setConnectionPoolSize(properties.getPoolSize());
        serversConfig.setConnectionMinimumIdleSize(properties.getIdleSize());
        serversConfig.setIdleConnectionTimeout(properties.getConnectionTimeout());
        serversConfig.setConnectTimeout(properties.getConnectionTimeout());
        serversConfig.setTimeout(properties.getTimeout());
        return config;
    }

    private static Config masterSlaveConfig(RedissonLockProperties properties) {
        Config config = new Config();
        MasterSlaveServersConfig serversConfig = config.useMasterSlaveServers();
        serversConfig.setMasterAddress(properties.getMasterAddress());
        serversConfig.addSlaveAddress(properties.getSlaveAddress());
        String password = properties.getPassword();
        if (StringUtil.isNotBlank(password)) {
            serversConfig.setPassword(password);
        }
        serversConfig.setDatabase(properties.getDatabase());
        serversConfig.setMasterConnectionPoolSize(properties.getPoolSize());
        serversConfig.setMasterConnectionMinimumIdleSize(properties.getIdleSize());
        serversConfig.setSlaveConnectionPoolSize(properties.getPoolSize());
        serversConfig.setSlaveConnectionMinimumIdleSize(properties.getIdleSize());
        serversConfig.setIdleConnectionTimeout(properties.getConnectionTimeout());
        serversConfig.setConnectTimeout(properties.getConnectionTimeout());
        serversConfig.setTimeout(properties.getTimeout());
        return config;
    }

    private static Config sentinelConfig(RedissonLockProperties properties) {
        Config config = new Config();
        SentinelServersConfig serversConfig = config.useSentinelServers();
        serversConfig.setMasterName(properties.getMasterName());
        serversConfig.addSentinelAddress(properties.getSentinelAddress());
        String password = properties.getPassword();
        if (StringUtil.isNotBlank(password)) {
            serversConfig.setPassword(password);
        }
        serversConfig.setDatabase(properties.getDatabase());
        serversConfig.setMasterConnectionPoolSize(properties.getPoolSize());
        serversConfig.setMasterConnectionMinimumIdleSize(properties.getIdleSize());
        serversConfig.setSlaveConnectionPoolSize(properties.getPoolSize());
        serversConfig.setSlaveConnectionMinimumIdleSize(properties.getIdleSize());
        serversConfig.setIdleConnectionTimeout(properties.getConnectionTimeout());
        serversConfig.setConnectTimeout(properties.getConnectionTimeout());
        serversConfig.setTimeout(properties.getTimeout());
        return config;
    }

    private static Config clusterConfig(RedissonLockProperties properties) {
        Config config = new Config();
        ClusterServersConfig serversConfig = config.useClusterServers();
        serversConfig.addNodeAddress(properties.getNodeAddress());
        String password = properties.getPassword();
        if (StringUtil.isNotBlank(password)) {
            serversConfig.setPassword(password);
        }
        serversConfig.setMasterConnectionPoolSize(properties.getPoolSize());
        serversConfig.setMasterConnectionMinimumIdleSize(properties.getIdleSize());
        serversConfig.setSlaveConnectionPoolSize(properties.getPoolSize());
        serversConfig.setSlaveConnectionMinimumIdleSize(properties.getIdleSize());
        serversConfig.setIdleConnectionTimeout(properties.getConnectionTimeout());
        serversConfig.setConnectTimeout(properties.getConnectionTimeout());
        serversConfig.setTimeout(properties.getTimeout());
        return config;
    }

    @Bean
    @Primary
    public RedissonClient redissonClient(RedissonLockProperties properties) {
        int mode = properties.getMode();
        Config config = null;
        switch (mode) {
            case 1:
                config = singleConfig(properties);
                return Redisson.create(config);
            case 2:
                config = masterSlaveConfig(properties);
                return Redisson.create(config);
            case 3:
                config = sentinelConfig(properties);
                return Redisson.create(config);
            case 4:
                config = clusterConfig(properties);
                return Redisson.create(config);
        }
        return null;
    }

}

2.4 RedisConfig

??這里采用jedis的連接池工廠來裝配一個redisTemplateLimit,這個是上一篇文章中的一個配置,這里需要修改一下的,不然有可能會報錯的

自定義注解實現(xiàn)Redis分布式鎖、手動控制事務和根據(jù)異常名字或內容限流的三合一的功能

https://mp.weixin.qq.com/s/aW4PU_wlNVfzPc6uGFnndA
package xxx.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericToStringSerializer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Component;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.Protocol;

@Slf4j
@RefreshScope
@Component
public class RedisConfig {

    @Value("${spring.redis.host}")
    private String host;
    @Value("${spring.redis.port}")
    private String port;
    @Value("${spring.redis.password}")
    private String password;
    @Value("${spring.redis.database}")
    private String database;

    @Value("${spring.redis.jedis.pool.max-active}")
    private String maxActive;
    @Value("${spring.redis.jedis.pool.max-idle}")
    private String maxIdle;

    @Value("${spring.redis.jedis.pool.min-idle}")
    private String minIdle;

    //RedisConnectionFactory是這個spring-boot-starter-data-redis中的redis的連接工廠,如果不用jedis需要引入spring-boot-starter-data-redis即可,默認redisson-spring-boot-starter里面有這個依賴,如果沒有redisson-spring-boot-starter需要引入spring-boot-starter-data-redis可以使用的
    @Bean
    @SuppressWarnings("all")
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        // 定義泛型為 <String, Object> 的 RedisTemplate
        RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
        // 設置連接工廠
        template.setConnectionFactory(factory);
        // 定義 Json 序列化
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        // Json 轉換工具
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        //方法二:解決jackson2無法反序列化LocalDateTime的問題
        om.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        om.registerModule(new JavaTimeModule());
        jackson2JsonRedisSerializer.setObjectMapper(om);
        // 定義 String 序列化
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        // key采用String的序列化方式
        template.setKeySerializer(stringRedisSerializer);
        // hash的key也采用String的序列化方式
        template.setHashKeySerializer(stringRedisSerializer);
        // value序列化方式采用jackson
        template.setValueSerializer(jackson2JsonRedisSerializer);
        // hash的value序列化方式采用jackson
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }

    @Bean
    JedisPool redisPoolFactory() {
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxTotal(Integer.valueOf(maxActive).intValue());
        jedisPoolConfig.setMaxIdle(Integer.valueOf(maxIdle).intValue());
        jedisPoolConfig.setMinIdle(Integer.valueOf(minIdle).intValue());
        JedisPool jedisPool = new JedisPool(jedisPoolConfig, host, Integer.valueOf(port).intValue(), Protocol.DEFAULT_TIMEOUT, password, database);
        log.info("JedisPool注入成功??!");
        log.info("redis地址:" + host + ":" + port);
        return jedisPool;
    }

    @Bean
    RedisTemplate<String, Long> redisTemplateLimit(JedisConnectionFactory factory) {
        final RedisTemplate<String, Long> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        template.setKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(new GenericToStringSerializer<>(Long.class));
        template.setValueSerializer(new GenericToStringSerializer<>(Long.class));
        return template;
    }

    //springboot報錯:Could not resolve placeholder ‘xxx‘ in value “${XXXX}
    @Bean
    public static PropertySourcesPlaceholderConfigurer placeholderConfigurer() {
        PropertySourcesPlaceholderConfigurer placeholderConfigurer = new PropertySourcesPlaceholderConfigurer();
        placeholderConfigurer.setIgnoreUnresolvablePlaceholders(true);
        return placeholderConfigurer;
    }

}

2.3 nacos配置

spring:
  redis:
    host: xxx
    port: 6379
    password: xxxx
    database: 5
    # jedis配置
    jedis:
      pool:
        max-active: 200
        max-idle: 20
        max-wait: 2000
        min-idle: 5
    lettuce:
      shutdown-timeout: 0ms
redisson:
  lock:
    enabled: true
    config:
      address: redis://xxx:6379
      password: xxxx

3.集成分布式開源限流組件ratelimiter-spring-boot-starter

ratelimiter-spring-boot-starter

https://github.com/TapTap/ratelimiter-spring-boot-starter#ratelimiter-spring-boot-starter

3.1 引入依賴

maven

<dependency>
  <groupId>com.github.taptap</groupId>
  <artifactId>ratelimiter-spring-boot-starter</artifactId>
  <version>1.3</version>
</dependency>

gradle

implementation 'com.github.taptap:ratelimiter-spring-boot-starter:1.3'

3.2 nacos配置

spring:
  ratelimiter:
    enabled: true
    redis-address: redis://xxx:6379
    redis-password: xxxx
    response-body: "您請求的太快了,請慢點,不然會有點受不了哦!"
    status-code: 500

3.3 基礎使用

3.3.1 在需要加限流邏輯的方法上,添加注解 @RateLimit

如下所示:

@RestController
@RequestMapping("/test")
public class TestController {

    @GetMapping("/get")
    @RateLimit(rate = 5, rateInterval = "10s")
    public String get(String name) {
        return "hello";
    }
}

3.3.2 @RateLimit 注解說明

屬性 單位 默認值 是否必填 描述
mode enum(TIME_WINDOW/TOKEN_BUCKET) TIME_WINDOW 限流模式,目前可選時間窗口和令牌桶
rate int 時間窗口模式表示每個時間窗口內的請求數(shù)量、令牌桶模式表示每秒的令牌生產(chǎn)數(shù)量
rateInterval String 1s 用于時間窗口模式,表示時間窗口
rateExpression String 通過 EL 表達式從 Spring Config 上下文中獲取 rate 的值,rateExpression 的優(yōu)先級比 rate
fallbackFunction String 自定義觸發(fā)限流時的降級策略方法,默認觸發(fā)限流會拋 RateLimitException 異常
customKeyFunction String 自定義獲取限流 key 的方法
bucketCapacity int 用于令牌桶模式,表示令牌桶的桶的大小,這個參數(shù)控制了請求最大并發(fā)數(shù)
bucketCapacityExpression String 通過 EL 表達式從 Spring Config 上下文中獲取 bucketCapacity 的值,bucketCapacityExpression 的優(yōu)先級比 bucketCapacity
requestedTokens int 1 用于令牌桶模式,表示每次獲取的令牌數(shù),一般不用改動這個參數(shù)值,除非你知道你在干嘛

??@RateLimit 注解可以添加到任意被 spring 管理的 bean 上,不局限于 controller ,service 、repository 也可以。在最基礎限流功能使用上,以上三個步驟就已經(jīng)完成了。

3.3.3 限流的粒度,限流 key

??限流的粒度是通過限流的 key 來做的,在最基礎的設置下,限流的 key 默認是通過方法名稱拼出來的,規(guī)則如下:

key=RateLimiter_ + 類名 + 方法名

??除了默認的 key 策略,ratelimiter-spring-boot-starter 充分考慮了業(yè)務限流時的復雜性,提供了多種方式。結合業(yè)務特征,達到更細粒度的限流控制。

3.3.4 觸發(fā)限流后的行為

??默認觸發(fā)限流后 程序會返回一個 http 狀態(tài)碼為 429 的響應,響應值如下:

{
  "code": 429,
  "msg": "Too Many Requests"
}

??同時,響應的 header 里會攜帶一個 Retry-After 的時間值,單位 s,用來告訴調用方多久后可以重試。當然這一切都是可以自定義的,進階用法可以繼續(xù)往下看

3.4 進階用法

3.4.1 自定義限流的 key

??自定義限流 key 有三種方式,當自定義限流的 key 生效時,限流的 key 就變成了(默認的 key + 自定義的 key)。下面依次給出示例

3.4.1.1 @RateLimitKey 的方式
@RestController
@RequestMapping("/test")
public class TestController {

    @GetMapping("/get")
    @RateLimit(rate = 5, rateInterval = "10s")
    public String get(@RateLimitKey String name) {
        return "get";
    }
}

??@RateLimitKey 注解可以放在方法的入?yún)⑸希笕雲(yún)⑹腔A數(shù)據(jù)類型,上面的例子,如果 name = kl。那么最終限流的 key 如下:

key=RateLimiter_com.taptap.ratelimiter.web.TestController.get-kl
3.4.1.2 指定 keys 的方式
@RestController
@RequestMapping("/test")
public class TestController {

    @GetMapping("/get")
    @RateLimit(rate = 5, rateInterval = "10s", keys = {"#name"})
    public String get(String name) {
        return "get";
    }

    @GetMapping("/hello")
    @RateLimit(rate = 5, rateInterval = "10s", keys = {"#user.name", "user.id"})
    public String hello(User user) {
        return "hello";
    }
}

??keys 這個參數(shù)比 @RateLimitKey 注解更智能,基本可以包含 @RateLimitKey 的能力,只是簡單場景下,使用起來沒有 @RateLimitKey 那么便捷。keys 的語法來自 spring 的 Spel ,可以獲取對象入?yún)⒗锏膶傩?,支持獲取多個,最后會拼接起來。使用過 spring-cache 的同學可能會更加熟悉 如果不清楚 Spel 的用法,可以參考 spring-cache 的注解文檔

3.4.1.3 自定義 key 獲取函數(shù)
@RestController
@RequestMapping("/test")
public class TestController {

    @GetMapping("/get")
    @RateLimit(rate = 5, rateInterval = "10s", customKeyFunction = "keyFunction")
    public String get(String name) {
        return "get";
    }

    public String keyFunction(String name) {
        return "keyFunction" + name;
    }
}

??當 @RateLimitKey 和 keys 參數(shù)都沒法滿足時,比如入?yún)⒌闹凳且粋€加密的值,需要解密后根據(jù)相關明文內容限流??梢酝ㄟ^在同一類里自定義獲取 key 的函數(shù),這個函數(shù)要求和被限流的方法入?yún)⒁恢?,返回值?String 類型。返回值不能為空,為空時,會回退到默認的 key 獲取策略。

3.4.2 自定義限流后的行為

3.4.2.1 配置響應內容
spring.ratelimiter.enabled=true
spring.ratelimiter.response-body=Too Many Requests
spring.ratelimiter.status-code=509

??添加如上配置后,觸發(fā)限流時,http 的狀態(tài)碼就變成了 509 。響應的內容變成了 Too Many Requests 了

3.4.2.2 自定義限流觸發(fā)異常處理器

??默認的觸發(fā)限流后,限流器會拋出一個異常,限流器框架內定義了一個異常處理器來處理。自定義限流觸發(fā)處理器,需要先禁用系統(tǒng)默認的限流觸發(fā)處理器,禁用方式如下:

spring.ratelimiter.exceptionHandler.enable=false

??然后在項目里添加自定義處理器,如下:

@ControllerAdvice
public class RateLimitExceptionHandler {

    private final RateLimiterProperties limiterProperties;

    public RateLimitExceptionHandler(RateLimiterProperties limiterProperties) {
        this.limiterProperties = limiterProperties;
    }

    @ExceptionHandler(value = RateLimitException.class)
    @ResponseBody
    public String exceptionHandler(HttpServletResponse response, RateLimitException e) {
        response.setStatus(limiterProperties.getStatusCode());
        response.setHeader("Retry-After", String.valueOf(e.getRetryAfter()));
        return limiterProperties.getResponseBody();
    }
}
3.4.2.3 自定義觸發(fā)限流處理函數(shù),限流降級
@RequestMapping("/test")
public class TestController {

    @GetMapping("/get")
    @RateLimit(rate = 5, rateInterval = "10s", fallbackFunction = "getFallback")
    public String get(String name) {
        return "get";
    }

    public String getFallback(String name) {
        return "Too Many Requests" + name;
    }

}

??這種方式實現(xiàn)和使用和 2.1.3、自定義 key 獲取函數(shù)類似。但是多一個要求,返回值的類型需要和原限流函數(shù)的返回值類型一致,當觸發(fā)限流時,框架會調用 fallbackFunction 配置的函數(shù)執(zhí)行并返回,達到限流降級的效果

3.4.3 動態(tài)設置限流大小

3.4.3.1 rateExpression 的使用

??從 v1.2 版本開始,在 @RateLimit 注解里新增了屬性 rateExpression。該屬性支持 Spel 表達式從 Spring 的配置上下文中獲取值。 當配置了 rateExpression 后,rate 屬性的配置就不生效了。使用方式如下:

@GetMapping("/get2")
@RateLimit(rate = 2, rateInterval = "10s", rateExpression = "${spring.ratelimiter.max}")
public String get2(){
        return"get";
}

??集成 apollo 等配置中心后,可以做到限流大小的動態(tài)調整在線熱更。

3.5 直接使用限流器服務-RateLimiterService

??從 v1.3 版本開始,限流器框架內部提供了一個限流器服務,可以直接使用。當使用 RateLimiterService 后,則不用關心限流注解的邏輯了,所有限流邏輯都可以高度定制,如下:

@RestController
@RequestMapping("/test")
public class TestController {

    @Autowired
    private RateLimiterService limiterService;

    @GetMapping("/limiterService/time-window")
    public String limiterServiceTimeWindow(String key) {
        Rule rule = new Rule(Mode.TIME_WINDOW); // 限流策略,設置為時間窗口
        rule.setKey(key); //限流的 key
        rule.setRate(5); //限流的速率
        rule.setRateInterval(10); //時間窗口大小,單位為秒
        Result result = limiterService.isAllowed(rule);
        if (result.isAllow()) { //如果允許訪問
            return "ok";
        } else {
            //觸發(fā)限流
            return "no";
        }
    }

    @GetMapping("/limiterService/token-bucket")
    public String limiterServiceTokenBucket(String key) {
        Rule rule = new Rule(Mode.TOKEN_BUCKET); // 限流策略,設置為令牌桶
        rule.setKey(key); //限流的 key
        rule.setRate(5); //每秒產(chǎn)生的令牌數(shù)
        rule.setBucketCapacity(10); //令牌桶容量
        rule.setRequestedTokens(1); //請求的令牌數(shù)
        Result result = limiterService.isAllowed(rule);
        if (result.isAllow()) { //如果允許訪問
            return "ok";
        } else {
            //觸發(fā)限流
            return "no";
        }
    }
}

3.6壓力測試

  • 壓測工具 wrk: https://github.com/wg/wrk
  • 測試環(huán)境: 8 核心 cpu ,jvm 內存給的 -Xms2048m -Xmx2048m ,鏈接的本地的 redis
#壓測數(shù)據(jù)
kldeMacBook-Pro-6:ratelimiter-spring-boot-starter kl$ wrk -t16 -c100 -d15s --latency http://localhost:8080/test/wrk
Running 15s test @ http://localhost:8080/test/wrk
  16 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     6.18ms   20.70ms 281.21ms   98.17%
    Req/Sec     1.65k   307.06     2.30k    76.44%
  Latency Distribution
     50%    3.57ms
     75%    4.11ms
     90%    5.01ms
     99%  115.48ms
  389399 requests in 15.03s, 43.15MB read
Requests/sec:  25915.91
Transfer/sec:      2.87MB

??壓測下,所有流量都過限流器,qps 可以達到 2w+。

3.7版本更新

3.7.1 (v1.1.1)版本更新內容

  • 1、觸發(fā)限流時,header 的 Retry-After 值,單位由 ms ,調整成了 s

3.7.2(v1.2)版本更新內容

  • 1、觸發(fā)限流時,響應的類型從 text/plain 變成了 application/json
  • 2、優(yōu)化了限流的 lua 腳本,將原來的兩步 lua 腳本請求,合并成了一個,減少了和 redis 的交互
  • 3、限流的時間窗口大小,支持 Spel 從 Spring 的配置上下文中獲取,結合 apollo 等配置中心后,支持規(guī)則的動態(tài)下發(fā)熱更新

3.7.3(v1.3)版本更新內容

  • 1、配置策略變化,不在從應用的上下文中獲取 Redis 數(shù)據(jù)源,而是必須配置。但是配置的數(shù)據(jù)源在 Spring 上下文中聲明了 rateLimiterRedissonBeanName,應用也可以獲取使用
  • 2、代碼重構,新增了令牌桶的限流策略支持
  • 3、抽象了限流器服務 RateLimiterService,并在 Spring 上下文中聲明了,應用可以直接注入使用

4.總結

??這個也是在生產(chǎn)實踐后遇到的坑的一個總結,ratelimiter-spring-boot-starter、redisson-spring-boot-starter同時使用會有沖突,已經(jīng)RedisTemplate裝配上一篇文章的redisConfig配置會有報錯,所以這篇文章做了一個代碼調整,總結和分享,也是方便以后快速使用,不至于搞半天,所以總結成文是很有必要的,也是對以后的一種方便,ratelimiter-spring-boot-starter開源分布式限流組件(偏業(yè)務)的使用也是非常簡單,參看官網(wǎng)就可以學會的,源碼寫的也是很好的,就不需要自己重復的去制造輪子了,有這種開源好用的輪子直接拿來使用解決業(yè)務的燃眉之急,ratelimiter-spring-boot-starter可以針對一個接口使用令牌桶(接口總體上的限流)限流 + 時間窗口限流(針對一個用戶主鍵key,用戶唯一標識,對這個用戶限制在3s內只能點擊一次的操作,防止重復點擊) + redisson分布式鎖(比如說鎖一個用戶唯一標識3s鐘釋放鎖,這里存在一個問題就是3s內執(zhí)行的太快就容易點擊多次,取決于用戶3s內的手續(xù)和接口每次執(zhí)行的快慢),經(jīng)過這3個步驟,就可以讓系統(tǒng)接口的具有3保險,也就是系統(tǒng)接口魯棒性得到了大大的增強,希望我的分享對你有幫助,請一鍵三連,么么么噠!文章來源地址http://www.zghlxwxcb.cn/news/detail-724584.html

到了這里,關于自定義redission裝配和集成分布式開源限流業(yè)務組件ratelimiter-spring-boot-starter的正確姿勢的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如若轉載,請注明出處: 如若內容造成侵權/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領支付寶紅包贊助服務器費用

相關文章

  • 百度開源分布式id生成器集成--真香警告

    百度開源分布式id生成器集成--真香警告

    百度開源分布式id生成器集成–真香警告 ? ? 在復雜分布式系統(tǒng)中,往往需要對大量的數(shù)據(jù)和消息進行唯一標識。如在美團點評的金融、支付、餐飲、酒店、貓眼電影等產(chǎn)品的系統(tǒng)中,數(shù)據(jù)日漸增長,對數(shù)據(jù)分庫分表后需要有一個唯一ID來標識一條數(shù)據(jù)或消息,數(shù)據(jù)庫的自增

    2024年02月04日
    瀏覽(23)
  • 基于springboot+Redis的前后端分離項目之分布式鎖-redission(五)-【黑馬點評】

    基于springboot+Redis的前后端分離項目之分布式鎖-redission(五)-【黑馬點評】

    ????資源文件分享 鏈接:https://pan.baidu.com/s/1189u6u4icQYHg_9_7ovWmA?pwd=eh11 提取碼:eh11 基于setnx實現(xiàn)的分布式鎖存在下面的問題: 重入問題 :重入問題是指 獲得鎖的線程可以再次進入到相同的鎖的代碼塊中,可重入鎖的意義在于防止死鎖,比如HashTable這樣的代碼中,他的方法都

    2024年02月11日
    瀏覽(23)
  • @SpringBootApplication注解的理解——如何排除自動裝配 & 分布式情況下如何自動加載 & nacos是怎么被發(fā)現(xiàn)的

    @SpringBootApplication注解的理解——如何排除自動裝配 & 分布式情況下如何自動加載 & nacos是怎么被發(fā)現(xiàn)的

    spring作為主流的 Java Web 開發(fā)的開源框架,是Java 世界最為成功的框架,持續(xù)不斷深入認識spring框架是Java程序員不變的追求。 本篇博客介紹SpringBootApplicant注解的自動加載相關內容 其他相關的Spring博客文章列表如下: Spring基礎(核心容器)——從配置文件到注解開發(fā) 創(chuàng)建對象

    2024年02月07日
    瀏覽(22)
  • 【分布式事務】Seata 開源的分布式事務解決方案

    【分布式事務】Seata 開源的分布式事務解決方案

    Seata 是一款開源的分布式事務解決方案,致力于提供高性能和簡單易用的分布式事務服務。Seata 將為用戶提供了 AT、TCC、SAGA 和 XA 事務模式,為用戶打造一站式的分布式解決方案。 阿里巴巴作為國內最早一批進行應用分布式(微服務化)改造的企業(yè),很早就遇到微服務架構下

    2024年02月02日
    瀏覽(18)
  • seata分布式事務(與dubbo集成)

    seata分布式事務(與dubbo集成)

    ????????Seata 是一款開源的分布式事務解決方案,致力于在微服務架構下提供高性能和簡單易用的分布式事務服務。 @GlobalTransactional:全局事務注解,添加了以后可實現(xiàn)分布式事務的回滾和提交,用法與spring的@Transactional注解類似,注解參數(shù)的作用也基本一致 ? ? ? ? se

    2024年01月21日
    瀏覽(22)
  • springCloudAlibaba集成seata實戰(zhàn)(分布式事物詳解)

    springCloudAlibaba集成seata實戰(zhàn)(分布式事物詳解)

    1.1 基礎概念 事務:保證我們多個數(shù)據(jù)庫操作的原子性,多個操作要么都成功要么都不成功 事務ACID原則 A(Atomic)原子性 :構成事務的所有操作,要么都執(zhí)行完成,要么全部不執(zhí)行,不可能出現(xiàn)部分成功部分失 敗的情況。 C(Consistency)一致性 :在事務執(zhí)行前后,數(shù)據(jù)庫的一

    2024年04月15日
    瀏覽(23)
  • SpringBoot集成Skywalking實現(xiàn)分布式鏈路追蹤

    SpringBoot集成Skywalking實現(xiàn)分布式鏈路追蹤

    官方網(wǎng)址: ?Apache SkyWalking 官方文檔: ?SkyWalking 極簡入門 | Apache SkyWalking 下載地址 :Downloads | Apache SkyWalking? Agent :以探針的方式進行請求鏈路的數(shù)據(jù)采集,并向管理服務上報; OAP-Service :接收數(shù)據(jù),完成數(shù)據(jù)的存儲和展示; Storage :數(shù)據(jù)的存儲層,支持ElasticSearch、Mysql、

    2024年02月03日
    瀏覽(21)
  • Spring Boot 集成 Redisson分布式鎖

    Spring Boot 集成 Redisson分布式鎖

    ????????Redisson 是一種基于 Redis 的 Java 駐留集群的分布式對象和服務庫,可以為我們提供豐富的分布式鎖和線程安全集合的實現(xiàn)。在 Spring Boot 應用程序中使用 Redisson 可以方便地實現(xiàn)分布式應用程序的某些方面,例如分布式鎖、分布式集合、分布式事件發(fā)布和訂閱等。本篇

    2024年02月10日
    瀏覽(29)
  • 【分布式技術專題】「分布式ID系列」百度開源的分布式高性能的唯一ID生成器UidGenerator

    【分布式技術專題】「分布式ID系列」百度開源的分布式高性能的唯一ID生成器UidGenerator

    UidGenerator是什么 UidGenerator是百度開源的一款分布式高性能的唯一ID生成器,更詳細的情況可以查看官網(wǎng)集成文檔 uid-generator是基于Twitter開源的snowflake算法實現(xiàn)的一款唯一主鍵生成器(數(shù)據(jù)庫表的主鍵要求全局唯一是相當重要的)。要求java8及以上版本。 snowflake算法 Snowflake算法描

    2024年02月04日
    瀏覽(22)
  • 分布式開源監(jiān)控Zabbix實戰(zhàn)

    分布式開源監(jiān)控Zabbix實戰(zhàn)

    Zabbix作為一個分布式開源監(jiān)控軟件,在傳統(tǒng)的監(jiān)控領域有著先天的優(yōu)勢,具備靈活的數(shù)據(jù)采集、自定義的告警策略、豐富的圖表展示以及高可用性和擴展性。本文簡要介紹Zabbix的特性、整體架構和工作流程,以及安裝部署的過程,并結合實戰(zhàn)進行監(jiān)控配置。 1、Zabbix介紹 Zab

    2024年02月09日
    瀏覽(19)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領取紅包,優(yōu)惠每天領

二維碼1

領取紅包

二維碼2

領紅包