• 两个简单的API限流实现方案


    1, Ngnix限流

    Nginx在架构中起到请求转发与负载均衡器的作用。外部req首先到Nginx监听的80端口,然后Nginx将req交给到监听8080端口的APP服务器处理。处理结果再经由Nginx返回给调用方。

    Nginx限流的配置:(/usr/local/etc/nginx/nginx.conf)

     1 #user  nobody;
     2 worker_processes  1;
     3  
     4 events {
     5     worker_connections  1024;
     6 }
     7  
     8 http {
     9     include       mime.types;
    10     default_type  application/octet-stream;
    11  
    12     sendfile        on;
    13     keepalive_timeout  65;
    14  
    15     # Rate limitation conf
    16     limit_req_zone $binary_remote_addr zone=mylimit:1m rate=1r/s;
    17     server {
    18         listen       80;
    19         server_name  localhost;
    20  
    21         location / {
    22             root   html;
    23             index  index.html index.htm;
    24         }
    25  
    26         error_page   500 502 503 504  /50x.html;
    27         location = /50x.html {
    28             root   html;
    29         }
    30  
    31         # rate limitation and redirection to APP 
    32         location ~* /(inventories|prices) {
    33             limit_req zone=mylimit;
    34             proxy_pass http://localhost:8080;
    35         }
    36     }
    37     include servers/*;
    38 }

    其中, limit_req_zone $binary_remote_addr zone=mylimit:1m rate=1r/s;
    将流量限制为 1QPS,如调用方超过该限制则返回 503

    重启nginx:  sudo nginx -s reload

    2, Redis 计数器

    原理: 每个req都对redis中生命周期为一个时间单位的计数器(key:callLimit)加1,如果单位时间内的访问次数超过限制,则拒绝所有后来的请求直到下一个单位时间开始。

     1 public Mono<ServerResponse>  req_handle(ServerRequest request) {
     2         if (callLimitCheck()){
     3             return getResp(request){
     4         … business logics to handle the req …
     5             };
     6         } else {
     7             return ServerResponse.ok().body(Mono.just(“Over call limit!”), String.class);
     8         }
     9     }
    10  
    11     private boolean callLimitCheck() {
    12         String callLimit = this.template.opsForValue().get("callLimit");
    13         if(callLimit != null && Integer.valueOf(callLimit)>30) {
    14             return false;
    15         }   
    16         DefaultRedisScript<String> script = new DefaultRedisScript<>(
    17         "local current current = redis.call('incr',KEYS[1]) if tonumber(current) == 1 then redis.call('expire', KEYS[1], 60) end");
    18         script.setResultType(String.class);
    19         List<String> keys = new ArrayList<>();
    20         keys.add("callLimit");
    21         this.template.execute(script, keys);
    22         return true;
    23     }

    上面的代码将有效的QPS限制为 0.5/s ( 30/m).

    其中为了避免race condition,将 incr 与 expire两个操作写到一个Lua脚本中实现原子性。

    "local current current = redis.call('incr',KEYS[1]) if tonumber(current) == 1 then redis.call('expire', KEYS[1], 60) end"

    转自:https://blog.csdn.net/qijin2016/article/details/79284553

  • 相关阅读:
    StartSSL免费证书申请笔记
    CAS实践笔录
    MySQL常用SQL/函数汇总(持续更新)
    Git学习笔记(持续更新)
    Nginx配置性能优化
    OneDrive无法正常登录
    Windows注册表(持续更新)
    Mysql操作笔记(持续更新)
    MySQL 绿色版(zip) 安装
    Ubuntu/Deepin下常用软件汇总(持续更新)
  • 原文地址:https://www.cnblogs.com/laoxia/p/9414185.html
Copyright © 2020-2023  润新知