java限流api_RemoteRateLimiter

#RemoteRateLimiter

RateLimiter接口

/**

* Token bucket

*

* @param id tokens consistent id

* @param replenishRate How many requests do you want to replenish in {@param timeCount} * {@param timeUnit}

* @param burstCapacity How much bursting do you want to allow in {@param timeCount} * {@param timeUnit}?

* 0 means to forbidden any request.

* @param timeCount

* @param timeUnit

* @param rateType 1 means to set last refresh time value of first request time or last replenish time.

* 2 means to set last refresh time value of last allowed request time.

* 3 means to set last refresh time value of last request time.

* @param requested How many tokens for this request.

* @return

*/

boolean isAllowed(String id, int replenishRate, int burstCapacity, int timeCount, TimeUnit timeUnit, int rateType);

当设置 timeCount 为 1 设置 timeUnit 为 SECONDS 设置 rateType 为 3 的时候,该接口应该提供基于令牌桶算法的实现。

当设置 timeCount 为 1 设置 timeUnit 为 SECONDS 设置 rateType 为 2 的时候,该接口应该提供基于令牌桶算法变种的实现。

当设置 rateType 为 1 的时候,该接口应该提供基础的时间窗口限流计数的实现。

RedisRateLimiter的实现

为每个限流策略在Redis里生成一对Key:

request_rate_limiter.{@param id}.tokens

request_rate_limiter.{@param id}.timestamp

分别存放着限流策略对应的剩余可用tokens的数量和最后更新时间。

Lua 脚本

令牌桶算法的变种实现。每次请求去计算当前时间和最后更新时间之间的差值,根据时间差来计算需要补充的令牌数量。

RateLimiterInterceptor的实现

通过获取注解 @RateLimiterPolicy 和 @RateLimiterPolicies 来生成限流策略。再通过调用 RedisRateLimiter 来判断是否限流当前请求。

@RateLimiterPolicy

限流策略

属性 value 和 type 共同决定了限流策略的 id

相同功能的Bean为 RateLimiterPolicyBean

@RateLimiterPolicies

限流策略组

属性 names 会依赖Spring上下文来获取对应名称的限流策略Bean

属性 policies 可以直接写入一个或多个策略

需要注意组内策略的先后顺序,原则上需要把最可能生效(限流成功)的策略放在前面,以避免浪费策略的令牌数。

使用示例

1. 实现一个Controller

提供一个REST接口,返回字符 'success'

给接口添加 @RateLimiterPolicy 声明限流策略为每10秒限流一次请求,更新时间戳类型为最后一次通过的请求。

@RestController

@RequestMapping("/test")

public class TestController {

@RateLimiterPolicy(

value = "one",

type = RateLimiterPolicy.Type.IP,

replenishRate = 1,

burstCapacity = 1,

timeCount = 10,

refreshType = RateLimiterPolicy.RefreshType.LAST_ALLOWED_REQUEST

)

@GetMapping("/limiter")

public String test1() throws InterruptedException {

Thread.sleep(1000);

return "success";

}

}

2. 测试代码

一共发送5次请求,每隔6秒发送一次。根据限流策略预期结果为请求交替被拒绝。

@Test

public void test1() throws Throwable {

MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get("/test/limiter").accept(MediaType.APPLICATION_JSON))

.andDo(r -> Assert.assertTrue("success".equalsIgnoreCase(r.getResponse().getContentAsString())))

.andReturn();

log.info(result.getResponse().getContentAsString());

Thread.sleep(6000);

result = mockMvc.perform(MockMvcRequestBuilders.get("/test/limiter").accept(MediaType.APPLICATION_JSON))

.andDo(r -> Assert.assertTrue(!"success".equalsIgnoreCase(r.getResponse().getContentAsString())))

.andReturn();

log.info(result.getResponse().getContentAsString());

Thread.sleep(6000);

result = mockMvc.perform(MockMvcRequestBuilders.get("/test/limiter").accept(MediaType.APPLICATION_JSON))

.andDo(r -> Assert.assertTrue("success".equalsIgnoreCase(r.getResponse().getContentAsString())))

.andReturn();

log.info(result.getResponse().getContentAsString());

Thread.sleep(6000);

result = mockMvc.perform(MockMvcRequestBuilders.get("/test/limiter").accept(MediaType.APPLICATION_JSON))

.andDo(r -> Assert.assertTrue(!"success".equalsIgnoreCase(r.getResponse().getContentAsString())))

.andReturn();

log.info(result.getResponse().getContentAsString());

Thread.sleep(6000);

result = mockMvc.perform(MockMvcRequestBuilders.get("/test/limiter").accept(MediaType.APPLICATION_JSON))

.andDo(r -> Assert.assertTrue("success".equalsIgnoreCase(r.getResponse().getContentAsString())))

.andReturn();

log.info(result.getResponse().getContentAsString());

}

3. 注册策略Bean方式

依赖Spring 声明策略Bean 每10秒限流一次请求

@Component

public class PoliciesConfig {

/**

* Policy One

* Most 1 request in 10 seconds

* @return

*/

@Bean(name = "policyOne")

public RateLimiterPolicyBean policyBeanOne() {

return new RateLimiterPolicyBean("one", "IP", 1, 1, 10, "SECONDS", 2);

}

}

接口使用 names 来注入策略Bean

@RateLimiterPolicies(names = {"policyOne"})

@GetMapping("/limiter")

public String test1() throws InterruptedException {

Thread.sleep(1000);

return "success";

}

4. 更多示例

5. Dubbo集成

在服务提供者注解内声明 filter="ratelimiter" 并注入参数 parameters = { "$YOUR_METHOD_NAME.rateLimiterPolicies", "$RATELIMITER_1,$RATELIMITER_2 ..."}

例子如下:

@Service(version = "1.0.0", parameters = { "sayHello.rateLimiterPolicies", "policyThr" }, filter = "ratelimiter")

public class TestDubboProvider implements TestService {

@Override

public String sayHello(TestBean bean) {

return "Hello, " + bean.getName();

}

}

其中方法名为 sayHello 注入的限速策略类为 policyThr 如有多个限速策略用 , 连接

6. 自定义Dubbo过滤器

默认提供的过滤器只支持取ip地址,如果需要扩展自定义字段需要单独实现。

例子如下:

@Activate(group = { Constants.PROVIDER }, value = "MyRatelimiter")

public class CustomDubboFilter extends RateLimiterDubboFilter {

@Override

protected String getUserId(Invoker> invoker, Invocation invocation) {

return ((TestBean) invocation.getArguments()[0]).getName();

}

}

并且在

src

|-main

|-java

|-com

|-xxx

|-XxxFilter.java (实现Filter接口)

|-resources

|-META-INF

|-dubbo

|-org.apache.dubbo.rpc.Filter (纯文本文件,内容为:xxx=com.xxx.XxxFilter)

对应文件内添加配置使得自定义的filter生效。

同时修改服务提供者的注解配置

@Service(version = "1.0.0", parameters = { "sayHello.rateLimiterPolicies", "policyThr" }, filter = "MyRatelimiter")

public class TestDubboProvider implements TestService {

@Override

public String sayHello(TestBean bean) {

return "Hello, " + bean.getName();

}

}

依赖

spring-boot-starter-date-redis

spring-boot-starter-web

guava

dubbo-spring-boot-starter

lombok

net.sourceforge.groboutils

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值