加入收藏 | 设为首页 | 会员中心 | 我要投稿 孝感站长网 (https://www.0712zz.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 运营中心 > 网站设计 > 教程 > 正文

分布式限流,你想知道的都在这里

发布时间:2019-04-25 13:55:39 所属栏目:教程 来源:儒雅程序员
导读:前言 在一个高并发系统中对流量的把控是非常重要的,当巨大的流量直接请求到我们的服务器上没多久就可能造成接口不可用,不处理的话甚至会造成整个应用不可用。 比如最近就有个这样的需求,我作为客户端要向kafka生产数据,而kafka的消费者则再源源不断的

Java 中的调用逻辑:

  1. --lua 下标从 1 开始 
  2. -- 限流 key 
  3. local key = KEYS[1] 
  4. -- 限流大小 
  5. local limit = tonumber(ARGV[1]) 
  6. -- 获取当前流量大小 
  7. local curentLimit = tonumber(redis.call('get', key) or "0") 
  8. if curentLimit + 1 > limit then 
  9.  -- 达到限流大小 返回 
  10.  return 0; 
  11. else 
  12.  -- 没有达到阈值 value + 1 
  13.  redis.call("INCRBY", key, 1) 
  14.  redis.call("EXPIRE", key, 2) 
  15.  return curentLimit + 1 
  16. end 

所以只需要在需要限流的地方调用该方法对返回值进行判断即可达到限流的目的。

当然这只是利用 Redis 做了一个粗暴的计数器,如果想实现类似于上文中的令牌桶算法可以基于 Lua 自行实现。

Builder 构建器

在设计这个组件时想尽量的提供给使用者清晰、可读性、不易出错的 API。

比如第一步,如何构建一个限流对象。

最常用的方式自然就是构造函数,如果有多个域则可以采用重叠构造器的方式:

  1. public A(){} 
  2. public A(int a){} 
  3. public A(int a,int b){} 

缺点也是显而易见的:如果参数过多会导致难以阅读,甚至如果参数类型一致的情况下客户端颠倒了顺序,但不会引起警告从而出现难以预测的结果。

第二种方案可以采用 JavaBean 模式,利用 setter 方法进行构建:

  1. A a = new A(); 
  2. a.setA(a); 
  3. a.setB(b); 

这种方式清晰易读,但却容易让对象处于不一致的状态,使对象处于线程不安全的状态。

所以这里采用了第三种创建对象的方式,构建器:

  1. public class RedisLimit { 
  2.  private JedisCommands jedis; 
  3.  private int limit = 200; 
  4.  private static final int FAIL_CODE = 0; 
  5.  /** 
  6.  * lua script 
  7.  */ 
  8.  private String script; 
  9.  private RedisLimit(Builder builder) { 
  10.  this.limit = builder.limit ; 
  11.  this.jedis = builder.jedis ; 
  12.  buildScript(); 
  13.  } 
  14.  /** 
  15.  * limit traffic 
  16.  * @return if true 
  17.  */ 
  18.  public boolean limit() { 
  19.  String key = String.valueOf(System.currentTimeMillis() / 1000); 
  20.  Object result = null; 
  21.  if (jedis instanceof Jedis) { 
  22.  result = ((Jedis) this.jedis).eval(script, Collections.singletonList(key), Collections.singletonList(String.valueOf(limit))); 
  23.  } else if (jedis instanceof JedisCluster) { 
  24.  result = ((JedisCluster) this.jedis).eval(script, Collections.singletonList(key), Collections.singletonList(String.valueOf(limit))); 
  25.  } else { 
  26.  //throw new RuntimeException("instance is error") ; 
  27.  return false; 
  28.  } 
  29.  if (FAIL_CODE != (Long) result) { 
  30.  return true; 
  31.  } else { 
  32.  return false; 
  33.  } 
  34.  } 
  35.  /** 
  36.  * read lua script 
  37.  */ 
  38.  private void buildScript() { 
  39.  script = ScriptUtil.getScript("limit.lua"); 
  40.  } 
  41.  /** 
  42.  * the builder 
  43.  * @param <T> 
  44.  */ 
  45.  public static class Builder<T extends JedisCommands>{ 
  46.  private T jedis = null ; 
  47.  private int limit = 200; 
  48.  public Builder(T jedis){ 
  49.  this.jedis = jedis ; 
  50.  } 
  51.  public Builder limit(int limit){ 
  52.  this.limit = limit ; 
  53.  return this; 
  54.  } 
  55.  public RedisLimit build(){ 
  56.  return new RedisLimit(this) ; 
  57.  } 
  58.  } 

(编辑:孝感站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

热点阅读